@@ -9,6 +9,180 @@
#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_calc(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 sizeof_field(struct virtio_net_toeplitz_state, key_buffer) + len;
+}
+
+static inline u32 virtio_net_hash_report(u32 types,
+ struct flow_dissector_key_basic key)
+{
+ switch (key.n_proto) {
+ case cpu_to_be16(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 cpu_to_be16(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 void virtio_net_hash_rss(const struct sk_buff *skb,
+ u32 types, const __be32 *key,
+ struct virtio_net_hash *hash)
+{
+ struct virtio_net_toeplitz_state toeplitz_state = {
+ .key_buffer = be32_to_cpu(*key),
+ .key = key
+ };
+ struct flow_keys flow;
+ u16 report;
+
+ if (!skb_flow_dissect_flow_keys(skb, &flow, 0)) {
+ hash->report = VIRTIO_NET_HASH_REPORT_NONE;
+ return;
+ }
+
+ report = virtio_net_hash_report(types, flow.basic);
+
+ switch (report) {
+ case VIRTIO_NET_HASH_REPORT_IPv4:
+ virtio_net_toeplitz_calc(&toeplitz_state,
+ (__be32 *)&flow.addrs.v4addrs,
+ sizeof(flow.addrs.v4addrs) / 4);
+ break;
+
+ case VIRTIO_NET_HASH_REPORT_TCPv4:
+ virtio_net_toeplitz_calc(&toeplitz_state,
+ (__be32 *)&flow.addrs.v4addrs,
+ sizeof(flow.addrs.v4addrs) / 4);
+ virtio_net_toeplitz_calc(&toeplitz_state, &flow.ports.ports, 1);
+ break;
+
+ case VIRTIO_NET_HASH_REPORT_UDPv4:
+ virtio_net_toeplitz_calc(&toeplitz_state,
+ (__be32 *)&flow.addrs.v4addrs,
+ sizeof(flow.addrs.v4addrs) / 4);
+ virtio_net_toeplitz_calc(&toeplitz_state, &flow.ports.ports, 1);
+ break;
+
+ case VIRTIO_NET_HASH_REPORT_IPv6:
+ virtio_net_toeplitz_calc(&toeplitz_state,
+ (__be32 *)&flow.addrs.v6addrs,
+ sizeof(flow.addrs.v6addrs) / 4);
+ break;
+
+ case VIRTIO_NET_HASH_REPORT_TCPv6:
+ virtio_net_toeplitz_calc(&toeplitz_state,
+ (__be32 *)&flow.addrs.v6addrs,
+ sizeof(flow.addrs.v6addrs) / 4);
+ virtio_net_toeplitz_calc(&toeplitz_state, &flow.ports.ports, 1);
+ break;
+
+ case VIRTIO_NET_HASH_REPORT_UDPv6:
+ virtio_net_toeplitz_calc(&toeplitz_state,
+ (__be32 *)&flow.addrs.v6addrs,
+ sizeof(flow.addrs.v6addrs) / 4);
+ virtio_net_toeplitz_calc(&toeplitz_state, &flow.ports.ports, 1);
+ break;
+
+ default:
+ hash->report = VIRTIO_NET_HASH_REPORT_NONE;
+ return;
+ }
+
+ hash->value = toeplitz_state.hash;
+ hash->report = report;
+}
+
static inline bool virtio_net_hdr_match_proto(__be16 protocol, __u8 gso_type)
{
switch (gso_type & ~VIRTIO_NET_HDR_GSO_ECN) {
They are useful to implement VIRTIO_NET_F_RSS and VIRTIO_NET_F_HASH_REPORT. Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com> --- include/linux/virtio_net.h | 174 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 174 insertions(+)