[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20240915-rss-v3-2-c630015db082@daynix.com>
Date: Sun, 15 Sep 2024 10:17:41 +0900
From: Akihiko Odaki <akihiko.odaki@...nix.com>
To: Jonathan Corbet <corbet@....net>,
Willem de Bruijn <willemdebruijn.kernel@...il.com>,
Jason Wang <jasowang@...hat.com>, "David S. Miller" <davem@...emloft.net>,
Eric Dumazet <edumazet@...gle.com>, Jakub Kicinski <kuba@...nel.org>,
Paolo Abeni <pabeni@...hat.com>, "Michael S. Tsirkin" <mst@...hat.com>,
Xuan Zhuo <xuanzhuo@...ux.alibaba.com>, Shuah Khan <shuah@...nel.org>,
linux-doc@...r.kernel.org, linux-kernel@...r.kernel.org,
netdev@...r.kernel.org, kvm@...r.kernel.org,
virtualization@...ts.linux-foundation.org, linux-kselftest@...r.kernel.org,
Yuri Benditovich <yuri.benditovich@...nix.com>,
Andrew Melnychenko <andrew@...nix.com>,
Akihiko Odaki <akihiko.odaki@...nix.com>
Subject: [PATCH RFC v3 2/9] virtio_net: Add functions for hashing
They are useful to implement VIRTIO_NET_F_RSS and
VIRTIO_NET_F_HASH_REPORT.
Signed-off-by: Akihiko Odaki <akihiko.odaki@...nix.com>
---
include/linux/virtio_net.h | 198 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 198 insertions(+)
diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h
index 6c395a2600e8..7ee2e2f2625a 100644
--- a/include/linux/virtio_net.h
+++ b/include/linux/virtio_net.h
@@ -9,6 +9,183 @@
#include <uapi/linux/tcp.h>
#include <uapi/linux/virtio_net.h>
+struct virtio_net_hash {
+ u32 value;
+ u16 report;
+};
+
+struct virtio_net_toeplitz_state {
+ u32 hash;
+ u32 key_buffer;
+ const __be32 *key;
+};
+
+#define VIRTIO_NET_SUPPORTED_HASH_TYPES (VIRTIO_NET_RSS_HASH_TYPE_IPv4 | \
+ VIRTIO_NET_RSS_HASH_TYPE_TCPv4 | \
+ VIRTIO_NET_RSS_HASH_TYPE_UDPv4 | \
+ VIRTIO_NET_RSS_HASH_TYPE_IPv6 | \
+ VIRTIO_NET_RSS_HASH_TYPE_TCPv6 | \
+ VIRTIO_NET_RSS_HASH_TYPE_UDPv6)
+
+#define VIRTIO_NET_RSS_MAX_KEY_SIZE 40
+
+static inline void virtio_net_toeplitz(struct virtio_net_toeplitz_state *state,
+ const __be32 *input, size_t len)
+{
+ u32 key;
+
+ while (len) {
+ state->key++;
+ key = be32_to_cpu(*state->key);
+
+ for (u32 bit = BIT(31); bit; bit >>= 1) {
+ if (be32_to_cpu(*input) & bit)
+ state->hash ^= state->key_buffer;
+
+ state->key_buffer =
+ (state->key_buffer << 1) | !!(key & bit);
+ }
+
+ input++;
+ len--;
+ }
+}
+
+static inline u8 virtio_net_hash_key_length(u32 types)
+{
+ size_t len = 0;
+
+ if (types & VIRTIO_NET_HASH_REPORT_IPv4)
+ len = max(len,
+ sizeof(struct flow_dissector_key_ipv4_addrs));
+
+ if (types &
+ (VIRTIO_NET_HASH_REPORT_TCPv4 | VIRTIO_NET_HASH_REPORT_UDPv4))
+ len = max(len,
+ sizeof(struct flow_dissector_key_ipv4_addrs) +
+ sizeof(struct flow_dissector_key_ports));
+
+ if (types & VIRTIO_NET_HASH_REPORT_IPv6)
+ len = max(len,
+ sizeof(struct flow_dissector_key_ipv6_addrs));
+
+ if (types &
+ (VIRTIO_NET_HASH_REPORT_TCPv6 | VIRTIO_NET_HASH_REPORT_UDPv6))
+ len = max(len,
+ sizeof(struct flow_dissector_key_ipv6_addrs) +
+ sizeof(struct flow_dissector_key_ports));
+
+ return 4 + len;
+}
+
+static inline u32 virtio_net_hash_report(u32 types,
+ struct flow_dissector_key_basic key)
+{
+ switch (key.n_proto) {
+ case htons(ETH_P_IP):
+ if (key.ip_proto == IPPROTO_TCP &&
+ (types & VIRTIO_NET_RSS_HASH_TYPE_TCPv4))
+ return VIRTIO_NET_HASH_REPORT_TCPv4;
+
+ if (key.ip_proto == IPPROTO_UDP &&
+ (types & VIRTIO_NET_RSS_HASH_TYPE_UDPv4))
+ return VIRTIO_NET_HASH_REPORT_UDPv4;
+
+ if (types & VIRTIO_NET_RSS_HASH_TYPE_IPv4)
+ return VIRTIO_NET_HASH_REPORT_IPv4;
+
+ return VIRTIO_NET_HASH_REPORT_NONE;
+
+ case htons(ETH_P_IPV6):
+ if (key.ip_proto == IPPROTO_TCP &&
+ (types & VIRTIO_NET_RSS_HASH_TYPE_TCPv6))
+ return VIRTIO_NET_HASH_REPORT_TCPv6;
+
+ if (key.ip_proto == IPPROTO_UDP &&
+ (types & VIRTIO_NET_RSS_HASH_TYPE_UDPv6))
+ return VIRTIO_NET_HASH_REPORT_UDPv6;
+
+ if (types & VIRTIO_NET_RSS_HASH_TYPE_IPv6)
+ return VIRTIO_NET_HASH_REPORT_IPv6;
+
+ return VIRTIO_NET_HASH_REPORT_NONE;
+
+ default:
+ return VIRTIO_NET_HASH_REPORT_NONE;
+ }
+}
+
+static inline bool virtio_net_hash_rss(const struct sk_buff *skb,
+ u32 types, const __be32 *key,
+ struct virtio_net_hash *hash)
+{
+ u16 report;
+ struct virtio_net_toeplitz_state toeplitz_state = {
+ .key_buffer = be32_to_cpu(*key),
+ .key = key
+ };
+ struct flow_keys flow;
+
+ if (!skb_flow_dissect_flow_keys(skb, &flow, 0))
+ return false;
+
+ report = virtio_net_hash_report(types, flow.basic);
+
+ switch (report) {
+ case VIRTIO_NET_HASH_REPORT_IPv4:
+ virtio_net_toeplitz(&toeplitz_state,
+ (__be32 *)&flow.addrs.v4addrs,
+ sizeof(flow.addrs.v4addrs) / 4);
+ break;
+
+ case VIRTIO_NET_HASH_REPORT_TCPv4:
+ virtio_net_toeplitz(&toeplitz_state,
+ (__be32 *)&flow.addrs.v4addrs,
+ sizeof(flow.addrs.v4addrs) / 4);
+ virtio_net_toeplitz(&toeplitz_state, &flow.ports.ports,
+ 1);
+ break;
+
+ case VIRTIO_NET_HASH_REPORT_UDPv4:
+ virtio_net_toeplitz(&toeplitz_state,
+ (__be32 *)&flow.addrs.v4addrs,
+ sizeof(flow.addrs.v4addrs) / 4);
+ virtio_net_toeplitz(&toeplitz_state, &flow.ports.ports,
+ 1);
+ break;
+
+ case VIRTIO_NET_HASH_REPORT_IPv6:
+ virtio_net_toeplitz(&toeplitz_state,
+ (__be32 *)&flow.addrs.v6addrs,
+ sizeof(flow.addrs.v6addrs) / 4);
+ break;
+
+ case VIRTIO_NET_HASH_REPORT_TCPv6:
+ virtio_net_toeplitz(&toeplitz_state,
+ (__be32 *)&flow.addrs.v6addrs,
+ sizeof(flow.addrs.v6addrs) / 4);
+ virtio_net_toeplitz(&toeplitz_state, &flow.ports.ports,
+ 1);
+ break;
+
+ case VIRTIO_NET_HASH_REPORT_UDPv6:
+ virtio_net_toeplitz(&toeplitz_state,
+ (__be32 *)&flow.addrs.v6addrs,
+ sizeof(flow.addrs.v6addrs) / 4);
+ virtio_net_toeplitz(&toeplitz_state, &flow.ports.ports,
+ 1);
+ break;
+
+ default:
+ return false;
+ }
+
+ hash->value = toeplitz_state.hash;
+ hash->report = report;
+
+ return true;
+}
+
static inline bool virtio_net_hdr_match_proto(__be16 protocol, __u8 gso_type)
{
switch (gso_type & ~VIRTIO_NET_HDR_GSO_ECN) {
@@ -239,4 +416,25 @@ static inline int virtio_net_hdr_from_skb(const struct sk_buff *skb,
return 0;
}
+static inline int virtio_net_hdr_v1_hash_from_skb(const struct sk_buff *skb,
+ struct virtio_net_hdr_v1_hash *hdr,
+ bool has_data_valid,
+ int vlan_hlen,
+ const struct virtio_net_hash *hash)
+{
+ int ret;
+
+ memset(hdr, 0, sizeof(*hdr));
+
+ ret = virtio_net_hdr_from_skb(skb, (struct virtio_net_hdr *)hdr,
+ true, has_data_valid, vlan_hlen);
+ if (!ret) {
+ hdr->hdr.num_buffers = cpu_to_le16(1);
+ hdr->hash_value = cpu_to_le32(hash->value);
+ hdr->hash_report = cpu_to_le16(hash->report);
+ }
+
+ return ret;
+}
+
#endif /* _LINUX_VIRTIO_NET_H */
--
2.46.0
Powered by blists - more mailing lists