summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--hw/net/virtio-net.c99
-rw-r--r--include/hw/virtio/virtio-net.h2
2 files changed, 81 insertions, 20 deletions
diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
index 556f221669..6ff3cc35c5 100644
--- a/hw/net/virtio-net.c
+++ b/hw/net/virtio-net.c
@@ -119,7 +119,7 @@ static VirtIOFeature feature_sizes[] = {
.end = endof(struct virtio_net_config, mtu)},
{.flags = 1ULL << VIRTIO_NET_F_SPEED_DUPLEX,
.end = endof(struct virtio_net_config, duplex)},
- {.flags = 1ULL << VIRTIO_NET_F_RSS,
+ {.flags = (1ULL << VIRTIO_NET_F_RSS) | (1ULL << VIRTIO_NET_F_HASH_REPORT),
.end = endof(struct virtio_net_config, supported_hash_types)},
{}
};
@@ -153,7 +153,8 @@ static void virtio_net_get_config(VirtIODevice *vdev, uint8_t *config)
netcfg.duplex = n->net_conf.duplex;
netcfg.rss_max_key_size = VIRTIO_NET_RSS_MAX_KEY_SIZE;
virtio_stw_p(vdev, &netcfg.rss_max_indirection_table_length,
- VIRTIO_NET_RSS_MAX_TABLE_LEN);
+ 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);
memcpy(config, &netcfg, n->config_size);
@@ -579,7 +580,7 @@ static int peer_has_ufo(VirtIONet *n)
}
static void virtio_net_set_mrg_rx_bufs(VirtIONet *n, int mergeable_rx_bufs,
- int version_1)
+ int version_1, int hash_report)
{
int i;
NetClientState *nc;
@@ -587,7 +588,10 @@ static void virtio_net_set_mrg_rx_bufs(VirtIONet *n, int mergeable_rx_bufs,
n->mergeable_rx_bufs = mergeable_rx_bufs;
if (version_1) {
- n->guest_hdr_len = sizeof(struct virtio_net_hdr_mrg_rxbuf);
+ n->guest_hdr_len = hash_report ?
+ sizeof(struct virtio_net_hdr_v1_hash) :
+ sizeof(struct virtio_net_hdr_mrg_rxbuf);
+ n->rss_data.populate_hash = !!hash_report;
} else {
n->guest_hdr_len = n->mergeable_rx_bufs ?
sizeof(struct virtio_net_hdr_mrg_rxbuf) :
@@ -708,6 +712,8 @@ static uint64_t virtio_net_get_features(VirtIODevice *vdev, uint64_t features,
virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_TSO4);
virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_TSO6);
virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_ECN);
+
+ virtio_clear_feature(&features, VIRTIO_NET_F_HASH_REPORT);
}
if (!peer_has_vnet_hdr(n) || !peer_has_ufo(n)) {
@@ -720,6 +726,7 @@ static uint64_t virtio_net_get_features(VirtIODevice *vdev, uint64_t 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;
@@ -886,12 +893,15 @@ static void virtio_net_set_features(VirtIODevice *vdev, uint64_t features)
virtio_has_feature(features,
VIRTIO_NET_F_MRG_RXBUF),
virtio_has_feature(features,
- VIRTIO_F_VERSION_1));
+ VIRTIO_F_VERSION_1),
+ virtio_has_feature(features,
+ VIRTIO_NET_F_HASH_REPORT));
n->rsc4_enabled = virtio_has_feature(features, VIRTIO_NET_F_RSC_EXT) &&
virtio_has_feature(features, VIRTIO_NET_F_GUEST_TSO4);
n->rsc6_enabled = virtio_has_feature(features, VIRTIO_NET_F_RSC_EXT) &&
virtio_has_feature(features, VIRTIO_NET_F_GUEST_TSO6);
+ n->rss_data.redirect = virtio_has_feature(features, VIRTIO_NET_F_RSS);
if (n->has_vnet_hdr) {
n->curr_guest_offloads =
@@ -1165,7 +1175,9 @@ static void virtio_net_disable_rss(VirtIONet *n)
}
static uint16_t virtio_net_handle_rss(VirtIONet *n,
- struct iovec *iov, unsigned int iov_cnt)
+ struct iovec *iov,
+ unsigned int iov_cnt,
+ bool do_rss)
{
VirtIODevice *vdev = VIRTIO_DEVICE(n);
struct virtio_net_rss_config cfg;
@@ -1178,10 +1190,14 @@ static uint16_t virtio_net_handle_rss(VirtIONet *n,
const char *err_msg = "";
uint32_t err_value = 0;
- if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_RSS)) {
+ if (do_rss && !virtio_vdev_has_feature(vdev, VIRTIO_NET_F_RSS)) {
err_msg = "RSS is not negotiated";
goto error;
}
+ if (!do_rss && !virtio_vdev_has_feature(vdev, VIRTIO_NET_F_HASH_REPORT)) {
+ err_msg = "Hash report is not negotiated";
+ goto error;
+ }
size_get = offsetof(struct virtio_net_rss_config, indirection_table);
s = iov_to_buf(iov, iov_cnt, offset, &cfg, size_get);
if (s != size_get) {
@@ -1193,6 +1209,9 @@ static uint16_t virtio_net_handle_rss(VirtIONet *n,
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;
+ }
if (!is_power_of_2(n->rss_data.indirections_len)) {
err_msg = "Invalid size of indirection table";
err_value = n->rss_data.indirections_len;
@@ -1203,8 +1222,8 @@ static uint16_t virtio_net_handle_rss(VirtIONet *n,
err_value = n->rss_data.indirections_len;
goto error;
}
- n->rss_data.default_queue =
- virtio_lduw_p(vdev, &cfg.unclassified_queue);
+ n->rss_data.default_queue = do_rss ?
+ virtio_lduw_p(vdev, &cfg.unclassified_queue) : 0;
if (n->rss_data.default_queue >= n->max_queues) {
err_msg = "Invalid default queue";
err_value = n->rss_data.default_queue;
@@ -1238,7 +1257,7 @@ static uint16_t virtio_net_handle_rss(VirtIONet *n,
err_value = (uint32_t)s;
goto error;
}
- queues = virtio_lduw_p(vdev, &temp.us);
+ queues = do_rss ? virtio_lduw_p(vdev, &temp.us) : n->curr_queues;
if (queues == 0 || queues > n->max_queues) {
err_msg = "Invalid number of queues";
err_value = queues;
@@ -1284,8 +1303,12 @@ static int virtio_net_handle_mq(VirtIONet *n, uint8_t cmd,
uint16_t queues;
virtio_net_disable_rss(n);
+ if (cmd == VIRTIO_NET_CTRL_MQ_HASH_CONFIG) {
+ queues = virtio_net_handle_rss(n, iov, iov_cnt, false);
+ return queues ? VIRTIO_NET_OK : VIRTIO_NET_ERR;
+ }
if (cmd == VIRTIO_NET_CTRL_MQ_RSS_CONFIG) {
- queues = virtio_net_handle_rss(n, iov, iov_cnt);
+ queues = virtio_net_handle_rss(n, iov, iov_cnt, true);
} else if (cmd == VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET) {
struct virtio_net_ctrl_mq mq;
size_t s;
@@ -1572,15 +1595,34 @@ static uint8_t virtio_net_get_hash_type(bool isip4,
return 0xff;
}
+static void virtio_set_packet_hash(const uint8_t *buf, uint8_t report,
+ uint32_t hash)
+{
+ struct virtio_net_hdr_v1_hash *hdr = (void *)buf;
+ hdr->hash_value = hash;
+ hdr->hash_report = report;
+}
+
static int virtio_net_process_rss(NetClientState *nc, const uint8_t *buf,
size_t size)
{
VirtIONet *n = qemu_get_nic_opaque(nc);
- unsigned int index = nc->queue_index, new_index;
+ unsigned int index = nc->queue_index, new_index = index;
struct NetRxPkt *pkt = n->rx_pkt;
uint8_t net_hash_type;
uint32_t hash;
bool isip4, isip6, isudp, istcp;
+ static const uint8_t reports[NetPktRssIpV6UdpEx + 1] = {
+ VIRTIO_NET_HASH_REPORT_IPv4,
+ VIRTIO_NET_HASH_REPORT_TCPv4,
+ VIRTIO_NET_HASH_REPORT_TCPv6,
+ VIRTIO_NET_HASH_REPORT_IPv6,
+ VIRTIO_NET_HASH_REPORT_IPv6_EX,
+ VIRTIO_NET_HASH_REPORT_TCPv6_EX,
+ VIRTIO_NET_HASH_REPORT_UDPv4,
+ VIRTIO_NET_HASH_REPORT_UDPv6,
+ VIRTIO_NET_HASH_REPORT_UDPv6_EX
+ };
net_rx_pkt_set_protocols(pkt, buf + n->host_hdr_len,
size - n->host_hdr_len);
@@ -1594,16 +1636,24 @@ static int virtio_net_process_rss(NetClientState *nc, const uint8_t *buf,
net_hash_type = virtio_net_get_hash_type(isip4, isip6, isudp, istcp,
n->rss_data.hash_types);
if (net_hash_type > NetPktRssIpV6UdpEx) {
- return n->rss_data.default_queue;
+ if (n->rss_data.populate_hash) {
+ virtio_set_packet_hash(buf, VIRTIO_NET_HASH_REPORT_NONE, 0);
+ }
+ return n->rss_data.redirect ? n->rss_data.default_queue : -1;
}
hash = net_rx_pkt_calc_rss_hash(pkt, net_hash_type, n->rss_data.key);
- new_index = hash & (n->rss_data.indirections_len - 1);
- new_index = n->rss_data.indirections_table[new_index];
- if (index == new_index) {
- return -1;
+
+ if (n->rss_data.populate_hash) {
+ virtio_set_packet_hash(buf, reports[net_hash_type], hash);
}
- return new_index;
+
+ if (n->rss_data.redirect) {
+ new_index = hash & (n->rss_data.indirections_len - 1);
+ new_index = n->rss_data.indirections_table[new_index];
+ }
+
+ return (index == new_index) ? -1 : new_index;
}
static ssize_t virtio_net_receive_rcu(NetClientState *nc, const uint8_t *buf,
@@ -1679,6 +1729,11 @@ 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) {
+ offset = sizeof(mhdr);
+ iov_from_buf(sg, elem->in_num, offset,
+ buf + offset, n->host_hdr_len - sizeof(mhdr));
+ }
offset = n->host_hdr_len;
total += n->guest_hdr_len;
guest_offset = n->guest_hdr_len;
@@ -2671,7 +2726,9 @@ static int virtio_net_post_load_device(void *opaque, int version_id)
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));
+ VIRTIO_F_VERSION_1),
+ virtio_vdev_has_feature(vdev,
+ VIRTIO_NET_F_HASH_REPORT));
/* MAC_TABLE_ENTRIES may be different from the saved image */
if (n->mac_table.in_use > MAC_TABLE_ENTRIES) {
@@ -3290,7 +3347,7 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp)
n->vqs[0].tx_waiting = 0;
n->tx_burst = n->net_conf.txburst;
- virtio_net_set_mrg_rx_bufs(n, 0, 0);
+ virtio_net_set_mrg_rx_bufs(n, 0, 0, 0);
n->promisc = 1; /* for compatibility */
n->mac_table.macs = g_malloc0(MAC_TABLE_ENTRIES * ETH_ALEN);
@@ -3445,6 +3502,8 @@ static Property virtio_net_properties[] = {
DEFINE_PROP_BIT64("mq", VirtIONet, host_features, VIRTIO_NET_F_MQ, false),
DEFINE_PROP_BIT64("rss", VirtIONet, host_features,
VIRTIO_NET_F_RSS, false),
+ DEFINE_PROP_BIT64("hash", VirtIONet, host_features,
+ VIRTIO_NET_F_HASH_REPORT, false),
DEFINE_PROP_BIT64("guest_rsc_ext", VirtIONet, host_features,
VIRTIO_NET_F_RSC_EXT, false),
DEFINE_PROP_UINT32("rsc_interval", VirtIONet, rsc_timeout,
diff --git a/include/hw/virtio/virtio-net.h b/include/hw/virtio/virtio-net.h
index 5081f3c52a..a45ef8278e 100644
--- a/include/hw/virtio/virtio-net.h
+++ b/include/hw/virtio/virtio-net.h
@@ -131,6 +131,8 @@ typedef struct VirtioNetRscChain {
typedef struct VirtioNetRssData {
bool enabled;
+ bool redirect;
+ bool populate_hash;
uint32_t hash_types;
uint8_t key[VIRTIO_NET_RSS_MAX_KEY_SIZE];
uint16_t indirections_len;