1b5a02f50SAnish Bhatt /* 2b5a02f50SAnish Bhatt * This file is part of the Chelsio T4 Ethernet driver for Linux. 3b5a02f50SAnish Bhatt * Copyright (C) 2003-2014 Chelsio Communications. All rights reserved. 4b5a02f50SAnish Bhatt * 5b5a02f50SAnish Bhatt * Written by Deepak (deepak.s@chelsio.com) 6b5a02f50SAnish Bhatt * 7b5a02f50SAnish Bhatt * This program is distributed in the hope that it will be useful, but WITHOUT 8b5a02f50SAnish Bhatt * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 9b5a02f50SAnish Bhatt * FITNESS FOR A PARTICULAR PURPOSE. See the LICENSE file included in this 10b5a02f50SAnish Bhatt * release for licensing terms and conditions. 11b5a02f50SAnish Bhatt */ 12b5a02f50SAnish Bhatt 13b5a02f50SAnish Bhatt #include <linux/module.h> 14b5a02f50SAnish Bhatt #include <linux/netdevice.h> 15b5a02f50SAnish Bhatt #include <linux/jhash.h> 16b5a02f50SAnish Bhatt #include <linux/if_vlan.h> 17b5a02f50SAnish Bhatt #include <net/addrconf.h> 18b5a02f50SAnish Bhatt #include "cxgb4.h" 19b5a02f50SAnish Bhatt #include "clip_tbl.h" 20b5a02f50SAnish Bhatt 21b5a02f50SAnish Bhatt static inline unsigned int ipv4_clip_hash(struct clip_tbl *c, const u32 *key) 22b5a02f50SAnish Bhatt { 23b5a02f50SAnish Bhatt unsigned int clipt_size_half = c->clipt_size / 2; 24b5a02f50SAnish Bhatt 25b5a02f50SAnish Bhatt return jhash_1word(*key, 0) % clipt_size_half; 26b5a02f50SAnish Bhatt } 27b5a02f50SAnish Bhatt 28b5a02f50SAnish Bhatt static inline unsigned int ipv6_clip_hash(struct clip_tbl *d, const u32 *key) 29b5a02f50SAnish Bhatt { 30b5a02f50SAnish Bhatt unsigned int clipt_size_half = d->clipt_size / 2; 31b5a02f50SAnish Bhatt u32 xor = key[0] ^ key[1] ^ key[2] ^ key[3]; 32b5a02f50SAnish Bhatt 33b5a02f50SAnish Bhatt return clipt_size_half + 34b5a02f50SAnish Bhatt (jhash_1word(xor, 0) % clipt_size_half); 35b5a02f50SAnish Bhatt } 36b5a02f50SAnish Bhatt 37b5a02f50SAnish Bhatt static unsigned int clip_addr_hash(struct clip_tbl *ctbl, const u32 *addr, 385a8eeec4SAnish Bhatt u8 v6) 39b5a02f50SAnish Bhatt { 405a8eeec4SAnish Bhatt return v6 ? ipv6_clip_hash(ctbl, addr) : 415a8eeec4SAnish Bhatt ipv4_clip_hash(ctbl, addr); 42b5a02f50SAnish Bhatt } 43b5a02f50SAnish Bhatt 44b5a02f50SAnish Bhatt static int clip6_get_mbox(const struct net_device *dev, 45b5a02f50SAnish Bhatt const struct in6_addr *lip) 46b5a02f50SAnish Bhatt { 47b5a02f50SAnish Bhatt struct adapter *adap = netdev2adap(dev); 48b5a02f50SAnish Bhatt struct fw_clip_cmd c; 49b5a02f50SAnish Bhatt 50b5a02f50SAnish Bhatt memset(&c, 0, sizeof(c)); 51b5a02f50SAnish Bhatt c.op_to_write = htonl(FW_CMD_OP_V(FW_CLIP_CMD) | 52b5a02f50SAnish Bhatt FW_CMD_REQUEST_F | FW_CMD_WRITE_F); 53b5a02f50SAnish Bhatt c.alloc_to_len16 = htonl(FW_CLIP_CMD_ALLOC_F | FW_LEN16(c)); 54b5a02f50SAnish Bhatt *(__be64 *)&c.ip_hi = *(__be64 *)(lip->s6_addr); 55b5a02f50SAnish Bhatt *(__be64 *)&c.ip_lo = *(__be64 *)(lip->s6_addr + 8); 56b5a02f50SAnish Bhatt return t4_wr_mbox_meat(adap, adap->mbox, &c, sizeof(c), &c, false); 57b5a02f50SAnish Bhatt } 58b5a02f50SAnish Bhatt 59b5a02f50SAnish Bhatt static int clip6_release_mbox(const struct net_device *dev, 60b5a02f50SAnish Bhatt const struct in6_addr *lip) 61b5a02f50SAnish Bhatt { 62b5a02f50SAnish Bhatt struct adapter *adap = netdev2adap(dev); 63b5a02f50SAnish Bhatt struct fw_clip_cmd c; 64b5a02f50SAnish Bhatt 65b5a02f50SAnish Bhatt memset(&c, 0, sizeof(c)); 66b5a02f50SAnish Bhatt c.op_to_write = htonl(FW_CMD_OP_V(FW_CLIP_CMD) | 67b5a02f50SAnish Bhatt FW_CMD_REQUEST_F | FW_CMD_READ_F); 68b5a02f50SAnish Bhatt c.alloc_to_len16 = htonl(FW_CLIP_CMD_FREE_F | FW_LEN16(c)); 69b5a02f50SAnish Bhatt *(__be64 *)&c.ip_hi = *(__be64 *)(lip->s6_addr); 70b5a02f50SAnish Bhatt *(__be64 *)&c.ip_lo = *(__be64 *)(lip->s6_addr + 8); 71b5a02f50SAnish Bhatt return t4_wr_mbox_meat(adap, adap->mbox, &c, sizeof(c), &c, false); 72b5a02f50SAnish Bhatt } 73b5a02f50SAnish Bhatt 74b5a02f50SAnish Bhatt int cxgb4_clip_get(const struct net_device *dev, const u32 *lip, u8 v6) 75b5a02f50SAnish Bhatt { 76b5a02f50SAnish Bhatt struct adapter *adap = netdev2adap(dev); 77b5a02f50SAnish Bhatt struct clip_tbl *ctbl = adap->clipt; 78b5a02f50SAnish Bhatt struct clip_entry *ce, *cte; 79b5a02f50SAnish Bhatt u32 *addr = (u32 *)lip; 80b5a02f50SAnish Bhatt int hash; 815a8eeec4SAnish Bhatt int ret = -1; 82b5a02f50SAnish Bhatt 83acde2c2dSHariprasad Shenai if (!ctbl) 84acde2c2dSHariprasad Shenai return 0; 85acde2c2dSHariprasad Shenai 865a8eeec4SAnish Bhatt hash = clip_addr_hash(ctbl, addr, v6); 87b5a02f50SAnish Bhatt 88b5a02f50SAnish Bhatt read_lock_bh(&ctbl->lock); 89b5a02f50SAnish Bhatt list_for_each_entry(cte, &ctbl->hash_list[hash], list) { 905a8eeec4SAnish Bhatt if (cte->addr6.sin6_family == AF_INET6 && v6) 915a8eeec4SAnish Bhatt ret = memcmp(lip, cte->addr6.sin6_addr.s6_addr, 925a8eeec4SAnish Bhatt sizeof(struct in6_addr)); 935a8eeec4SAnish Bhatt else if (cte->addr.sin_family == AF_INET && !v6) 945a8eeec4SAnish Bhatt ret = memcmp(lip, (char *)(&cte->addr.sin_addr), 955a8eeec4SAnish Bhatt sizeof(struct in_addr)); 965a8eeec4SAnish Bhatt if (!ret) { 97b5a02f50SAnish Bhatt ce = cte; 98b5a02f50SAnish Bhatt read_unlock_bh(&ctbl->lock); 99*eaf6ab76SElena Reshetova refcount_inc(&ce->refcnt); 100*eaf6ab76SElena Reshetova return 0; 101b5a02f50SAnish Bhatt } 102b5a02f50SAnish Bhatt } 103b5a02f50SAnish Bhatt read_unlock_bh(&ctbl->lock); 104b5a02f50SAnish Bhatt 105b5a02f50SAnish Bhatt write_lock_bh(&ctbl->lock); 106b5a02f50SAnish Bhatt if (!list_empty(&ctbl->ce_free_head)) { 107b5a02f50SAnish Bhatt ce = list_first_entry(&ctbl->ce_free_head, 108b5a02f50SAnish Bhatt struct clip_entry, list); 109b5a02f50SAnish Bhatt list_del(&ce->list); 110b5a02f50SAnish Bhatt INIT_LIST_HEAD(&ce->list); 111b5a02f50SAnish Bhatt spin_lock_init(&ce->lock); 112*eaf6ab76SElena Reshetova refcount_set(&ce->refcnt, 0); 113b5a02f50SAnish Bhatt atomic_dec(&ctbl->nfree); 114b5a02f50SAnish Bhatt list_add_tail(&ce->list, &ctbl->hash_list[hash]); 115b5a02f50SAnish Bhatt if (v6) { 1165a8eeec4SAnish Bhatt ce->addr6.sin6_family = AF_INET6; 1175a8eeec4SAnish Bhatt memcpy(ce->addr6.sin6_addr.s6_addr, 1185a8eeec4SAnish Bhatt lip, sizeof(struct in6_addr)); 119b5a02f50SAnish Bhatt ret = clip6_get_mbox(dev, (const struct in6_addr *)lip); 120b5a02f50SAnish Bhatt if (ret) { 121b5a02f50SAnish Bhatt write_unlock_bh(&ctbl->lock); 122eb72f74fSHariprasad Shenai dev_err(adap->pdev_dev, 123eb72f74fSHariprasad Shenai "CLIP FW cmd failed with error %d, " 124eb72f74fSHariprasad Shenai "Connections using %pI6c wont be " 125eb72f74fSHariprasad Shenai "offloaded", 126eb72f74fSHariprasad Shenai ret, ce->addr6.sin6_addr.s6_addr); 127b5a02f50SAnish Bhatt return ret; 128b5a02f50SAnish Bhatt } 1295a8eeec4SAnish Bhatt } else { 1305a8eeec4SAnish Bhatt ce->addr.sin_family = AF_INET; 1315a8eeec4SAnish Bhatt memcpy((char *)(&ce->addr.sin_addr), lip, 1325a8eeec4SAnish Bhatt sizeof(struct in_addr)); 133b5a02f50SAnish Bhatt } 134b5a02f50SAnish Bhatt } else { 135b5a02f50SAnish Bhatt write_unlock_bh(&ctbl->lock); 136eb72f74fSHariprasad Shenai dev_info(adap->pdev_dev, "CLIP table overflow, " 137eb72f74fSHariprasad Shenai "Connections using %pI6c wont be offloaded", 138eb72f74fSHariprasad Shenai (void *)lip); 139b5a02f50SAnish Bhatt return -ENOMEM; 140b5a02f50SAnish Bhatt } 141b5a02f50SAnish Bhatt write_unlock_bh(&ctbl->lock); 142*eaf6ab76SElena Reshetova refcount_set(&ce->refcnt, 1); 143b5a02f50SAnish Bhatt return 0; 144b5a02f50SAnish Bhatt } 145b5a02f50SAnish Bhatt EXPORT_SYMBOL(cxgb4_clip_get); 146b5a02f50SAnish Bhatt 147b5a02f50SAnish Bhatt void cxgb4_clip_release(const struct net_device *dev, const u32 *lip, u8 v6) 148b5a02f50SAnish Bhatt { 149b5a02f50SAnish Bhatt struct adapter *adap = netdev2adap(dev); 150b5a02f50SAnish Bhatt struct clip_tbl *ctbl = adap->clipt; 151b5a02f50SAnish Bhatt struct clip_entry *ce, *cte; 152b5a02f50SAnish Bhatt u32 *addr = (u32 *)lip; 153b5a02f50SAnish Bhatt int hash; 1545a8eeec4SAnish Bhatt int ret = -1; 155b5a02f50SAnish Bhatt 156eb72f74fSHariprasad Shenai if (!ctbl) 157eb72f74fSHariprasad Shenai return; 158eb72f74fSHariprasad Shenai 1595a8eeec4SAnish Bhatt hash = clip_addr_hash(ctbl, addr, v6); 160b5a02f50SAnish Bhatt 161b5a02f50SAnish Bhatt read_lock_bh(&ctbl->lock); 162b5a02f50SAnish Bhatt list_for_each_entry(cte, &ctbl->hash_list[hash], list) { 1635a8eeec4SAnish Bhatt if (cte->addr6.sin6_family == AF_INET6 && v6) 1645a8eeec4SAnish Bhatt ret = memcmp(lip, cte->addr6.sin6_addr.s6_addr, 1655a8eeec4SAnish Bhatt sizeof(struct in6_addr)); 1665a8eeec4SAnish Bhatt else if (cte->addr.sin_family == AF_INET && !v6) 1675a8eeec4SAnish Bhatt ret = memcmp(lip, (char *)(&cte->addr.sin_addr), 1685a8eeec4SAnish Bhatt sizeof(struct in_addr)); 1695a8eeec4SAnish Bhatt if (!ret) { 170b5a02f50SAnish Bhatt ce = cte; 171b5a02f50SAnish Bhatt read_unlock_bh(&ctbl->lock); 172b5a02f50SAnish Bhatt goto found; 173b5a02f50SAnish Bhatt } 174b5a02f50SAnish Bhatt } 175b5a02f50SAnish Bhatt read_unlock_bh(&ctbl->lock); 176b5a02f50SAnish Bhatt 177b5a02f50SAnish Bhatt return; 178b5a02f50SAnish Bhatt found: 179b5a02f50SAnish Bhatt write_lock_bh(&ctbl->lock); 180b5a02f50SAnish Bhatt spin_lock_bh(&ce->lock); 181*eaf6ab76SElena Reshetova if (refcount_dec_and_test(&ce->refcnt)) { 182b5a02f50SAnish Bhatt list_del(&ce->list); 183b5a02f50SAnish Bhatt INIT_LIST_HEAD(&ce->list); 184b5a02f50SAnish Bhatt list_add_tail(&ce->list, &ctbl->ce_free_head); 185b5a02f50SAnish Bhatt atomic_inc(&ctbl->nfree); 186b5a02f50SAnish Bhatt if (v6) 187b5a02f50SAnish Bhatt clip6_release_mbox(dev, (const struct in6_addr *)lip); 188b5a02f50SAnish Bhatt } 189b5a02f50SAnish Bhatt spin_unlock_bh(&ce->lock); 190b5a02f50SAnish Bhatt write_unlock_bh(&ctbl->lock); 191b5a02f50SAnish Bhatt } 192b5a02f50SAnish Bhatt EXPORT_SYMBOL(cxgb4_clip_release); 193b5a02f50SAnish Bhatt 194b5a02f50SAnish Bhatt /* Retrieves IPv6 addresses from a root device (bond, vlan) associated with 195b5a02f50SAnish Bhatt * a physical device. 196b5a02f50SAnish Bhatt * The physical device reference is needed to send the actul CLIP command. 197b5a02f50SAnish Bhatt */ 198b5a02f50SAnish Bhatt static int cxgb4_update_dev_clip(struct net_device *root_dev, 199b5a02f50SAnish Bhatt struct net_device *dev) 200b5a02f50SAnish Bhatt { 201b5a02f50SAnish Bhatt struct inet6_dev *idev = NULL; 202b5a02f50SAnish Bhatt struct inet6_ifaddr *ifa; 203b5a02f50SAnish Bhatt int ret = 0; 204b5a02f50SAnish Bhatt 205b5a02f50SAnish Bhatt idev = __in6_dev_get(root_dev); 206b5a02f50SAnish Bhatt if (!idev) 207b5a02f50SAnish Bhatt return ret; 208b5a02f50SAnish Bhatt 209b5a02f50SAnish Bhatt read_lock_bh(&idev->lock); 210b5a02f50SAnish Bhatt list_for_each_entry(ifa, &idev->addr_list, if_list) { 211b5a02f50SAnish Bhatt ret = cxgb4_clip_get(dev, (const u32 *)ifa->addr.s6_addr, 1); 212b5a02f50SAnish Bhatt if (ret < 0) 213b5a02f50SAnish Bhatt break; 214b5a02f50SAnish Bhatt } 215b5a02f50SAnish Bhatt read_unlock_bh(&idev->lock); 216b5a02f50SAnish Bhatt 217b5a02f50SAnish Bhatt return ret; 218b5a02f50SAnish Bhatt } 219b5a02f50SAnish Bhatt 220b5a02f50SAnish Bhatt int cxgb4_update_root_dev_clip(struct net_device *dev) 221b5a02f50SAnish Bhatt { 222b5a02f50SAnish Bhatt struct net_device *root_dev = NULL; 223b5a02f50SAnish Bhatt int i, ret = 0; 224b5a02f50SAnish Bhatt 225b5a02f50SAnish Bhatt /* First populate the real net device's IPv6 addresses */ 226b5a02f50SAnish Bhatt ret = cxgb4_update_dev_clip(dev, dev); 227b5a02f50SAnish Bhatt if (ret) 228b5a02f50SAnish Bhatt return ret; 229b5a02f50SAnish Bhatt 230b5a02f50SAnish Bhatt /* Parse all bond and vlan devices layered on top of the physical dev */ 231b5a02f50SAnish Bhatt root_dev = netdev_master_upper_dev_get_rcu(dev); 232b5a02f50SAnish Bhatt if (root_dev) { 233b5a02f50SAnish Bhatt ret = cxgb4_update_dev_clip(root_dev, dev); 234b5a02f50SAnish Bhatt if (ret) 235b5a02f50SAnish Bhatt return ret; 236b5a02f50SAnish Bhatt } 237b5a02f50SAnish Bhatt 238b5a02f50SAnish Bhatt for (i = 0; i < VLAN_N_VID; i++) { 239b5a02f50SAnish Bhatt root_dev = __vlan_find_dev_deep_rcu(dev, htons(ETH_P_8021Q), i); 240b5a02f50SAnish Bhatt if (!root_dev) 241b5a02f50SAnish Bhatt continue; 242b5a02f50SAnish Bhatt 243b5a02f50SAnish Bhatt ret = cxgb4_update_dev_clip(root_dev, dev); 244b5a02f50SAnish Bhatt if (ret) 245b5a02f50SAnish Bhatt break; 246b5a02f50SAnish Bhatt } 247b5a02f50SAnish Bhatt 248b5a02f50SAnish Bhatt return ret; 249b5a02f50SAnish Bhatt } 250b5a02f50SAnish Bhatt EXPORT_SYMBOL(cxgb4_update_root_dev_clip); 251b5a02f50SAnish Bhatt 252b5a02f50SAnish Bhatt int clip_tbl_show(struct seq_file *seq, void *v) 253b5a02f50SAnish Bhatt { 254b5a02f50SAnish Bhatt struct adapter *adapter = seq->private; 255b5a02f50SAnish Bhatt struct clip_tbl *ctbl = adapter->clipt; 256b5a02f50SAnish Bhatt struct clip_entry *ce; 257b5a02f50SAnish Bhatt char ip[60]; 258b5a02f50SAnish Bhatt int i; 259b5a02f50SAnish Bhatt 260b5a02f50SAnish Bhatt read_lock_bh(&ctbl->lock); 261b5a02f50SAnish Bhatt 262b5a02f50SAnish Bhatt seq_puts(seq, "IP Address Users\n"); 263b5a02f50SAnish Bhatt for (i = 0 ; i < ctbl->clipt_size; ++i) { 264b5a02f50SAnish Bhatt list_for_each_entry(ce, &ctbl->hash_list[i], list) { 265b5a02f50SAnish Bhatt ip[0] = '\0'; 2665a8eeec4SAnish Bhatt sprintf(ip, "%pISc", &ce->addr); 267b5a02f50SAnish Bhatt seq_printf(seq, "%-25s %u\n", ip, 268*eaf6ab76SElena Reshetova refcount_read(&ce->refcnt)); 269b5a02f50SAnish Bhatt } 270b5a02f50SAnish Bhatt } 271b5a02f50SAnish Bhatt seq_printf(seq, "Free clip entries : %d\n", atomic_read(&ctbl->nfree)); 272b5a02f50SAnish Bhatt 273b5a02f50SAnish Bhatt read_unlock_bh(&ctbl->lock); 274b5a02f50SAnish Bhatt 275b5a02f50SAnish Bhatt return 0; 276b5a02f50SAnish Bhatt } 277b5a02f50SAnish Bhatt 278b5a02f50SAnish Bhatt struct clip_tbl *t4_init_clip_tbl(unsigned int clipt_start, 279b5a02f50SAnish Bhatt unsigned int clipt_end) 280b5a02f50SAnish Bhatt { 281b5a02f50SAnish Bhatt struct clip_entry *cl_list; 282b5a02f50SAnish Bhatt struct clip_tbl *ctbl; 283b5a02f50SAnish Bhatt unsigned int clipt_size; 284b5a02f50SAnish Bhatt int i; 285b5a02f50SAnish Bhatt 286b5a02f50SAnish Bhatt if (clipt_start >= clipt_end) 287b5a02f50SAnish Bhatt return NULL; 288b5a02f50SAnish Bhatt clipt_size = clipt_end - clipt_start + 1; 289b5a02f50SAnish Bhatt if (clipt_size < CLIPT_MIN_HASH_BUCKETS) 290b5a02f50SAnish Bhatt return NULL; 291b5a02f50SAnish Bhatt 292752ade68SMichal Hocko ctbl = kvzalloc(sizeof(*ctbl) + 293752ade68SMichal Hocko clipt_size*sizeof(struct list_head), GFP_KERNEL); 294b5a02f50SAnish Bhatt if (!ctbl) 295b5a02f50SAnish Bhatt return NULL; 296b5a02f50SAnish Bhatt 297b5a02f50SAnish Bhatt ctbl->clipt_start = clipt_start; 298b5a02f50SAnish Bhatt ctbl->clipt_size = clipt_size; 299b5a02f50SAnish Bhatt INIT_LIST_HEAD(&ctbl->ce_free_head); 300b5a02f50SAnish Bhatt 301b5a02f50SAnish Bhatt atomic_set(&ctbl->nfree, clipt_size); 302b5a02f50SAnish Bhatt rwlock_init(&ctbl->lock); 303b5a02f50SAnish Bhatt 304b5a02f50SAnish Bhatt for (i = 0; i < ctbl->clipt_size; ++i) 305b5a02f50SAnish Bhatt INIT_LIST_HEAD(&ctbl->hash_list[i]); 306b5a02f50SAnish Bhatt 307752ade68SMichal Hocko cl_list = kvzalloc(clipt_size*sizeof(struct clip_entry), GFP_KERNEL); 3083934aa4cSInsu Yun if (!cl_list) { 309752ade68SMichal Hocko kvfree(ctbl); 3103934aa4cSInsu Yun return NULL; 3113934aa4cSInsu Yun } 312b5a02f50SAnish Bhatt ctbl->cl_list = (void *)cl_list; 313b5a02f50SAnish Bhatt 314b5a02f50SAnish Bhatt for (i = 0; i < clipt_size; i++) { 315b5a02f50SAnish Bhatt INIT_LIST_HEAD(&cl_list[i].list); 316b5a02f50SAnish Bhatt list_add_tail(&cl_list[i].list, &ctbl->ce_free_head); 317b5a02f50SAnish Bhatt } 318b5a02f50SAnish Bhatt 319b5a02f50SAnish Bhatt return ctbl; 320b5a02f50SAnish Bhatt } 321b5a02f50SAnish Bhatt 322b5a02f50SAnish Bhatt void t4_cleanup_clip_tbl(struct adapter *adap) 323b5a02f50SAnish Bhatt { 324b5a02f50SAnish Bhatt struct clip_tbl *ctbl = adap->clipt; 325b5a02f50SAnish Bhatt 326b5a02f50SAnish Bhatt if (ctbl) { 327b5a02f50SAnish Bhatt if (ctbl->cl_list) 328752ade68SMichal Hocko kvfree(ctbl->cl_list); 329752ade68SMichal Hocko kvfree(ctbl); 330b5a02f50SAnish Bhatt } 331b5a02f50SAnish Bhatt } 332b5a02f50SAnish Bhatt EXPORT_SYMBOL(t4_cleanup_clip_tbl); 333