|
@@ -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,
|