Skip to content

Commit b5a02f5

Browse files
anishdavem330
authored andcommitted
cxgb4 : Update ipv6 address handling api
This patch improves on previously added support for ipv6 addresses. The code is consolidated to a single file and adds an api for use by dependent upper level drivers such as cxgb4i/iw_cxgb4 etc. Signed-off-by: Anish Bhatt <[email protected]> Signed-off-by: Deepak Singh <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 5055c37 commit b5a02f5

File tree

7 files changed

+447
-163
lines changed

7 files changed

+447
-163
lines changed

drivers/net/ethernet/chelsio/cxgb4/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@
44

55
obj-$(CONFIG_CHELSIO_T4) += cxgb4.o
66

7-
cxgb4-objs := cxgb4_main.o l2t.o t4_hw.o sge.o
7+
cxgb4-objs := cxgb4_main.o l2t.o t4_hw.o sge.o clip_tbl.o
88
cxgb4-$(CONFIG_CHELSIO_T4_DCB) += cxgb4_dcb.o
99
cxgb4-$(CONFIG_DEBUG_FS) += cxgb4_debugfs.o
Lines changed: 314 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,314 @@
1+
/*
2+
* This file is part of the Chelsio T4 Ethernet driver for Linux.
3+
* Copyright (C) 2003-2014 Chelsio Communications. All rights reserved.
4+
*
5+
* Written by Deepak ([email protected])
6+
*
7+
* This program is distributed in the hope that it will be useful, but WITHOUT
8+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
9+
* FITNESS FOR A PARTICULAR PURPOSE. See the LICENSE file included in this
10+
* release for licensing terms and conditions.
11+
*/
12+
13+
#include <linux/module.h>
14+
#include <linux/netdevice.h>
15+
#include <linux/jhash.h>
16+
#include <linux/if_vlan.h>
17+
#include <net/addrconf.h>
18+
#include "cxgb4.h"
19+
#include "clip_tbl.h"
20+
21+
static inline unsigned int ipv4_clip_hash(struct clip_tbl *c, const u32 *key)
22+
{
23+
unsigned int clipt_size_half = c->clipt_size / 2;
24+
25+
return jhash_1word(*key, 0) % clipt_size_half;
26+
}
27+
28+
static inline unsigned int ipv6_clip_hash(struct clip_tbl *d, const u32 *key)
29+
{
30+
unsigned int clipt_size_half = d->clipt_size / 2;
31+
u32 xor = key[0] ^ key[1] ^ key[2] ^ key[3];
32+
33+
return clipt_size_half +
34+
(jhash_1word(xor, 0) % clipt_size_half);
35+
}
36+
37+
static unsigned int clip_addr_hash(struct clip_tbl *ctbl, const u32 *addr,
38+
int addr_len)
39+
{
40+
return addr_len == 4 ? ipv4_clip_hash(ctbl, addr) :
41+
ipv6_clip_hash(ctbl, addr);
42+
}
43+
44+
static int clip6_get_mbox(const struct net_device *dev,
45+
const struct in6_addr *lip)
46+
{
47+
struct adapter *adap = netdev2adap(dev);
48+
struct fw_clip_cmd c;
49+
50+
memset(&c, 0, sizeof(c));
51+
c.op_to_write = htonl(FW_CMD_OP_V(FW_CLIP_CMD) |
52+
FW_CMD_REQUEST_F | FW_CMD_WRITE_F);
53+
c.alloc_to_len16 = htonl(FW_CLIP_CMD_ALLOC_F | FW_LEN16(c));
54+
*(__be64 *)&c.ip_hi = *(__be64 *)(lip->s6_addr);
55+
*(__be64 *)&c.ip_lo = *(__be64 *)(lip->s6_addr + 8);
56+
return t4_wr_mbox_meat(adap, adap->mbox, &c, sizeof(c), &c, false);
57+
}
58+
59+
static int clip6_release_mbox(const struct net_device *dev,
60+
const struct in6_addr *lip)
61+
{
62+
struct adapter *adap = netdev2adap(dev);
63+
struct fw_clip_cmd c;
64+
65+
memset(&c, 0, sizeof(c));
66+
c.op_to_write = htonl(FW_CMD_OP_V(FW_CLIP_CMD) |
67+
FW_CMD_REQUEST_F | FW_CMD_READ_F);
68+
c.alloc_to_len16 = htonl(FW_CLIP_CMD_FREE_F | FW_LEN16(c));
69+
*(__be64 *)&c.ip_hi = *(__be64 *)(lip->s6_addr);
70+
*(__be64 *)&c.ip_lo = *(__be64 *)(lip->s6_addr + 8);
71+
return t4_wr_mbox_meat(adap, adap->mbox, &c, sizeof(c), &c, false);
72+
}
73+
74+
int cxgb4_clip_get(const struct net_device *dev, const u32 *lip, u8 v6)
75+
{
76+
struct adapter *adap = netdev2adap(dev);
77+
struct clip_tbl *ctbl = adap->clipt;
78+
struct clip_entry *ce, *cte;
79+
u32 *addr = (u32 *)lip;
80+
int hash;
81+
int addr_len;
82+
int ret = 0;
83+
84+
if (v6)
85+
addr_len = 16;
86+
else
87+
addr_len = 4;
88+
89+
hash = clip_addr_hash(ctbl, addr, addr_len);
90+
91+
read_lock_bh(&ctbl->lock);
92+
list_for_each_entry(cte, &ctbl->hash_list[hash], list) {
93+
if (addr_len == cte->addr_len &&
94+
memcmp(lip, cte->addr, cte->addr_len) == 0) {
95+
ce = cte;
96+
read_unlock_bh(&ctbl->lock);
97+
goto found;
98+
}
99+
}
100+
read_unlock_bh(&ctbl->lock);
101+
102+
write_lock_bh(&ctbl->lock);
103+
if (!list_empty(&ctbl->ce_free_head)) {
104+
ce = list_first_entry(&ctbl->ce_free_head,
105+
struct clip_entry, list);
106+
list_del(&ce->list);
107+
INIT_LIST_HEAD(&ce->list);
108+
spin_lock_init(&ce->lock);
109+
atomic_set(&ce->refcnt, 0);
110+
atomic_dec(&ctbl->nfree);
111+
ce->addr_len = addr_len;
112+
memcpy(ce->addr, lip, addr_len);
113+
list_add_tail(&ce->list, &ctbl->hash_list[hash]);
114+
if (v6) {
115+
ret = clip6_get_mbox(dev, (const struct in6_addr *)lip);
116+
if (ret) {
117+
write_unlock_bh(&ctbl->lock);
118+
return ret;
119+
}
120+
}
121+
} else {
122+
write_unlock_bh(&ctbl->lock);
123+
return -ENOMEM;
124+
}
125+
write_unlock_bh(&ctbl->lock);
126+
found:
127+
atomic_inc(&ce->refcnt);
128+
129+
return 0;
130+
}
131+
EXPORT_SYMBOL(cxgb4_clip_get);
132+
133+
void cxgb4_clip_release(const struct net_device *dev, const u32 *lip, u8 v6)
134+
{
135+
struct adapter *adap = netdev2adap(dev);
136+
struct clip_tbl *ctbl = adap->clipt;
137+
struct clip_entry *ce, *cte;
138+
u32 *addr = (u32 *)lip;
139+
int hash;
140+
int addr_len;
141+
142+
if (v6)
143+
addr_len = 16;
144+
else
145+
addr_len = 4;
146+
147+
hash = clip_addr_hash(ctbl, addr, addr_len);
148+
149+
read_lock_bh(&ctbl->lock);
150+
list_for_each_entry(cte, &ctbl->hash_list[hash], list) {
151+
if (addr_len == cte->addr_len &&
152+
memcmp(lip, cte->addr, cte->addr_len) == 0) {
153+
ce = cte;
154+
read_unlock_bh(&ctbl->lock);
155+
goto found;
156+
}
157+
}
158+
read_unlock_bh(&ctbl->lock);
159+
160+
return;
161+
found:
162+
write_lock_bh(&ctbl->lock);
163+
spin_lock_bh(&ce->lock);
164+
if (atomic_dec_and_test(&ce->refcnt)) {
165+
list_del(&ce->list);
166+
INIT_LIST_HEAD(&ce->list);
167+
list_add_tail(&ce->list, &ctbl->ce_free_head);
168+
atomic_inc(&ctbl->nfree);
169+
if (v6)
170+
clip6_release_mbox(dev, (const struct in6_addr *)lip);
171+
}
172+
spin_unlock_bh(&ce->lock);
173+
write_unlock_bh(&ctbl->lock);
174+
}
175+
EXPORT_SYMBOL(cxgb4_clip_release);
176+
177+
/* Retrieves IPv6 addresses from a root device (bond, vlan) associated with
178+
* a physical device.
179+
* The physical device reference is needed to send the actul CLIP command.
180+
*/
181+
static int cxgb4_update_dev_clip(struct net_device *root_dev,
182+
struct net_device *dev)
183+
{
184+
struct inet6_dev *idev = NULL;
185+
struct inet6_ifaddr *ifa;
186+
int ret = 0;
187+
188+
idev = __in6_dev_get(root_dev);
189+
if (!idev)
190+
return ret;
191+
192+
read_lock_bh(&idev->lock);
193+
list_for_each_entry(ifa, &idev->addr_list, if_list) {
194+
ret = cxgb4_clip_get(dev, (const u32 *)ifa->addr.s6_addr, 1);
195+
if (ret < 0)
196+
break;
197+
}
198+
read_unlock_bh(&idev->lock);
199+
200+
return ret;
201+
}
202+
203+
int cxgb4_update_root_dev_clip(struct net_device *dev)
204+
{
205+
struct net_device *root_dev = NULL;
206+
int i, ret = 0;
207+
208+
/* First populate the real net device's IPv6 addresses */
209+
ret = cxgb4_update_dev_clip(dev, dev);
210+
if (ret)
211+
return ret;
212+
213+
/* Parse all bond and vlan devices layered on top of the physical dev */
214+
root_dev = netdev_master_upper_dev_get_rcu(dev);
215+
if (root_dev) {
216+
ret = cxgb4_update_dev_clip(root_dev, dev);
217+
if (ret)
218+
return ret;
219+
}
220+
221+
for (i = 0; i < VLAN_N_VID; i++) {
222+
root_dev = __vlan_find_dev_deep_rcu(dev, htons(ETH_P_8021Q), i);
223+
if (!root_dev)
224+
continue;
225+
226+
ret = cxgb4_update_dev_clip(root_dev, dev);
227+
if (ret)
228+
break;
229+
}
230+
231+
return ret;
232+
}
233+
EXPORT_SYMBOL(cxgb4_update_root_dev_clip);
234+
235+
int clip_tbl_show(struct seq_file *seq, void *v)
236+
{
237+
struct adapter *adapter = seq->private;
238+
struct clip_tbl *ctbl = adapter->clipt;
239+
struct clip_entry *ce;
240+
char ip[60];
241+
int i;
242+
243+
read_lock_bh(&ctbl->lock);
244+
245+
seq_puts(seq, "IP Address Users\n");
246+
for (i = 0 ; i < ctbl->clipt_size; ++i) {
247+
list_for_each_entry(ce, &ctbl->hash_list[i], list) {
248+
ip[0] = '\0';
249+
if (ce->addr_len == 16)
250+
sprintf(ip, "%pI6c", ce->addr);
251+
else
252+
sprintf(ip, "%pI4c", ce->addr);
253+
seq_printf(seq, "%-25s %u\n", ip,
254+
atomic_read(&ce->refcnt));
255+
}
256+
}
257+
seq_printf(seq, "Free clip entries : %d\n", atomic_read(&ctbl->nfree));
258+
259+
read_unlock_bh(&ctbl->lock);
260+
261+
return 0;
262+
}
263+
264+
struct clip_tbl *t4_init_clip_tbl(unsigned int clipt_start,
265+
unsigned int clipt_end)
266+
{
267+
struct clip_entry *cl_list;
268+
struct clip_tbl *ctbl;
269+
unsigned int clipt_size;
270+
int i;
271+
272+
if (clipt_start >= clipt_end)
273+
return NULL;
274+
clipt_size = clipt_end - clipt_start + 1;
275+
if (clipt_size < CLIPT_MIN_HASH_BUCKETS)
276+
return NULL;
277+
278+
ctbl = t4_alloc_mem(sizeof(*ctbl) +
279+
clipt_size*sizeof(struct list_head));
280+
if (!ctbl)
281+
return NULL;
282+
283+
ctbl->clipt_start = clipt_start;
284+
ctbl->clipt_size = clipt_size;
285+
INIT_LIST_HEAD(&ctbl->ce_free_head);
286+
287+
atomic_set(&ctbl->nfree, clipt_size);
288+
rwlock_init(&ctbl->lock);
289+
290+
for (i = 0; i < ctbl->clipt_size; ++i)
291+
INIT_LIST_HEAD(&ctbl->hash_list[i]);
292+
293+
cl_list = t4_alloc_mem(clipt_size*sizeof(struct clip_entry));
294+
ctbl->cl_list = (void *)cl_list;
295+
296+
for (i = 0; i < clipt_size; i++) {
297+
INIT_LIST_HEAD(&cl_list[i].list);
298+
list_add_tail(&cl_list[i].list, &ctbl->ce_free_head);
299+
}
300+
301+
return ctbl;
302+
}
303+
304+
void t4_cleanup_clip_tbl(struct adapter *adap)
305+
{
306+
struct clip_tbl *ctbl = adap->clipt;
307+
308+
if (ctbl) {
309+
if (ctbl->cl_list)
310+
t4_free_mem(ctbl->cl_list);
311+
t4_free_mem(ctbl);
312+
}
313+
}
314+
EXPORT_SYMBOL(t4_cleanup_clip_tbl);
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* This file is part of the Chelsio T4 Ethernet driver for Linux.
3+
* Copyright (C) 2003-2014 Chelsio Communications. All rights reserved.
4+
*
5+
* Written by Deepak ([email protected])
6+
*
7+
* This program is distributed in the hope that it will be useful, but WITHOUT
8+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
9+
* FITNESS FOR A PARTICULAR PURPOSE. See the LICENSE file included in this
10+
* release for licensing terms and conditions.
11+
*/
12+
13+
struct clip_entry {
14+
spinlock_t lock; /* Hold while modifying clip reference */
15+
atomic_t refcnt;
16+
struct list_head list;
17+
u32 addr[4];
18+
int addr_len;
19+
};
20+
21+
struct clip_tbl {
22+
unsigned int clipt_start;
23+
unsigned int clipt_size;
24+
rwlock_t lock;
25+
atomic_t nfree;
26+
struct list_head ce_free_head;
27+
void *cl_list;
28+
struct list_head hash_list[0];
29+
};
30+
31+
enum {
32+
CLIPT_MIN_HASH_BUCKETS = 2,
33+
};
34+
35+
struct clip_tbl *t4_init_clip_tbl(unsigned int clipt_start,
36+
unsigned int clipt_end);
37+
int cxgb4_clip_get(const struct net_device *dev, const u32 *lip, u8 v6);
38+
void cxgb4_clip_release(const struct net_device *dev, const u32 *lip, u8 v6);
39+
int clip_tbl_show(struct seq_file *seq, void *v);
40+
int cxgb4_update_root_dev_clip(struct net_device *dev);
41+
void t4_cleanup_clip_tbl(struct adapter *adap);

drivers/net/ethernet/chelsio/cxgb4/cxgb4.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -668,6 +668,9 @@ struct adapter {
668668
unsigned int l2t_start;
669669
unsigned int l2t_end;
670670
struct l2t_data *l2t;
671+
unsigned int clipt_start;
672+
unsigned int clipt_end;
673+
struct clip_tbl *clipt;
671674
void *uld_handle[CXGB4_ULD_MAX];
672675
struct list_head list_node;
673676
struct list_head rcu_node;

0 commit comments

Comments
 (0)