18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci *  This file is part of the Chelsio T4 Ethernet driver for Linux.
38c2ecf20Sopenharmony_ci *  Copyright (C) 2003-2014 Chelsio Communications.  All rights reserved.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Written by Deepak (deepak.s@chelsio.com)
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci *  This program is distributed in the hope that it will be useful, but WITHOUT
88c2ecf20Sopenharmony_ci *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
98c2ecf20Sopenharmony_ci *  FITNESS FOR A PARTICULAR PURPOSE.  See the LICENSE file included in this
108c2ecf20Sopenharmony_ci *  release for licensing terms and conditions.
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <linux/module.h>
148c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
158c2ecf20Sopenharmony_ci#include <linux/jhash.h>
168c2ecf20Sopenharmony_ci#include <linux/if_vlan.h>
178c2ecf20Sopenharmony_ci#include <net/addrconf.h>
188c2ecf20Sopenharmony_ci#include "cxgb4.h"
198c2ecf20Sopenharmony_ci#include "clip_tbl.h"
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistatic inline unsigned int ipv4_clip_hash(struct clip_tbl *c, const u32 *key)
228c2ecf20Sopenharmony_ci{
238c2ecf20Sopenharmony_ci	unsigned int clipt_size_half = c->clipt_size / 2;
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci	return jhash_1word(*key, 0) % clipt_size_half;
268c2ecf20Sopenharmony_ci}
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cistatic inline unsigned int ipv6_clip_hash(struct clip_tbl *d, const u32 *key)
298c2ecf20Sopenharmony_ci{
308c2ecf20Sopenharmony_ci	unsigned int clipt_size_half = d->clipt_size / 2;
318c2ecf20Sopenharmony_ci	u32 xor = key[0] ^ key[1] ^ key[2] ^ key[3];
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	return clipt_size_half +
348c2ecf20Sopenharmony_ci		(jhash_1word(xor, 0) % clipt_size_half);
358c2ecf20Sopenharmony_ci}
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic unsigned int clip_addr_hash(struct clip_tbl *ctbl, const u32 *addr,
388c2ecf20Sopenharmony_ci				   u8 v6)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	return v6 ? ipv6_clip_hash(ctbl, addr) :
418c2ecf20Sopenharmony_ci			ipv4_clip_hash(ctbl, addr);
428c2ecf20Sopenharmony_ci}
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistatic int clip6_get_mbox(const struct net_device *dev,
458c2ecf20Sopenharmony_ci			  const struct in6_addr *lip)
468c2ecf20Sopenharmony_ci{
478c2ecf20Sopenharmony_ci	struct adapter *adap = netdev2adap(dev);
488c2ecf20Sopenharmony_ci	struct fw_clip_cmd c;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	memset(&c, 0, sizeof(c));
518c2ecf20Sopenharmony_ci	c.op_to_write = htonl(FW_CMD_OP_V(FW_CLIP_CMD) |
528c2ecf20Sopenharmony_ci			      FW_CMD_REQUEST_F | FW_CMD_WRITE_F);
538c2ecf20Sopenharmony_ci	c.alloc_to_len16 = htonl(FW_CLIP_CMD_ALLOC_F | FW_LEN16(c));
548c2ecf20Sopenharmony_ci	*(__be64 *)&c.ip_hi = *(__be64 *)(lip->s6_addr);
558c2ecf20Sopenharmony_ci	*(__be64 *)&c.ip_lo = *(__be64 *)(lip->s6_addr + 8);
568c2ecf20Sopenharmony_ci	return t4_wr_mbox_meat(adap, adap->mbox, &c, sizeof(c), &c, false);
578c2ecf20Sopenharmony_ci}
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistatic int clip6_release_mbox(const struct net_device *dev,
608c2ecf20Sopenharmony_ci			      const struct in6_addr *lip)
618c2ecf20Sopenharmony_ci{
628c2ecf20Sopenharmony_ci	struct adapter *adap = netdev2adap(dev);
638c2ecf20Sopenharmony_ci	struct fw_clip_cmd c;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	memset(&c, 0, sizeof(c));
668c2ecf20Sopenharmony_ci	c.op_to_write = htonl(FW_CMD_OP_V(FW_CLIP_CMD) |
678c2ecf20Sopenharmony_ci			      FW_CMD_REQUEST_F | FW_CMD_READ_F);
688c2ecf20Sopenharmony_ci	c.alloc_to_len16 = htonl(FW_CLIP_CMD_FREE_F | FW_LEN16(c));
698c2ecf20Sopenharmony_ci	*(__be64 *)&c.ip_hi = *(__be64 *)(lip->s6_addr);
708c2ecf20Sopenharmony_ci	*(__be64 *)&c.ip_lo = *(__be64 *)(lip->s6_addr + 8);
718c2ecf20Sopenharmony_ci	return t4_wr_mbox_meat(adap, adap->mbox, &c, sizeof(c), &c, false);
728c2ecf20Sopenharmony_ci}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ciint cxgb4_clip_get(const struct net_device *dev, const u32 *lip, u8 v6)
758c2ecf20Sopenharmony_ci{
768c2ecf20Sopenharmony_ci	struct adapter *adap = netdev2adap(dev);
778c2ecf20Sopenharmony_ci	struct clip_tbl *ctbl = adap->clipt;
788c2ecf20Sopenharmony_ci	struct clip_entry *ce, *cte;
798c2ecf20Sopenharmony_ci	u32 *addr = (u32 *)lip;
808c2ecf20Sopenharmony_ci	int hash;
818c2ecf20Sopenharmony_ci	int ret = -1;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	if (!ctbl)
848c2ecf20Sopenharmony_ci		return 0;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	hash = clip_addr_hash(ctbl, addr, v6);
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	read_lock_bh(&ctbl->lock);
898c2ecf20Sopenharmony_ci	list_for_each_entry(cte, &ctbl->hash_list[hash], list) {
908c2ecf20Sopenharmony_ci		if (cte->addr6.sin6_family == AF_INET6 && v6)
918c2ecf20Sopenharmony_ci			ret = memcmp(lip, cte->addr6.sin6_addr.s6_addr,
928c2ecf20Sopenharmony_ci				     sizeof(struct in6_addr));
938c2ecf20Sopenharmony_ci		else if (cte->addr.sin_family == AF_INET && !v6)
948c2ecf20Sopenharmony_ci			ret = memcmp(lip, (char *)(&cte->addr.sin_addr),
958c2ecf20Sopenharmony_ci				     sizeof(struct in_addr));
968c2ecf20Sopenharmony_ci		if (!ret) {
978c2ecf20Sopenharmony_ci			ce = cte;
988c2ecf20Sopenharmony_ci			read_unlock_bh(&ctbl->lock);
998c2ecf20Sopenharmony_ci			refcount_inc(&ce->refcnt);
1008c2ecf20Sopenharmony_ci			return 0;
1018c2ecf20Sopenharmony_ci		}
1028c2ecf20Sopenharmony_ci	}
1038c2ecf20Sopenharmony_ci	read_unlock_bh(&ctbl->lock);
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	write_lock_bh(&ctbl->lock);
1068c2ecf20Sopenharmony_ci	if (!list_empty(&ctbl->ce_free_head)) {
1078c2ecf20Sopenharmony_ci		ce = list_first_entry(&ctbl->ce_free_head,
1088c2ecf20Sopenharmony_ci				      struct clip_entry, list);
1098c2ecf20Sopenharmony_ci		list_del(&ce->list);
1108c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&ce->list);
1118c2ecf20Sopenharmony_ci		spin_lock_init(&ce->lock);
1128c2ecf20Sopenharmony_ci		refcount_set(&ce->refcnt, 0);
1138c2ecf20Sopenharmony_ci		atomic_dec(&ctbl->nfree);
1148c2ecf20Sopenharmony_ci		list_add_tail(&ce->list, &ctbl->hash_list[hash]);
1158c2ecf20Sopenharmony_ci		if (v6) {
1168c2ecf20Sopenharmony_ci			ce->addr6.sin6_family = AF_INET6;
1178c2ecf20Sopenharmony_ci			memcpy(ce->addr6.sin6_addr.s6_addr,
1188c2ecf20Sopenharmony_ci			       lip, sizeof(struct in6_addr));
1198c2ecf20Sopenharmony_ci			ret = clip6_get_mbox(dev, (const struct in6_addr *)lip);
1208c2ecf20Sopenharmony_ci			if (ret) {
1218c2ecf20Sopenharmony_ci				write_unlock_bh(&ctbl->lock);
1228c2ecf20Sopenharmony_ci				dev_err(adap->pdev_dev,
1238c2ecf20Sopenharmony_ci					"CLIP FW cmd failed with error %d, "
1248c2ecf20Sopenharmony_ci					"Connections using %pI6c wont be "
1258c2ecf20Sopenharmony_ci					"offloaded",
1268c2ecf20Sopenharmony_ci					ret, ce->addr6.sin6_addr.s6_addr);
1278c2ecf20Sopenharmony_ci				return ret;
1288c2ecf20Sopenharmony_ci			}
1298c2ecf20Sopenharmony_ci		} else {
1308c2ecf20Sopenharmony_ci			ce->addr.sin_family = AF_INET;
1318c2ecf20Sopenharmony_ci			memcpy((char *)(&ce->addr.sin_addr), lip,
1328c2ecf20Sopenharmony_ci			       sizeof(struct in_addr));
1338c2ecf20Sopenharmony_ci		}
1348c2ecf20Sopenharmony_ci	} else {
1358c2ecf20Sopenharmony_ci		write_unlock_bh(&ctbl->lock);
1368c2ecf20Sopenharmony_ci		dev_info(adap->pdev_dev, "CLIP table overflow, "
1378c2ecf20Sopenharmony_ci			 "Connections using %pI6c wont be offloaded",
1388c2ecf20Sopenharmony_ci			 (void *)lip);
1398c2ecf20Sopenharmony_ci		return -ENOMEM;
1408c2ecf20Sopenharmony_ci	}
1418c2ecf20Sopenharmony_ci	write_unlock_bh(&ctbl->lock);
1428c2ecf20Sopenharmony_ci	refcount_set(&ce->refcnt, 1);
1438c2ecf20Sopenharmony_ci	return 0;
1448c2ecf20Sopenharmony_ci}
1458c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cxgb4_clip_get);
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_civoid cxgb4_clip_release(const struct net_device *dev, const u32 *lip, u8 v6)
1488c2ecf20Sopenharmony_ci{
1498c2ecf20Sopenharmony_ci	struct adapter *adap = netdev2adap(dev);
1508c2ecf20Sopenharmony_ci	struct clip_tbl *ctbl = adap->clipt;
1518c2ecf20Sopenharmony_ci	struct clip_entry *ce, *cte;
1528c2ecf20Sopenharmony_ci	u32 *addr = (u32 *)lip;
1538c2ecf20Sopenharmony_ci	int hash;
1548c2ecf20Sopenharmony_ci	int ret = -1;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	if (!ctbl)
1578c2ecf20Sopenharmony_ci		return;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	hash = clip_addr_hash(ctbl, addr, v6);
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	read_lock_bh(&ctbl->lock);
1628c2ecf20Sopenharmony_ci	list_for_each_entry(cte, &ctbl->hash_list[hash], list) {
1638c2ecf20Sopenharmony_ci		if (cte->addr6.sin6_family == AF_INET6 && v6)
1648c2ecf20Sopenharmony_ci			ret = memcmp(lip, cte->addr6.sin6_addr.s6_addr,
1658c2ecf20Sopenharmony_ci				     sizeof(struct in6_addr));
1668c2ecf20Sopenharmony_ci		else if (cte->addr.sin_family == AF_INET && !v6)
1678c2ecf20Sopenharmony_ci			ret = memcmp(lip, (char *)(&cte->addr.sin_addr),
1688c2ecf20Sopenharmony_ci				     sizeof(struct in_addr));
1698c2ecf20Sopenharmony_ci		if (!ret) {
1708c2ecf20Sopenharmony_ci			ce = cte;
1718c2ecf20Sopenharmony_ci			read_unlock_bh(&ctbl->lock);
1728c2ecf20Sopenharmony_ci			goto found;
1738c2ecf20Sopenharmony_ci		}
1748c2ecf20Sopenharmony_ci	}
1758c2ecf20Sopenharmony_ci	read_unlock_bh(&ctbl->lock);
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	return;
1788c2ecf20Sopenharmony_cifound:
1798c2ecf20Sopenharmony_ci	write_lock_bh(&ctbl->lock);
1808c2ecf20Sopenharmony_ci	spin_lock_bh(&ce->lock);
1818c2ecf20Sopenharmony_ci	if (refcount_dec_and_test(&ce->refcnt)) {
1828c2ecf20Sopenharmony_ci		list_del(&ce->list);
1838c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&ce->list);
1848c2ecf20Sopenharmony_ci		list_add_tail(&ce->list, &ctbl->ce_free_head);
1858c2ecf20Sopenharmony_ci		atomic_inc(&ctbl->nfree);
1868c2ecf20Sopenharmony_ci		if (v6)
1878c2ecf20Sopenharmony_ci			clip6_release_mbox(dev, (const struct in6_addr *)lip);
1888c2ecf20Sopenharmony_ci	}
1898c2ecf20Sopenharmony_ci	spin_unlock_bh(&ce->lock);
1908c2ecf20Sopenharmony_ci	write_unlock_bh(&ctbl->lock);
1918c2ecf20Sopenharmony_ci}
1928c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cxgb4_clip_release);
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci/* Retrieves IPv6 addresses from a root device (bond, vlan) associated with
1958c2ecf20Sopenharmony_ci * a physical device.
1968c2ecf20Sopenharmony_ci * The physical device reference is needed to send the actul CLIP command.
1978c2ecf20Sopenharmony_ci */
1988c2ecf20Sopenharmony_cistatic int cxgb4_update_dev_clip(struct net_device *root_dev,
1998c2ecf20Sopenharmony_ci				 struct net_device *dev)
2008c2ecf20Sopenharmony_ci{
2018c2ecf20Sopenharmony_ci	struct inet6_dev *idev = NULL;
2028c2ecf20Sopenharmony_ci	struct inet6_ifaddr *ifa;
2038c2ecf20Sopenharmony_ci	int ret = 0;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	idev = __in6_dev_get(root_dev);
2068c2ecf20Sopenharmony_ci	if (!idev)
2078c2ecf20Sopenharmony_ci		return ret;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	read_lock_bh(&idev->lock);
2108c2ecf20Sopenharmony_ci	list_for_each_entry(ifa, &idev->addr_list, if_list) {
2118c2ecf20Sopenharmony_ci		ret = cxgb4_clip_get(dev, (const u32 *)ifa->addr.s6_addr, 1);
2128c2ecf20Sopenharmony_ci		if (ret < 0)
2138c2ecf20Sopenharmony_ci			break;
2148c2ecf20Sopenharmony_ci	}
2158c2ecf20Sopenharmony_ci	read_unlock_bh(&idev->lock);
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	return ret;
2188c2ecf20Sopenharmony_ci}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ciint cxgb4_update_root_dev_clip(struct net_device *dev)
2218c2ecf20Sopenharmony_ci{
2228c2ecf20Sopenharmony_ci	struct net_device *root_dev = NULL;
2238c2ecf20Sopenharmony_ci	int i, ret = 0;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	/* First populate the real net device's IPv6 addresses */
2268c2ecf20Sopenharmony_ci	ret = cxgb4_update_dev_clip(dev, dev);
2278c2ecf20Sopenharmony_ci	if (ret)
2288c2ecf20Sopenharmony_ci		return ret;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	/* Parse all bond and vlan devices layered on top of the physical dev */
2318c2ecf20Sopenharmony_ci	root_dev = netdev_master_upper_dev_get_rcu(dev);
2328c2ecf20Sopenharmony_ci	if (root_dev) {
2338c2ecf20Sopenharmony_ci		ret = cxgb4_update_dev_clip(root_dev, dev);
2348c2ecf20Sopenharmony_ci		if (ret)
2358c2ecf20Sopenharmony_ci			return ret;
2368c2ecf20Sopenharmony_ci	}
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	for (i = 0; i < VLAN_N_VID; i++) {
2398c2ecf20Sopenharmony_ci		root_dev = __vlan_find_dev_deep_rcu(dev, htons(ETH_P_8021Q), i);
2408c2ecf20Sopenharmony_ci		if (!root_dev)
2418c2ecf20Sopenharmony_ci			continue;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci		ret = cxgb4_update_dev_clip(root_dev, dev);
2448c2ecf20Sopenharmony_ci		if (ret)
2458c2ecf20Sopenharmony_ci			break;
2468c2ecf20Sopenharmony_ci	}
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	return ret;
2498c2ecf20Sopenharmony_ci}
2508c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cxgb4_update_root_dev_clip);
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ciint clip_tbl_show(struct seq_file *seq, void *v)
2538c2ecf20Sopenharmony_ci{
2548c2ecf20Sopenharmony_ci	struct adapter *adapter = seq->private;
2558c2ecf20Sopenharmony_ci	struct clip_tbl *ctbl = adapter->clipt;
2568c2ecf20Sopenharmony_ci	struct clip_entry *ce;
2578c2ecf20Sopenharmony_ci	char ip[60];
2588c2ecf20Sopenharmony_ci	int i;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	read_lock_bh(&ctbl->lock);
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	seq_puts(seq, "IP Address                  Users\n");
2638c2ecf20Sopenharmony_ci	for (i = 0 ; i < ctbl->clipt_size;  ++i) {
2648c2ecf20Sopenharmony_ci		list_for_each_entry(ce, &ctbl->hash_list[i], list) {
2658c2ecf20Sopenharmony_ci			ip[0] = '\0';
2668c2ecf20Sopenharmony_ci			sprintf(ip, "%pISc", &ce->addr);
2678c2ecf20Sopenharmony_ci			seq_printf(seq, "%-25s   %u\n", ip,
2688c2ecf20Sopenharmony_ci				   refcount_read(&ce->refcnt));
2698c2ecf20Sopenharmony_ci		}
2708c2ecf20Sopenharmony_ci	}
2718c2ecf20Sopenharmony_ci	seq_printf(seq, "Free clip entries : %d\n", atomic_read(&ctbl->nfree));
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	read_unlock_bh(&ctbl->lock);
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	return 0;
2768c2ecf20Sopenharmony_ci}
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_cistruct clip_tbl *t4_init_clip_tbl(unsigned int clipt_start,
2798c2ecf20Sopenharmony_ci				  unsigned int clipt_end)
2808c2ecf20Sopenharmony_ci{
2818c2ecf20Sopenharmony_ci	struct clip_entry *cl_list;
2828c2ecf20Sopenharmony_ci	struct clip_tbl *ctbl;
2838c2ecf20Sopenharmony_ci	unsigned int clipt_size;
2848c2ecf20Sopenharmony_ci	int i;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	if (clipt_start >= clipt_end)
2878c2ecf20Sopenharmony_ci		return NULL;
2888c2ecf20Sopenharmony_ci	clipt_size = clipt_end - clipt_start + 1;
2898c2ecf20Sopenharmony_ci	if (clipt_size < CLIPT_MIN_HASH_BUCKETS)
2908c2ecf20Sopenharmony_ci		return NULL;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	ctbl = kvzalloc(struct_size(ctbl, hash_list, clipt_size), GFP_KERNEL);
2938c2ecf20Sopenharmony_ci	if (!ctbl)
2948c2ecf20Sopenharmony_ci		return NULL;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	ctbl->clipt_start = clipt_start;
2978c2ecf20Sopenharmony_ci	ctbl->clipt_size = clipt_size;
2988c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&ctbl->ce_free_head);
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	atomic_set(&ctbl->nfree, clipt_size);
3018c2ecf20Sopenharmony_ci	rwlock_init(&ctbl->lock);
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	for (i = 0; i < ctbl->clipt_size; ++i)
3048c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&ctbl->hash_list[i]);
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	cl_list = kvcalloc(clipt_size, sizeof(struct clip_entry), GFP_KERNEL);
3078c2ecf20Sopenharmony_ci	if (!cl_list) {
3088c2ecf20Sopenharmony_ci		kvfree(ctbl);
3098c2ecf20Sopenharmony_ci		return NULL;
3108c2ecf20Sopenharmony_ci	}
3118c2ecf20Sopenharmony_ci	ctbl->cl_list = (void *)cl_list;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	for (i = 0; i < clipt_size; i++) {
3148c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&cl_list[i].list);
3158c2ecf20Sopenharmony_ci		list_add_tail(&cl_list[i].list, &ctbl->ce_free_head);
3168c2ecf20Sopenharmony_ci	}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	return ctbl;
3198c2ecf20Sopenharmony_ci}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_civoid t4_cleanup_clip_tbl(struct adapter *adap)
3228c2ecf20Sopenharmony_ci{
3238c2ecf20Sopenharmony_ci	struct clip_tbl *ctbl = adap->clipt;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	if (ctbl) {
3268c2ecf20Sopenharmony_ci		if (ctbl->cl_list)
3278c2ecf20Sopenharmony_ci			kvfree(ctbl->cl_list);
3288c2ecf20Sopenharmony_ci		kvfree(ctbl);
3298c2ecf20Sopenharmony_ci	}
3308c2ecf20Sopenharmony_ci}
3318c2ecf20Sopenharmony_ciEXPORT_SYMBOL(t4_cleanup_clip_tbl);
332