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