18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright (c) 2016 Citrix Systems Inc.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or
58c2ecf20Sopenharmony_ci * modify it under the terms of the GNU General Public License version 2
68c2ecf20Sopenharmony_ci * as published by the Free Softare Foundation; or, when distributed
78c2ecf20Sopenharmony_ci * separately from the Linux kernel or incorporated into other
88c2ecf20Sopenharmony_ci * software packages, subject to the following license:
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a copy
118c2ecf20Sopenharmony_ci * of this source file (the "Software"), to deal in the Software without
128c2ecf20Sopenharmony_ci * restriction, including without limitation the rights to use, copy, modify,
138c2ecf20Sopenharmony_ci * merge, publish, distribute, sublicense, and/or sell copies of the Software,
148c2ecf20Sopenharmony_ci * and to permit persons to whom the Software is furnished to do so, subject to
158c2ecf20Sopenharmony_ci * the following conditions:
168c2ecf20Sopenharmony_ci *
178c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice shall be included in
188c2ecf20Sopenharmony_ci * all copies or substantial portions of the Software.
198c2ecf20Sopenharmony_ci *
208c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
218c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
228c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
238c2ecf20Sopenharmony_ci * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
248c2ecf20Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
258c2ecf20Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
268c2ecf20Sopenharmony_ci * IN THE SOFTWARE.
278c2ecf20Sopenharmony_ci */
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#define XEN_NETIF_DEFINE_TOEPLITZ
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#include "common.h"
328c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
338c2ecf20Sopenharmony_ci#include <linux/rculist.h>
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic void xenvif_add_hash(struct xenvif *vif, const u8 *tag,
368c2ecf20Sopenharmony_ci			    unsigned int len, u32 val)
378c2ecf20Sopenharmony_ci{
388c2ecf20Sopenharmony_ci	struct xenvif_hash_cache_entry *new, *entry, *oldest;
398c2ecf20Sopenharmony_ci	unsigned long flags;
408c2ecf20Sopenharmony_ci	bool found;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	new = kmalloc(sizeof(*entry), GFP_ATOMIC);
438c2ecf20Sopenharmony_ci	if (!new)
448c2ecf20Sopenharmony_ci		return;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	memcpy(new->tag, tag, len);
478c2ecf20Sopenharmony_ci	new->len = len;
488c2ecf20Sopenharmony_ci	new->val = val;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vif->hash.cache.lock, flags);
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	found = false;
538c2ecf20Sopenharmony_ci	oldest = NULL;
548c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(entry, &vif->hash.cache.list, link,
558c2ecf20Sopenharmony_ci				lockdep_is_held(&vif->hash.cache.lock)) {
568c2ecf20Sopenharmony_ci		/* Make sure we don't add duplicate entries */
578c2ecf20Sopenharmony_ci		if (entry->len == len &&
588c2ecf20Sopenharmony_ci		    memcmp(entry->tag, tag, len) == 0)
598c2ecf20Sopenharmony_ci			found = true;
608c2ecf20Sopenharmony_ci		if (!oldest || entry->seq < oldest->seq)
618c2ecf20Sopenharmony_ci			oldest = entry;
628c2ecf20Sopenharmony_ci	}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	if (!found) {
658c2ecf20Sopenharmony_ci		new->seq = atomic_inc_return(&vif->hash.cache.seq);
668c2ecf20Sopenharmony_ci		list_add_rcu(&new->link, &vif->hash.cache.list);
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci		if (++vif->hash.cache.count > xenvif_hash_cache_size) {
698c2ecf20Sopenharmony_ci			list_del_rcu(&oldest->link);
708c2ecf20Sopenharmony_ci			vif->hash.cache.count--;
718c2ecf20Sopenharmony_ci			kfree_rcu(oldest, rcu);
728c2ecf20Sopenharmony_ci		}
738c2ecf20Sopenharmony_ci	}
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vif->hash.cache.lock, flags);
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	if (found)
788c2ecf20Sopenharmony_ci		kfree(new);
798c2ecf20Sopenharmony_ci}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_cistatic u32 xenvif_new_hash(struct xenvif *vif, const u8 *data,
828c2ecf20Sopenharmony_ci			   unsigned int len)
838c2ecf20Sopenharmony_ci{
848c2ecf20Sopenharmony_ci	u32 val;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	val = xen_netif_toeplitz_hash(vif->hash.key,
878c2ecf20Sopenharmony_ci				      sizeof(vif->hash.key),
888c2ecf20Sopenharmony_ci				      data, len);
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	if (xenvif_hash_cache_size != 0)
918c2ecf20Sopenharmony_ci		xenvif_add_hash(vif, data, len, val);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	return val;
948c2ecf20Sopenharmony_ci}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cistatic void xenvif_flush_hash(struct xenvif *vif)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	struct xenvif_hash_cache_entry *entry;
998c2ecf20Sopenharmony_ci	unsigned long flags;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	if (xenvif_hash_cache_size == 0)
1028c2ecf20Sopenharmony_ci		return;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vif->hash.cache.lock, flags);
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(entry, &vif->hash.cache.list, link,
1078c2ecf20Sopenharmony_ci				lockdep_is_held(&vif->hash.cache.lock)) {
1088c2ecf20Sopenharmony_ci		list_del_rcu(&entry->link);
1098c2ecf20Sopenharmony_ci		vif->hash.cache.count--;
1108c2ecf20Sopenharmony_ci		kfree_rcu(entry, rcu);
1118c2ecf20Sopenharmony_ci	}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vif->hash.cache.lock, flags);
1148c2ecf20Sopenharmony_ci}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cistatic u32 xenvif_find_hash(struct xenvif *vif, const u8 *data,
1178c2ecf20Sopenharmony_ci			    unsigned int len)
1188c2ecf20Sopenharmony_ci{
1198c2ecf20Sopenharmony_ci	struct xenvif_hash_cache_entry *entry;
1208c2ecf20Sopenharmony_ci	u32 val;
1218c2ecf20Sopenharmony_ci	bool found;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	if (len >= XEN_NETBK_HASH_TAG_SIZE)
1248c2ecf20Sopenharmony_ci		return 0;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	if (xenvif_hash_cache_size == 0)
1278c2ecf20Sopenharmony_ci		return xenvif_new_hash(vif, data, len);
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	rcu_read_lock();
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	found = false;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(entry, &vif->hash.cache.list, link) {
1348c2ecf20Sopenharmony_ci		if (entry->len == len &&
1358c2ecf20Sopenharmony_ci		    memcmp(entry->tag, data, len) == 0) {
1368c2ecf20Sopenharmony_ci			val = entry->val;
1378c2ecf20Sopenharmony_ci			entry->seq = atomic_inc_return(&vif->hash.cache.seq);
1388c2ecf20Sopenharmony_ci			found = true;
1398c2ecf20Sopenharmony_ci			break;
1408c2ecf20Sopenharmony_ci		}
1418c2ecf20Sopenharmony_ci	}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	rcu_read_unlock();
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	if (!found)
1468c2ecf20Sopenharmony_ci		val = xenvif_new_hash(vif, data, len);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	return val;
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_civoid xenvif_set_skb_hash(struct xenvif *vif, struct sk_buff *skb)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	struct flow_keys flow;
1548c2ecf20Sopenharmony_ci	u32 hash = 0;
1558c2ecf20Sopenharmony_ci	enum pkt_hash_types type = PKT_HASH_TYPE_NONE;
1568c2ecf20Sopenharmony_ci	u32 flags = vif->hash.flags;
1578c2ecf20Sopenharmony_ci	bool has_tcp_hdr;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	/* Quick rejection test: If the network protocol doesn't
1608c2ecf20Sopenharmony_ci	 * correspond to any enabled hash type then there's no point
1618c2ecf20Sopenharmony_ci	 * in parsing the packet header.
1628c2ecf20Sopenharmony_ci	 */
1638c2ecf20Sopenharmony_ci	switch (skb->protocol) {
1648c2ecf20Sopenharmony_ci	case htons(ETH_P_IP):
1658c2ecf20Sopenharmony_ci		if (flags & (XEN_NETIF_CTRL_HASH_TYPE_IPV4_TCP |
1668c2ecf20Sopenharmony_ci			     XEN_NETIF_CTRL_HASH_TYPE_IPV4))
1678c2ecf20Sopenharmony_ci			break;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci		goto done;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	case htons(ETH_P_IPV6):
1728c2ecf20Sopenharmony_ci		if (flags & (XEN_NETIF_CTRL_HASH_TYPE_IPV6_TCP |
1738c2ecf20Sopenharmony_ci			     XEN_NETIF_CTRL_HASH_TYPE_IPV6))
1748c2ecf20Sopenharmony_ci			break;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci		goto done;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	default:
1798c2ecf20Sopenharmony_ci		goto done;
1808c2ecf20Sopenharmony_ci	}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	memset(&flow, 0, sizeof(flow));
1838c2ecf20Sopenharmony_ci	if (!skb_flow_dissect_flow_keys(skb, &flow, 0))
1848c2ecf20Sopenharmony_ci		goto done;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	has_tcp_hdr = (flow.basic.ip_proto == IPPROTO_TCP) &&
1878c2ecf20Sopenharmony_ci		      !(flow.control.flags & FLOW_DIS_IS_FRAGMENT);
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	switch (skb->protocol) {
1908c2ecf20Sopenharmony_ci	case htons(ETH_P_IP):
1918c2ecf20Sopenharmony_ci		if (has_tcp_hdr &&
1928c2ecf20Sopenharmony_ci		    (flags & XEN_NETIF_CTRL_HASH_TYPE_IPV4_TCP)) {
1938c2ecf20Sopenharmony_ci			u8 data[12];
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci			memcpy(&data[0], &flow.addrs.v4addrs.src, 4);
1968c2ecf20Sopenharmony_ci			memcpy(&data[4], &flow.addrs.v4addrs.dst, 4);
1978c2ecf20Sopenharmony_ci			memcpy(&data[8], &flow.ports.src, 2);
1988c2ecf20Sopenharmony_ci			memcpy(&data[10], &flow.ports.dst, 2);
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci			hash = xenvif_find_hash(vif, data, sizeof(data));
2018c2ecf20Sopenharmony_ci			type = PKT_HASH_TYPE_L4;
2028c2ecf20Sopenharmony_ci		} else if (flags & XEN_NETIF_CTRL_HASH_TYPE_IPV4) {
2038c2ecf20Sopenharmony_ci			u8 data[8];
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci			memcpy(&data[0], &flow.addrs.v4addrs.src, 4);
2068c2ecf20Sopenharmony_ci			memcpy(&data[4], &flow.addrs.v4addrs.dst, 4);
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci			hash = xenvif_find_hash(vif, data, sizeof(data));
2098c2ecf20Sopenharmony_ci			type = PKT_HASH_TYPE_L3;
2108c2ecf20Sopenharmony_ci		}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci		break;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	case htons(ETH_P_IPV6):
2158c2ecf20Sopenharmony_ci		if (has_tcp_hdr &&
2168c2ecf20Sopenharmony_ci		    (flags & XEN_NETIF_CTRL_HASH_TYPE_IPV6_TCP)) {
2178c2ecf20Sopenharmony_ci			u8 data[36];
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci			memcpy(&data[0], &flow.addrs.v6addrs.src, 16);
2208c2ecf20Sopenharmony_ci			memcpy(&data[16], &flow.addrs.v6addrs.dst, 16);
2218c2ecf20Sopenharmony_ci			memcpy(&data[32], &flow.ports.src, 2);
2228c2ecf20Sopenharmony_ci			memcpy(&data[34], &flow.ports.dst, 2);
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci			hash = xenvif_find_hash(vif, data, sizeof(data));
2258c2ecf20Sopenharmony_ci			type = PKT_HASH_TYPE_L4;
2268c2ecf20Sopenharmony_ci		} else if (flags & XEN_NETIF_CTRL_HASH_TYPE_IPV6) {
2278c2ecf20Sopenharmony_ci			u8 data[32];
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci			memcpy(&data[0], &flow.addrs.v6addrs.src, 16);
2308c2ecf20Sopenharmony_ci			memcpy(&data[16], &flow.addrs.v6addrs.dst, 16);
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci			hash = xenvif_find_hash(vif, data, sizeof(data));
2338c2ecf20Sopenharmony_ci			type = PKT_HASH_TYPE_L3;
2348c2ecf20Sopenharmony_ci		}
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci		break;
2378c2ecf20Sopenharmony_ci	}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_cidone:
2408c2ecf20Sopenharmony_ci	if (type == PKT_HASH_TYPE_NONE)
2418c2ecf20Sopenharmony_ci		skb_clear_hash(skb);
2428c2ecf20Sopenharmony_ci	else
2438c2ecf20Sopenharmony_ci		__skb_set_sw_hash(skb, hash, type == PKT_HASH_TYPE_L4);
2448c2ecf20Sopenharmony_ci}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ciu32 xenvif_set_hash_alg(struct xenvif *vif, u32 alg)
2478c2ecf20Sopenharmony_ci{
2488c2ecf20Sopenharmony_ci	switch (alg) {
2498c2ecf20Sopenharmony_ci	case XEN_NETIF_CTRL_HASH_ALGORITHM_NONE:
2508c2ecf20Sopenharmony_ci	case XEN_NETIF_CTRL_HASH_ALGORITHM_TOEPLITZ:
2518c2ecf20Sopenharmony_ci		break;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	default:
2548c2ecf20Sopenharmony_ci		return XEN_NETIF_CTRL_STATUS_INVALID_PARAMETER;
2558c2ecf20Sopenharmony_ci	}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	vif->hash.alg = alg;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	return XEN_NETIF_CTRL_STATUS_SUCCESS;
2608c2ecf20Sopenharmony_ci}
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ciu32 xenvif_get_hash_flags(struct xenvif *vif, u32 *flags)
2638c2ecf20Sopenharmony_ci{
2648c2ecf20Sopenharmony_ci	if (vif->hash.alg == XEN_NETIF_CTRL_HASH_ALGORITHM_NONE)
2658c2ecf20Sopenharmony_ci		return XEN_NETIF_CTRL_STATUS_NOT_SUPPORTED;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	*flags = XEN_NETIF_CTRL_HASH_TYPE_IPV4 |
2688c2ecf20Sopenharmony_ci		 XEN_NETIF_CTRL_HASH_TYPE_IPV4_TCP |
2698c2ecf20Sopenharmony_ci		 XEN_NETIF_CTRL_HASH_TYPE_IPV6 |
2708c2ecf20Sopenharmony_ci		 XEN_NETIF_CTRL_HASH_TYPE_IPV6_TCP;
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	return XEN_NETIF_CTRL_STATUS_SUCCESS;
2738c2ecf20Sopenharmony_ci}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ciu32 xenvif_set_hash_flags(struct xenvif *vif, u32 flags)
2768c2ecf20Sopenharmony_ci{
2778c2ecf20Sopenharmony_ci	if (flags & ~(XEN_NETIF_CTRL_HASH_TYPE_IPV4 |
2788c2ecf20Sopenharmony_ci		      XEN_NETIF_CTRL_HASH_TYPE_IPV4_TCP |
2798c2ecf20Sopenharmony_ci		      XEN_NETIF_CTRL_HASH_TYPE_IPV6 |
2808c2ecf20Sopenharmony_ci		      XEN_NETIF_CTRL_HASH_TYPE_IPV6_TCP))
2818c2ecf20Sopenharmony_ci		return XEN_NETIF_CTRL_STATUS_INVALID_PARAMETER;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	if (vif->hash.alg == XEN_NETIF_CTRL_HASH_ALGORITHM_NONE)
2848c2ecf20Sopenharmony_ci		return XEN_NETIF_CTRL_STATUS_INVALID_PARAMETER;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	vif->hash.flags = flags;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	return XEN_NETIF_CTRL_STATUS_SUCCESS;
2898c2ecf20Sopenharmony_ci}
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ciu32 xenvif_set_hash_key(struct xenvif *vif, u32 gref, u32 len)
2928c2ecf20Sopenharmony_ci{
2938c2ecf20Sopenharmony_ci	u8 *key = vif->hash.key;
2948c2ecf20Sopenharmony_ci	struct gnttab_copy copy_op = {
2958c2ecf20Sopenharmony_ci		.source.u.ref = gref,
2968c2ecf20Sopenharmony_ci		.source.domid = vif->domid,
2978c2ecf20Sopenharmony_ci		.dest.u.gmfn = virt_to_gfn(key),
2988c2ecf20Sopenharmony_ci		.dest.domid = DOMID_SELF,
2998c2ecf20Sopenharmony_ci		.dest.offset = xen_offset_in_page(key),
3008c2ecf20Sopenharmony_ci		.len = len,
3018c2ecf20Sopenharmony_ci		.flags = GNTCOPY_source_gref
3028c2ecf20Sopenharmony_ci	};
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	if (len > XEN_NETBK_MAX_HASH_KEY_SIZE)
3058c2ecf20Sopenharmony_ci		return XEN_NETIF_CTRL_STATUS_INVALID_PARAMETER;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	if (copy_op.len != 0) {
3088c2ecf20Sopenharmony_ci		gnttab_batch_copy(&copy_op, 1);
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci		if (copy_op.status != GNTST_okay)
3118c2ecf20Sopenharmony_ci			return XEN_NETIF_CTRL_STATUS_INVALID_PARAMETER;
3128c2ecf20Sopenharmony_ci	}
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	/* Clear any remaining key octets */
3158c2ecf20Sopenharmony_ci	if (len < XEN_NETBK_MAX_HASH_KEY_SIZE)
3168c2ecf20Sopenharmony_ci		memset(key + len, 0, XEN_NETBK_MAX_HASH_KEY_SIZE - len);
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	xenvif_flush_hash(vif);
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	return XEN_NETIF_CTRL_STATUS_SUCCESS;
3218c2ecf20Sopenharmony_ci}
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ciu32 xenvif_set_hash_mapping_size(struct xenvif *vif, u32 size)
3248c2ecf20Sopenharmony_ci{
3258c2ecf20Sopenharmony_ci	if (size > XEN_NETBK_MAX_HASH_MAPPING_SIZE)
3268c2ecf20Sopenharmony_ci		return XEN_NETIF_CTRL_STATUS_INVALID_PARAMETER;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	vif->hash.size = size;
3298c2ecf20Sopenharmony_ci	memset(vif->hash.mapping[vif->hash.mapping_sel], 0,
3308c2ecf20Sopenharmony_ci	       sizeof(u32) * size);
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	return XEN_NETIF_CTRL_STATUS_SUCCESS;
3338c2ecf20Sopenharmony_ci}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ciu32 xenvif_set_hash_mapping(struct xenvif *vif, u32 gref, u32 len,
3368c2ecf20Sopenharmony_ci			    u32 off)
3378c2ecf20Sopenharmony_ci{
3388c2ecf20Sopenharmony_ci	u32 *mapping = vif->hash.mapping[!vif->hash.mapping_sel];
3398c2ecf20Sopenharmony_ci	unsigned int nr = 1;
3408c2ecf20Sopenharmony_ci	struct gnttab_copy copy_op[2] = {{
3418c2ecf20Sopenharmony_ci		.source.u.ref = gref,
3428c2ecf20Sopenharmony_ci		.source.domid = vif->domid,
3438c2ecf20Sopenharmony_ci		.dest.domid = DOMID_SELF,
3448c2ecf20Sopenharmony_ci		.len = len * sizeof(*mapping),
3458c2ecf20Sopenharmony_ci		.flags = GNTCOPY_source_gref
3468c2ecf20Sopenharmony_ci	}};
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	if ((off + len < off) || (off + len > vif->hash.size) ||
3498c2ecf20Sopenharmony_ci	    len > XEN_PAGE_SIZE / sizeof(*mapping))
3508c2ecf20Sopenharmony_ci		return XEN_NETIF_CTRL_STATUS_INVALID_PARAMETER;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	copy_op[0].dest.u.gmfn = virt_to_gfn(mapping + off);
3538c2ecf20Sopenharmony_ci	copy_op[0].dest.offset = xen_offset_in_page(mapping + off);
3548c2ecf20Sopenharmony_ci	if (copy_op[0].dest.offset + copy_op[0].len > XEN_PAGE_SIZE) {
3558c2ecf20Sopenharmony_ci		copy_op[1] = copy_op[0];
3568c2ecf20Sopenharmony_ci		copy_op[1].source.offset = XEN_PAGE_SIZE - copy_op[0].dest.offset;
3578c2ecf20Sopenharmony_ci		copy_op[1].dest.u.gmfn = virt_to_gfn(mapping + off + len);
3588c2ecf20Sopenharmony_ci		copy_op[1].dest.offset = 0;
3598c2ecf20Sopenharmony_ci		copy_op[1].len = copy_op[0].len - copy_op[1].source.offset;
3608c2ecf20Sopenharmony_ci		copy_op[0].len = copy_op[1].source.offset;
3618c2ecf20Sopenharmony_ci		nr = 2;
3628c2ecf20Sopenharmony_ci	}
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	memcpy(mapping, vif->hash.mapping[vif->hash.mapping_sel],
3658c2ecf20Sopenharmony_ci	       vif->hash.size * sizeof(*mapping));
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	if (copy_op[0].len != 0) {
3688c2ecf20Sopenharmony_ci		gnttab_batch_copy(copy_op, nr);
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci		if (copy_op[0].status != GNTST_okay ||
3718c2ecf20Sopenharmony_ci		    copy_op[nr - 1].status != GNTST_okay)
3728c2ecf20Sopenharmony_ci			return XEN_NETIF_CTRL_STATUS_INVALID_PARAMETER;
3738c2ecf20Sopenharmony_ci	}
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	while (len-- != 0)
3768c2ecf20Sopenharmony_ci		if (mapping[off++] >= vif->num_queues)
3778c2ecf20Sopenharmony_ci			return XEN_NETIF_CTRL_STATUS_INVALID_PARAMETER;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	vif->hash.mapping_sel = !vif->hash.mapping_sel;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	return XEN_NETIF_CTRL_STATUS_SUCCESS;
3828c2ecf20Sopenharmony_ci}
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS
3858c2ecf20Sopenharmony_civoid xenvif_dump_hash_info(struct xenvif *vif, struct seq_file *m)
3868c2ecf20Sopenharmony_ci{
3878c2ecf20Sopenharmony_ci	unsigned int i;
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	switch (vif->hash.alg) {
3908c2ecf20Sopenharmony_ci	case XEN_NETIF_CTRL_HASH_ALGORITHM_TOEPLITZ:
3918c2ecf20Sopenharmony_ci		seq_puts(m, "Hash Algorithm: TOEPLITZ\n");
3928c2ecf20Sopenharmony_ci		break;
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	case XEN_NETIF_CTRL_HASH_ALGORITHM_NONE:
3958c2ecf20Sopenharmony_ci		seq_puts(m, "Hash Algorithm: NONE\n");
3968c2ecf20Sopenharmony_ci		fallthrough;
3978c2ecf20Sopenharmony_ci	default:
3988c2ecf20Sopenharmony_ci		return;
3998c2ecf20Sopenharmony_ci	}
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	if (vif->hash.flags) {
4028c2ecf20Sopenharmony_ci		seq_puts(m, "\nHash Flags:\n");
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci		if (vif->hash.flags & XEN_NETIF_CTRL_HASH_TYPE_IPV4)
4058c2ecf20Sopenharmony_ci			seq_puts(m, "- IPv4\n");
4068c2ecf20Sopenharmony_ci		if (vif->hash.flags & XEN_NETIF_CTRL_HASH_TYPE_IPV4_TCP)
4078c2ecf20Sopenharmony_ci			seq_puts(m, "- IPv4 + TCP\n");
4088c2ecf20Sopenharmony_ci		if (vif->hash.flags & XEN_NETIF_CTRL_HASH_TYPE_IPV6)
4098c2ecf20Sopenharmony_ci			seq_puts(m, "- IPv6\n");
4108c2ecf20Sopenharmony_ci		if (vif->hash.flags & XEN_NETIF_CTRL_HASH_TYPE_IPV6_TCP)
4118c2ecf20Sopenharmony_ci			seq_puts(m, "- IPv6 + TCP\n");
4128c2ecf20Sopenharmony_ci	}
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	seq_puts(m, "\nHash Key:\n");
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	for (i = 0; i < XEN_NETBK_MAX_HASH_KEY_SIZE; ) {
4178c2ecf20Sopenharmony_ci		unsigned int j, n;
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci		n = 8;
4208c2ecf20Sopenharmony_ci		if (i + n >= XEN_NETBK_MAX_HASH_KEY_SIZE)
4218c2ecf20Sopenharmony_ci			n = XEN_NETBK_MAX_HASH_KEY_SIZE - i;
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci		seq_printf(m, "[%2u - %2u]: ", i, i + n - 1);
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci		for (j = 0; j < n; j++, i++)
4268c2ecf20Sopenharmony_ci			seq_printf(m, "%02x ", vif->hash.key[i]);
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci		seq_puts(m, "\n");
4298c2ecf20Sopenharmony_ci	}
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	if (vif->hash.size != 0) {
4328c2ecf20Sopenharmony_ci		const u32 *mapping = vif->hash.mapping[vif->hash.mapping_sel];
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci		seq_puts(m, "\nHash Mapping:\n");
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci		for (i = 0; i < vif->hash.size; ) {
4378c2ecf20Sopenharmony_ci			unsigned int j, n;
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci			n = 8;
4408c2ecf20Sopenharmony_ci			if (i + n >= vif->hash.size)
4418c2ecf20Sopenharmony_ci				n = vif->hash.size - i;
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci			seq_printf(m, "[%4u - %4u]: ", i, i + n - 1);
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci			for (j = 0; j < n; j++, i++)
4468c2ecf20Sopenharmony_ci				seq_printf(m, "%4u ", mapping[i]);
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci			seq_puts(m, "\n");
4498c2ecf20Sopenharmony_ci		}
4508c2ecf20Sopenharmony_ci	}
4518c2ecf20Sopenharmony_ci}
4528c2ecf20Sopenharmony_ci#endif /* CONFIG_DEBUG_FS */
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_civoid xenvif_init_hash(struct xenvif *vif)
4558c2ecf20Sopenharmony_ci{
4568c2ecf20Sopenharmony_ci	if (xenvif_hash_cache_size == 0)
4578c2ecf20Sopenharmony_ci		return;
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	BUG_ON(vif->hash.cache.count);
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	spin_lock_init(&vif->hash.cache.lock);
4628c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&vif->hash.cache.list);
4638c2ecf20Sopenharmony_ci}
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_civoid xenvif_deinit_hash(struct xenvif *vif)
4668c2ecf20Sopenharmony_ci{
4678c2ecf20Sopenharmony_ci	xenvif_flush_hash(vif);
4688c2ecf20Sopenharmony_ci}
469