They are useful to implement VIRTIO_NET_F_RSS and VIRTIO_NET_F_HASH_REPORT. Signed-off-by: Akihiko Odaki <akihiko.odaki@xxxxxxxxxx> --- include/linux/virtio_net.h | 157 +++++++++++++++++++++++++++++++++++++ 1 file changed, 157 insertions(+) diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h index 7b4dd69555e4..f05781ddc261 100644 --- a/include/linux/virtio_net.h +++ b/include/linux/virtio_net.h @@ -7,6 +7,143 @@ #include <uapi/linux/udp.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 u32 *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) + +static inline void virtio_net_toeplitz(struct virtio_net_toeplitz_state *state, + const u32 *input, size_t len) +{ + u32 key; + + while (len) { + state->key++; + key = ntohl(*state->key); + + for (u32 bit = BIT(31); bit; bit >>= 1) { + if (*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 void virtio_net_hash(const struct sk_buff *skb, + u32 types, const u32 *key, + struct virtio_net_hash *hash) +{ + u16 report = VIRTIO_NET_HASH_REPORT_NONE; + struct virtio_net_toeplitz_state toeplitz_state = { + .key_buffer = ntohl(*key), + .key = key + }; + struct flow_keys flow; + + if (!skb_flow_dissect_flow_keys(skb, &flow, 0)) + return; + + switch (flow.basic.n_proto) { + case htons(ETH_P_IP): + if (flow.basic.ip_proto == IPPROTO_TCP && + (types & VIRTIO_NET_RSS_HASH_TYPE_TCPv4)) { + report = VIRTIO_NET_HASH_REPORT_TCPv4; + virtio_net_toeplitz(&toeplitz_state, + (u32 *)&flow.addrs.v4addrs, + sizeof(flow.addrs.v4addrs) / 4); + virtio_net_toeplitz(&toeplitz_state, &flow.ports.ports, + 1); + } else if (flow.basic.ip_proto == IPPROTO_UDP && + (types & VIRTIO_NET_RSS_HASH_TYPE_UDPv4)) { + report = VIRTIO_NET_HASH_REPORT_UDPv4; + virtio_net_toeplitz(&toeplitz_state, + (u32 *)&flow.addrs.v4addrs, + sizeof(flow.addrs.v4addrs) / 4); + virtio_net_toeplitz(&toeplitz_state, &flow.ports.ports, + 1); + } else if (types & VIRTIO_NET_RSS_HASH_TYPE_IPv4) { + report = VIRTIO_NET_HASH_REPORT_IPv4; + virtio_net_toeplitz(&toeplitz_state, + (u32 *)&flow.addrs.v4addrs, + sizeof(flow.addrs.v4addrs) / 4); + } + break; + + case htons(ETH_P_IPV6): + if (flow.basic.ip_proto == IPPROTO_TCP && + (types & VIRTIO_NET_RSS_HASH_TYPE_TCPv6)) { + report = VIRTIO_NET_HASH_REPORT_TCPv6; + virtio_net_toeplitz(&toeplitz_state, + (u32 *)&flow.addrs.v6addrs, + sizeof(flow.addrs.v6addrs) / 4); + virtio_net_toeplitz(&toeplitz_state, &flow.ports.ports, + 1); + } else if (flow.basic.ip_proto == IPPROTO_UDP && + (types & VIRTIO_NET_RSS_HASH_TYPE_UDPv6)) { + report = VIRTIO_NET_HASH_REPORT_UDPv6; + virtio_net_toeplitz(&toeplitz_state, + (u32 *)&flow.addrs.v6addrs, + sizeof(flow.addrs.v6addrs) / 4); + virtio_net_toeplitz(&toeplitz_state, &flow.ports.ports, + 1); + } else if (types & VIRTIO_NET_RSS_HASH_TYPE_IPv6) { + report = VIRTIO_NET_HASH_REPORT_IPv6; + virtio_net_toeplitz(&toeplitz_state, + (u32 *)&flow.addrs.v6addrs, + sizeof(flow.addrs.v6addrs) / 4); + } + break; + } + + 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) { @@ -216,4 +353,24 @@ 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->hash_value = cpu_to_le32(hash->value); + hdr->hash_report = cpu_to_le16(hash->report); + } + + return ret; +} + #endif /* _LINUX_VIRTIO_NET_H */ -- 2.42.0