This patch adds all the necessary infrastructure to allow a frontend to
specify toeplitz hashing of network packets on its receive side. (See
netif.h for details of the xenbus protocol).
The toeplitz hash algorithm itself was based on pseudo-code provided by
Microsoft at:
https://msdn.microsoft.com/en-us/library/windows/hardware/ff570725.aspx
Signed-off-by: Paul Durrant
Cc: Ian Campbell
Cc: Wei Liu
---
drivers/net/xen-netback/common.h| 32 ++
drivers/net/xen-netback/interface.c | 111 +++-
drivers/net/xen-netback/xenbus.c| 195
3 files changed, 335 insertions(+), 3 deletions(-)
diff --git a/drivers/net/xen-netback/common.h b/drivers/net/xen-netback/common.h
index 23f2275..4ebfad9 100644
--- a/drivers/net/xen-netback/common.h
+++ b/drivers/net/xen-netback/common.h
@@ -214,6 +214,31 @@ struct xenvif_mcast_addr {
#define XEN_NETBK_MAX_HASH_MAPPING_SIZE 128
+enum xenvif_hash_alg {
+ XEN_NETBK_HASH_UNSPECIFIED,
+ XEN_NETBK_HASH_TOEPLITZ,
+};
+
+#define XEN_NETBK_MAX_TOEPLITZ_KEY_LENGTH 40
+
+struct xenvif_toeplitz_params {
+ union {
+ struct {
+ u8 ipv4_enabled:1;
+ u8 ipv4_tcp_enabled:1;
+ u8 ipv6_enabled:1;
+ u8 ipv6_tcp_enabled:1;
+ };
+ u8 types;
+ };
+
+ u8 key[XEN_NETBK_MAX_TOEPLITZ_KEY_LENGTH];
+};
+
+union xenvif_hash_params {
+ struct xenvif_toeplitz_params toeplitz;
+};
+
struct xenvif {
/* Unique identifier for this interface. */
domid_t domid;
@@ -250,8 +275,15 @@ struct xenvif {
unsigned int table[XEN_NETBK_MAX_HASH_MAPPING_SIZE];
unsigned int length;
} hash_mapping;
+
+ /* Hash */
+ enum xenvif_hash_alg hash_alg;
+ union xenvif_hash_params hash_params;
+
struct xenbus_watch credit_watch;
struct xenbus_watch hash_mapping_watch;
+ struct xenbus_watch hash_watch;
+ struct xenbus_watch hash_params_watch;
spinlock_t lock;
diff --git a/drivers/net/xen-netback/interface.c
b/drivers/net/xen-netback/interface.c
index 0c7da7b..38eee4f 100644
--- a/drivers/net/xen-netback/interface.c
+++ b/drivers/net/xen-netback/interface.c
@@ -142,17 +142,122 @@ void xenvif_wake_queue(struct xenvif_queue *queue)
netif_tx_wake_queue(netdev_get_tx_queue(dev, id));
}
+static u32 toeplitz_hash(const u8 *k, unsigned int klen,
+const u8 *d, unsigned int dlen)
+{
+ unsigned int di, ki;
+ u64 prefix = 0;
+ u64 hash = 0;
+
+ for (ki = 0; ki < 8; ki++) {
+ prefix |= ki < klen ? k[ki] : 0;
+ prefix <<= 8;
+ }
+
+ for (di = 0; di < dlen; di++) {
+ u8 byte = d[di];
+ unsigned int bit;
+
+ prefix |= ki < klen ? k[ki] : 0;
+ ki++;
+
+ for (bit = 0; bit < 8; bit++) {
+ if (byte & 0x80)
+ hash ^= prefix;
+ byte <<= 1;
+ prefix <<= 1;
+ }
+ }
+
+ return hash >> 32;
+}
+
+static void xenvif_set_toeplitz_hash(struct xenvif *vif, struct sk_buff *skb)
+{
+ struct flow_keys flow;
+ u32 hash = 0;
+ enum pkt_hash_types type = PKT_HASH_TYPE_NONE;
+ const u8 *key = vif->hash_params.toeplitz.key;
+ const unsigned int len = ARRAY_SIZE(vif->hash_params.toeplitz.key);
+
+ memset(, 0, sizeof(flow));
+ if (!skb_flow_dissect_flow_keys(skb, , 0))
+ goto done;
+
+ if (flow.basic.n_proto == htons(ETH_P_IP)) {
+ if (vif->hash_params.toeplitz.ipv4_tcp_enabled &&
+ flow.basic.ip_proto == IPPROTO_TCP) {
+ u8 data[12];
+
+ memcpy([0], , 4);
+ memcpy([4], , 4);
+ memcpy([8], , 2);
+ memcpy([10], , 2);
+
+ hash = toeplitz_hash(key, len,
+data, sizeof(data));
+ type = PKT_HASH_TYPE_L4;
+ } else if (vif->hash_params.toeplitz.ipv4_enabled) {
+ u8 data[8];
+
+ memcpy([0], , 4);
+ memcpy([4], , 4);
+
+ hash = toeplitz_hash(key, len,
+data, sizeof(data));
+ type = PKT_HASH_TYPE_L3;
+ }
+ } else if (flow.basic.n_proto == htons(ETH_P_IPV6)) {
+ if (vif->hash_params.toeplitz.ipv6_tcp_enabled &&
+ flow.basic.ip_proto == IPPROTO_TCP) {
+ u8 data[36];
+
+ memcpy([0], , 16);
+ memcpy([16], ,