0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013 #include <linux/module.h>
0014 #include <linux/netdevice.h>
0015 #include <linux/jhash.h>
0016 #include <linux/if_vlan.h>
0017 #include <net/addrconf.h>
0018 #include "cxgb4.h"
0019 #include "clip_tbl.h"
0020
0021 static inline unsigned int ipv4_clip_hash(struct clip_tbl *c, const u32 *key)
0022 {
0023 unsigned int clipt_size_half = c->clipt_size / 2;
0024
0025 return jhash_1word(*key, 0) % clipt_size_half;
0026 }
0027
0028 static inline unsigned int ipv6_clip_hash(struct clip_tbl *d, const u32 *key)
0029 {
0030 unsigned int clipt_size_half = d->clipt_size / 2;
0031 u32 xor = key[0] ^ key[1] ^ key[2] ^ key[3];
0032
0033 return clipt_size_half +
0034 (jhash_1word(xor, 0) % clipt_size_half);
0035 }
0036
0037 static unsigned int clip_addr_hash(struct clip_tbl *ctbl, const u32 *addr,
0038 u8 v6)
0039 {
0040 return v6 ? ipv6_clip_hash(ctbl, addr) :
0041 ipv4_clip_hash(ctbl, addr);
0042 }
0043
0044 static int clip6_get_mbox(const struct net_device *dev,
0045 const struct in6_addr *lip)
0046 {
0047 struct adapter *adap = netdev2adap(dev);
0048 struct fw_clip_cmd c;
0049
0050 memset(&c, 0, sizeof(c));
0051 c.op_to_write = htonl(FW_CMD_OP_V(FW_CLIP_CMD) |
0052 FW_CMD_REQUEST_F | FW_CMD_WRITE_F);
0053 c.alloc_to_len16 = htonl(FW_CLIP_CMD_ALLOC_F | FW_LEN16(c));
0054 *(__be64 *)&c.ip_hi = *(__be64 *)(lip->s6_addr);
0055 *(__be64 *)&c.ip_lo = *(__be64 *)(lip->s6_addr + 8);
0056 return t4_wr_mbox_meat(adap, adap->mbox, &c, sizeof(c), &c, false);
0057 }
0058
0059 static int clip6_release_mbox(const struct net_device *dev,
0060 const struct in6_addr *lip)
0061 {
0062 struct adapter *adap = netdev2adap(dev);
0063 struct fw_clip_cmd c;
0064
0065 memset(&c, 0, sizeof(c));
0066 c.op_to_write = htonl(FW_CMD_OP_V(FW_CLIP_CMD) |
0067 FW_CMD_REQUEST_F | FW_CMD_READ_F);
0068 c.alloc_to_len16 = htonl(FW_CLIP_CMD_FREE_F | FW_LEN16(c));
0069 *(__be64 *)&c.ip_hi = *(__be64 *)(lip->s6_addr);
0070 *(__be64 *)&c.ip_lo = *(__be64 *)(lip->s6_addr + 8);
0071 return t4_wr_mbox_meat(adap, adap->mbox, &c, sizeof(c), &c, false);
0072 }
0073
0074 int cxgb4_clip_get(const struct net_device *dev, const u32 *lip, u8 v6)
0075 {
0076 struct adapter *adap = netdev2adap(dev);
0077 struct clip_tbl *ctbl = adap->clipt;
0078 struct clip_entry *ce, *cte;
0079 u32 *addr = (u32 *)lip;
0080 int hash;
0081 int ret = -1;
0082
0083 if (!ctbl)
0084 return 0;
0085
0086 hash = clip_addr_hash(ctbl, addr, v6);
0087
0088 read_lock_bh(&ctbl->lock);
0089 list_for_each_entry(cte, &ctbl->hash_list[hash], list) {
0090 if (cte->addr6.sin6_family == AF_INET6 && v6)
0091 ret = memcmp(lip, cte->addr6.sin6_addr.s6_addr,
0092 sizeof(struct in6_addr));
0093 else if (cte->addr.sin_family == AF_INET && !v6)
0094 ret = memcmp(lip, (char *)(&cte->addr.sin_addr),
0095 sizeof(struct in_addr));
0096 if (!ret) {
0097 ce = cte;
0098 read_unlock_bh(&ctbl->lock);
0099 refcount_inc(&ce->refcnt);
0100 return 0;
0101 }
0102 }
0103 read_unlock_bh(&ctbl->lock);
0104
0105 write_lock_bh(&ctbl->lock);
0106 if (!list_empty(&ctbl->ce_free_head)) {
0107 ce = list_first_entry(&ctbl->ce_free_head,
0108 struct clip_entry, list);
0109 list_del_init(&ce->list);
0110 spin_lock_init(&ce->lock);
0111 refcount_set(&ce->refcnt, 0);
0112 atomic_dec(&ctbl->nfree);
0113 list_add_tail(&ce->list, &ctbl->hash_list[hash]);
0114 if (v6) {
0115 ce->addr6.sin6_family = AF_INET6;
0116 memcpy(ce->addr6.sin6_addr.s6_addr,
0117 lip, sizeof(struct in6_addr));
0118 ret = clip6_get_mbox(dev, (const struct in6_addr *)lip);
0119 if (ret) {
0120 write_unlock_bh(&ctbl->lock);
0121 dev_err(adap->pdev_dev,
0122 "CLIP FW cmd failed with error %d, "
0123 "Connections using %pI6c wont be "
0124 "offloaded",
0125 ret, ce->addr6.sin6_addr.s6_addr);
0126 return ret;
0127 }
0128 } else {
0129 ce->addr.sin_family = AF_INET;
0130 memcpy((char *)(&ce->addr.sin_addr), lip,
0131 sizeof(struct in_addr));
0132 }
0133 } else {
0134 write_unlock_bh(&ctbl->lock);
0135 dev_info(adap->pdev_dev, "CLIP table overflow, "
0136 "Connections using %pI6c wont be offloaded",
0137 (void *)lip);
0138 return -ENOMEM;
0139 }
0140 write_unlock_bh(&ctbl->lock);
0141 refcount_set(&ce->refcnt, 1);
0142 return 0;
0143 }
0144 EXPORT_SYMBOL(cxgb4_clip_get);
0145
0146 void cxgb4_clip_release(const struct net_device *dev, const u32 *lip, u8 v6)
0147 {
0148 struct adapter *adap = netdev2adap(dev);
0149 struct clip_tbl *ctbl = adap->clipt;
0150 struct clip_entry *ce, *cte;
0151 u32 *addr = (u32 *)lip;
0152 int hash;
0153 int ret = -1;
0154
0155 if (!ctbl)
0156 return;
0157
0158 hash = clip_addr_hash(ctbl, addr, v6);
0159
0160 read_lock_bh(&ctbl->lock);
0161 list_for_each_entry(cte, &ctbl->hash_list[hash], list) {
0162 if (cte->addr6.sin6_family == AF_INET6 && v6)
0163 ret = memcmp(lip, cte->addr6.sin6_addr.s6_addr,
0164 sizeof(struct in6_addr));
0165 else if (cte->addr.sin_family == AF_INET && !v6)
0166 ret = memcmp(lip, (char *)(&cte->addr.sin_addr),
0167 sizeof(struct in_addr));
0168 if (!ret) {
0169 ce = cte;
0170 read_unlock_bh(&ctbl->lock);
0171 goto found;
0172 }
0173 }
0174 read_unlock_bh(&ctbl->lock);
0175
0176 return;
0177 found:
0178 write_lock_bh(&ctbl->lock);
0179 spin_lock_bh(&ce->lock);
0180 if (refcount_dec_and_test(&ce->refcnt)) {
0181 list_del_init(&ce->list);
0182 list_add_tail(&ce->list, &ctbl->ce_free_head);
0183 atomic_inc(&ctbl->nfree);
0184 if (v6)
0185 clip6_release_mbox(dev, (const struct in6_addr *)lip);
0186 }
0187 spin_unlock_bh(&ce->lock);
0188 write_unlock_bh(&ctbl->lock);
0189 }
0190 EXPORT_SYMBOL(cxgb4_clip_release);
0191
0192
0193
0194
0195
0196 static int cxgb4_update_dev_clip(struct net_device *root_dev,
0197 struct net_device *dev)
0198 {
0199 struct inet6_dev *idev = NULL;
0200 struct inet6_ifaddr *ifa;
0201 int ret = 0;
0202
0203 idev = __in6_dev_get(root_dev);
0204 if (!idev)
0205 return ret;
0206
0207 read_lock_bh(&idev->lock);
0208 list_for_each_entry(ifa, &idev->addr_list, if_list) {
0209 ret = cxgb4_clip_get(dev, (const u32 *)ifa->addr.s6_addr, 1);
0210 if (ret < 0)
0211 break;
0212 }
0213 read_unlock_bh(&idev->lock);
0214
0215 return ret;
0216 }
0217
0218 int cxgb4_update_root_dev_clip(struct net_device *dev)
0219 {
0220 struct net_device *root_dev = NULL;
0221 int i, ret = 0;
0222
0223
0224 ret = cxgb4_update_dev_clip(dev, dev);
0225 if (ret)
0226 return ret;
0227
0228
0229 root_dev = netdev_master_upper_dev_get_rcu(dev);
0230 if (root_dev) {
0231 ret = cxgb4_update_dev_clip(root_dev, dev);
0232 if (ret)
0233 return ret;
0234 }
0235
0236 for (i = 0; i < VLAN_N_VID; i++) {
0237 root_dev = __vlan_find_dev_deep_rcu(dev, htons(ETH_P_8021Q), i);
0238 if (!root_dev)
0239 continue;
0240
0241 ret = cxgb4_update_dev_clip(root_dev, dev);
0242 if (ret)
0243 break;
0244 }
0245
0246 return ret;
0247 }
0248 EXPORT_SYMBOL(cxgb4_update_root_dev_clip);
0249
0250 int clip_tbl_show(struct seq_file *seq, void *v)
0251 {
0252 struct adapter *adapter = seq->private;
0253 struct clip_tbl *ctbl = adapter->clipt;
0254 struct clip_entry *ce;
0255 char ip[60];
0256 int i;
0257
0258 read_lock_bh(&ctbl->lock);
0259
0260 seq_puts(seq, "IP Address Users\n");
0261 for (i = 0 ; i < ctbl->clipt_size; ++i) {
0262 list_for_each_entry(ce, &ctbl->hash_list[i], list) {
0263 ip[0] = '\0';
0264 sprintf(ip, "%pISc", &ce->addr);
0265 seq_printf(seq, "%-25s %u\n", ip,
0266 refcount_read(&ce->refcnt));
0267 }
0268 }
0269 seq_printf(seq, "Free clip entries : %d\n", atomic_read(&ctbl->nfree));
0270
0271 read_unlock_bh(&ctbl->lock);
0272
0273 return 0;
0274 }
0275
0276 struct clip_tbl *t4_init_clip_tbl(unsigned int clipt_start,
0277 unsigned int clipt_end)
0278 {
0279 struct clip_entry *cl_list;
0280 struct clip_tbl *ctbl;
0281 unsigned int clipt_size;
0282 int i;
0283
0284 if (clipt_start >= clipt_end)
0285 return NULL;
0286 clipt_size = clipt_end - clipt_start + 1;
0287 if (clipt_size < CLIPT_MIN_HASH_BUCKETS)
0288 return NULL;
0289
0290 ctbl = kvzalloc(struct_size(ctbl, hash_list, clipt_size), GFP_KERNEL);
0291 if (!ctbl)
0292 return NULL;
0293
0294 ctbl->clipt_start = clipt_start;
0295 ctbl->clipt_size = clipt_size;
0296 INIT_LIST_HEAD(&ctbl->ce_free_head);
0297
0298 atomic_set(&ctbl->nfree, clipt_size);
0299 rwlock_init(&ctbl->lock);
0300
0301 for (i = 0; i < ctbl->clipt_size; ++i)
0302 INIT_LIST_HEAD(&ctbl->hash_list[i]);
0303
0304 cl_list = kvcalloc(clipt_size, sizeof(struct clip_entry), GFP_KERNEL);
0305 if (!cl_list) {
0306 kvfree(ctbl);
0307 return NULL;
0308 }
0309 ctbl->cl_list = (void *)cl_list;
0310
0311 for (i = 0; i < clipt_size; i++) {
0312 INIT_LIST_HEAD(&cl_list[i].list);
0313 list_add_tail(&cl_list[i].list, &ctbl->ce_free_head);
0314 }
0315
0316 return ctbl;
0317 }
0318
0319 void t4_cleanup_clip_tbl(struct adapter *adap)
0320 {
0321 struct clip_tbl *ctbl = adap->clipt;
0322
0323 if (ctbl) {
0324 kvfree(ctbl->cl_list);
0325 kvfree(ctbl);
0326 }
0327 }
0328 EXPORT_SYMBOL(t4_cleanup_clip_tbl);