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); 99eaf6ab76SElena Reshetova refcount_inc(&ce->refcnt); 100eaf6ab76SElena 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); 109*44e261c7SYang Yingliang list_del_init(&ce->list); 110b5a02f50SAnish Bhatt spin_lock_init(&ce->lock); 111eaf6ab76SElena Reshetova refcount_set(&ce->refcnt, 0); 112b5a02f50SAnish Bhatt atomic_dec(&ctbl->nfree); 113b5a02f50SAnish Bhatt list_add_tail(&ce->list, &ctbl->hash_list[hash]); 114b5a02f50SAnish Bhatt if (v6) { 1155a8eeec4SAnish Bhatt ce->addr6.sin6_family = AF_INET6; 1165a8eeec4SAnish Bhatt memcpy(ce->addr6.sin6_addr.s6_addr, 1175a8eeec4SAnish Bhatt lip, sizeof(struct in6_addr)); 118b5a02f50SAnish Bhatt ret = clip6_get_mbox(dev, (const struct in6_addr *)lip); 119b5a02f50SAnish Bhatt if (ret) { 120b5a02f50SAnish Bhatt write_unlock_bh(&ctbl->lock); 121eb72f74fSHariprasad Shenai dev_err(adap->pdev_dev, 122eb72f74fSHariprasad Shenai "CLIP FW cmd failed with error %d, " 123eb72f74fSHariprasad Shenai "Connections using %pI6c wont be " 124eb72f74fSHariprasad Shenai "offloaded", 125eb72f74fSHariprasad Shenai ret, ce->addr6.sin6_addr.s6_addr); 126b5a02f50SAnish Bhatt return ret; 127b5a02f50SAnish Bhatt } 1285a8eeec4SAnish Bhatt } else { 1295a8eeec4SAnish Bhatt ce->addr.sin_family = AF_INET; 1305a8eeec4SAnish Bhatt memcpy((char *)(&ce->addr.sin_addr), lip, 1315a8eeec4SAnish Bhatt sizeof(struct in_addr)); 132b5a02f50SAnish Bhatt } 133b5a02f50SAnish Bhatt } else { 134b5a02f50SAnish Bhatt write_unlock_bh(&ctbl->lock); 135eb72f74fSHariprasad Shenai dev_info(adap->pdev_dev, "CLIP table overflow, " 136eb72f74fSHariprasad Shenai "Connections using %pI6c wont be offloaded", 137eb72f74fSHariprasad Shenai (void *)lip); 138b5a02f50SAnish Bhatt return -ENOMEM; 139b5a02f50SAnish Bhatt } 140b5a02f50SAnish Bhatt write_unlock_bh(&ctbl->lock); 141eaf6ab76SElena Reshetova refcount_set(&ce->refcnt, 1); 142b5a02f50SAnish Bhatt return 0; 143b5a02f50SAnish Bhatt } 144b5a02f50SAnish Bhatt EXPORT_SYMBOL(cxgb4_clip_get); 145b5a02f50SAnish Bhatt 146b5a02f50SAnish Bhatt void cxgb4_clip_release(const struct net_device *dev, const u32 *lip, u8 v6) 147b5a02f50SAnish Bhatt { 148b5a02f50SAnish Bhatt struct adapter *adap = netdev2adap(dev); 149b5a02f50SAnish Bhatt struct clip_tbl *ctbl = adap->clipt; 150b5a02f50SAnish Bhatt struct clip_entry *ce, *cte; 151b5a02f50SAnish Bhatt u32 *addr = (u32 *)lip; 152b5a02f50SAnish Bhatt int hash; 1535a8eeec4SAnish Bhatt int ret = -1; 154b5a02f50SAnish Bhatt 155eb72f74fSHariprasad Shenai if (!ctbl) 156eb72f74fSHariprasad Shenai return; 157eb72f74fSHariprasad Shenai 1585a8eeec4SAnish Bhatt hash = clip_addr_hash(ctbl, addr, v6); 159b5a02f50SAnish Bhatt 160b5a02f50SAnish Bhatt read_lock_bh(&ctbl->lock); 161b5a02f50SAnish Bhatt list_for_each_entry(cte, &ctbl->hash_list[hash], list) { 1625a8eeec4SAnish Bhatt if (cte->addr6.sin6_family == AF_INET6 && v6) 1635a8eeec4SAnish Bhatt ret = memcmp(lip, cte->addr6.sin6_addr.s6_addr, 1645a8eeec4SAnish Bhatt sizeof(struct in6_addr)); 1655a8eeec4SAnish Bhatt else if (cte->addr.sin_family == AF_INET && !v6) 1665a8eeec4SAnish Bhatt ret = memcmp(lip, (char *)(&cte->addr.sin_addr), 1675a8eeec4SAnish Bhatt sizeof(struct in_addr)); 1685a8eeec4SAnish Bhatt if (!ret) { 169b5a02f50SAnish Bhatt ce = cte; 170b5a02f50SAnish Bhatt read_unlock_bh(&ctbl->lock); 171b5a02f50SAnish Bhatt goto found; 172b5a02f50SAnish Bhatt } 173b5a02f50SAnish Bhatt } 174b5a02f50SAnish Bhatt read_unlock_bh(&ctbl->lock); 175b5a02f50SAnish Bhatt 176b5a02f50SAnish Bhatt return; 177b5a02f50SAnish Bhatt found: 178b5a02f50SAnish Bhatt write_lock_bh(&ctbl->lock); 179b5a02f50SAnish Bhatt spin_lock_bh(&ce->lock); 180eaf6ab76SElena Reshetova if (refcount_dec_and_test(&ce->refcnt)) { 181*44e261c7SYang Yingliang list_del_init(&ce->list); 182b5a02f50SAnish Bhatt list_add_tail(&ce->list, &ctbl->ce_free_head); 183b5a02f50SAnish Bhatt atomic_inc(&ctbl->nfree); 184b5a02f50SAnish Bhatt if (v6) 185b5a02f50SAnish Bhatt clip6_release_mbox(dev, (const struct in6_addr *)lip); 186b5a02f50SAnish Bhatt } 187b5a02f50SAnish Bhatt spin_unlock_bh(&ce->lock); 188b5a02f50SAnish Bhatt write_unlock_bh(&ctbl->lock); 189b5a02f50SAnish Bhatt } 190b5a02f50SAnish Bhatt EXPORT_SYMBOL(cxgb4_clip_release); 191b5a02f50SAnish Bhatt 192b5a02f50SAnish Bhatt /* Retrieves IPv6 addresses from a root device (bond, vlan) associated with 193b5a02f50SAnish Bhatt * a physical device. 194b5a02f50SAnish Bhatt * The physical device reference is needed to send the actul CLIP command. 195b5a02f50SAnish Bhatt */ 196b5a02f50SAnish Bhatt static int cxgb4_update_dev_clip(struct net_device *root_dev, 197b5a02f50SAnish Bhatt struct net_device *dev) 198b5a02f50SAnish Bhatt { 199b5a02f50SAnish Bhatt struct inet6_dev *idev = NULL; 200b5a02f50SAnish Bhatt struct inet6_ifaddr *ifa; 201b5a02f50SAnish Bhatt int ret = 0; 202b5a02f50SAnish Bhatt 203b5a02f50SAnish Bhatt idev = __in6_dev_get(root_dev); 204b5a02f50SAnish Bhatt if (!idev) 205b5a02f50SAnish Bhatt return ret; 206b5a02f50SAnish Bhatt 207b5a02f50SAnish Bhatt read_lock_bh(&idev->lock); 208b5a02f50SAnish Bhatt list_for_each_entry(ifa, &idev->addr_list, if_list) { 209b5a02f50SAnish Bhatt ret = cxgb4_clip_get(dev, (const u32 *)ifa->addr.s6_addr, 1); 210b5a02f50SAnish Bhatt if (ret < 0) 211b5a02f50SAnish Bhatt break; 212b5a02f50SAnish Bhatt } 213b5a02f50SAnish Bhatt read_unlock_bh(&idev->lock); 214b5a02f50SAnish Bhatt 215b5a02f50SAnish Bhatt return ret; 216b5a02f50SAnish Bhatt } 217b5a02f50SAnish Bhatt 218b5a02f50SAnish Bhatt int cxgb4_update_root_dev_clip(struct net_device *dev) 219b5a02f50SAnish Bhatt { 220b5a02f50SAnish Bhatt struct net_device *root_dev = NULL; 221b5a02f50SAnish Bhatt int i, ret = 0; 222b5a02f50SAnish Bhatt 223b5a02f50SAnish Bhatt /* First populate the real net device's IPv6 addresses */ 224b5a02f50SAnish Bhatt ret = cxgb4_update_dev_clip(dev, dev); 225b5a02f50SAnish Bhatt if (ret) 226b5a02f50SAnish Bhatt return ret; 227b5a02f50SAnish Bhatt 228b5a02f50SAnish Bhatt /* Parse all bond and vlan devices layered on top of the physical dev */ 229b5a02f50SAnish Bhatt root_dev = netdev_master_upper_dev_get_rcu(dev); 230b5a02f50SAnish Bhatt if (root_dev) { 231b5a02f50SAnish Bhatt ret = cxgb4_update_dev_clip(root_dev, dev); 232b5a02f50SAnish Bhatt if (ret) 233b5a02f50SAnish Bhatt return ret; 234b5a02f50SAnish Bhatt } 235b5a02f50SAnish Bhatt 236b5a02f50SAnish Bhatt for (i = 0; i < VLAN_N_VID; i++) { 237b5a02f50SAnish Bhatt root_dev = __vlan_find_dev_deep_rcu(dev, htons(ETH_P_8021Q), i); 238b5a02f50SAnish Bhatt if (!root_dev) 239b5a02f50SAnish Bhatt continue; 240b5a02f50SAnish Bhatt 241b5a02f50SAnish Bhatt ret = cxgb4_update_dev_clip(root_dev, dev); 242b5a02f50SAnish Bhatt if (ret) 243b5a02f50SAnish Bhatt break; 244b5a02f50SAnish Bhatt } 245b5a02f50SAnish Bhatt 246b5a02f50SAnish Bhatt return ret; 247b5a02f50SAnish Bhatt } 248b5a02f50SAnish Bhatt EXPORT_SYMBOL(cxgb4_update_root_dev_clip); 249b5a02f50SAnish Bhatt 250b5a02f50SAnish Bhatt int clip_tbl_show(struct seq_file *seq, void *v) 251b5a02f50SAnish Bhatt { 252b5a02f50SAnish Bhatt struct adapter *adapter = seq->private; 253b5a02f50SAnish Bhatt struct clip_tbl *ctbl = adapter->clipt; 254b5a02f50SAnish Bhatt struct clip_entry *ce; 255b5a02f50SAnish Bhatt char ip[60]; 256b5a02f50SAnish Bhatt int i; 257b5a02f50SAnish Bhatt 258b5a02f50SAnish Bhatt read_lock_bh(&ctbl->lock); 259b5a02f50SAnish Bhatt 260b5a02f50SAnish Bhatt seq_puts(seq, "IP Address Users\n"); 261b5a02f50SAnish Bhatt for (i = 0 ; i < ctbl->clipt_size; ++i) { 262b5a02f50SAnish Bhatt list_for_each_entry(ce, &ctbl->hash_list[i], list) { 263b5a02f50SAnish Bhatt ip[0] = '\0'; 2645a8eeec4SAnish Bhatt sprintf(ip, "%pISc", &ce->addr); 265b5a02f50SAnish Bhatt seq_printf(seq, "%-25s %u\n", ip, 266eaf6ab76SElena Reshetova refcount_read(&ce->refcnt)); 267b5a02f50SAnish Bhatt } 268b5a02f50SAnish Bhatt } 269b5a02f50SAnish Bhatt seq_printf(seq, "Free clip entries : %d\n", atomic_read(&ctbl->nfree)); 270b5a02f50SAnish Bhatt 271b5a02f50SAnish Bhatt read_unlock_bh(&ctbl->lock); 272b5a02f50SAnish Bhatt 273b5a02f50SAnish Bhatt return 0; 274b5a02f50SAnish Bhatt } 275b5a02f50SAnish Bhatt 276b5a02f50SAnish Bhatt struct clip_tbl *t4_init_clip_tbl(unsigned int clipt_start, 277b5a02f50SAnish Bhatt unsigned int clipt_end) 278b5a02f50SAnish Bhatt { 279b5a02f50SAnish Bhatt struct clip_entry *cl_list; 280b5a02f50SAnish Bhatt struct clip_tbl *ctbl; 281b5a02f50SAnish Bhatt unsigned int clipt_size; 282b5a02f50SAnish Bhatt int i; 283b5a02f50SAnish Bhatt 284b5a02f50SAnish Bhatt if (clipt_start >= clipt_end) 285b5a02f50SAnish Bhatt return NULL; 286b5a02f50SAnish Bhatt clipt_size = clipt_end - clipt_start + 1; 287b5a02f50SAnish Bhatt if (clipt_size < CLIPT_MIN_HASH_BUCKETS) 288b5a02f50SAnish Bhatt return NULL; 289b5a02f50SAnish Bhatt 2909f672984SGustavo A. R. Silva ctbl = kvzalloc(struct_size(ctbl, hash_list, clipt_size), GFP_KERNEL); 291b5a02f50SAnish Bhatt if (!ctbl) 292b5a02f50SAnish Bhatt return NULL; 293b5a02f50SAnish Bhatt 294b5a02f50SAnish Bhatt ctbl->clipt_start = clipt_start; 295b5a02f50SAnish Bhatt ctbl->clipt_size = clipt_size; 296b5a02f50SAnish Bhatt INIT_LIST_HEAD(&ctbl->ce_free_head); 297b5a02f50SAnish Bhatt 298b5a02f50SAnish Bhatt atomic_set(&ctbl->nfree, clipt_size); 299b5a02f50SAnish Bhatt rwlock_init(&ctbl->lock); 300b5a02f50SAnish Bhatt 301b5a02f50SAnish Bhatt for (i = 0; i < ctbl->clipt_size; ++i) 302b5a02f50SAnish Bhatt INIT_LIST_HEAD(&ctbl->hash_list[i]); 303b5a02f50SAnish Bhatt 304778e1cddSKees Cook cl_list = kvcalloc(clipt_size, sizeof(struct clip_entry), GFP_KERNEL); 3053934aa4cSInsu Yun if (!cl_list) { 306752ade68SMichal Hocko kvfree(ctbl); 3073934aa4cSInsu Yun return NULL; 3083934aa4cSInsu Yun } 309b5a02f50SAnish Bhatt ctbl->cl_list = (void *)cl_list; 310b5a02f50SAnish Bhatt 311b5a02f50SAnish Bhatt for (i = 0; i < clipt_size; i++) { 312b5a02f50SAnish Bhatt INIT_LIST_HEAD(&cl_list[i].list); 313b5a02f50SAnish Bhatt list_add_tail(&cl_list[i].list, &ctbl->ce_free_head); 314b5a02f50SAnish Bhatt } 315b5a02f50SAnish Bhatt 316b5a02f50SAnish Bhatt return ctbl; 317b5a02f50SAnish Bhatt } 318b5a02f50SAnish Bhatt 319b5a02f50SAnish Bhatt void t4_cleanup_clip_tbl(struct adapter *adap) 320b5a02f50SAnish Bhatt { 321b5a02f50SAnish Bhatt struct clip_tbl *ctbl = adap->clipt; 322b5a02f50SAnish Bhatt 323b5a02f50SAnish Bhatt if (ctbl) { 324752ade68SMichal Hocko kvfree(ctbl->cl_list); 325752ade68SMichal Hocko kvfree(ctbl); 326b5a02f50SAnish Bhatt } 327b5a02f50SAnish Bhatt } 328b5a02f50SAnish Bhatt EXPORT_SYMBOL(t4_cleanup_clip_tbl); 329