cxgb4: Add API to alloc l2t entry; also update existing ones
Based on original work by Kumar Sanghvi <kumaras@chelsio.com> Signed-off-by: Hariprasad Shenai <hariprasad@chelsio.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
e41e282428
commit
f7502659ce
3 changed files with 117 additions and 25 deletions
|
@ -1181,15 +1181,9 @@ static int set_filter_wr(struct adapter *adapter, int fidx)
|
||||||
*/
|
*/
|
||||||
if (f->fs.newdmac || f->fs.newvlan) {
|
if (f->fs.newdmac || f->fs.newvlan) {
|
||||||
/* allocate L2T entry for new filter */
|
/* allocate L2T entry for new filter */
|
||||||
f->l2t = t4_l2t_alloc_switching(adapter->l2t);
|
f->l2t = t4_l2t_alloc_switching(adapter, f->fs.vlan,
|
||||||
|
f->fs.eport, f->fs.dmac);
|
||||||
if (f->l2t == NULL) {
|
if (f->l2t == NULL) {
|
||||||
kfree_skb(skb);
|
|
||||||
return -EAGAIN;
|
|
||||||
}
|
|
||||||
if (t4_l2t_set_switching(adapter, f->l2t, f->fs.vlan,
|
|
||||||
f->fs.eport, f->fs.dmac)) {
|
|
||||||
cxgb4_l2t_release(f->l2t);
|
|
||||||
f->l2t = NULL;
|
|
||||||
kfree_skb(skb);
|
kfree_skb(skb);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
|
@ -305,9 +305,82 @@ found:
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
static struct l2t_entry *find_or_alloc_l2e(struct l2t_data *d, u16 vlan,
|
||||||
* Called when an L2T entry has no more users.
|
u8 port, u8 *dmac)
|
||||||
|
{
|
||||||
|
struct l2t_entry *end, *e, **p;
|
||||||
|
struct l2t_entry *first_free = NULL;
|
||||||
|
|
||||||
|
for (e = &d->l2tab[0], end = &d->l2tab[d->l2t_size]; e != end; ++e) {
|
||||||
|
if (atomic_read(&e->refcnt) == 0) {
|
||||||
|
if (!first_free)
|
||||||
|
first_free = e;
|
||||||
|
} else {
|
||||||
|
if (e->state == L2T_STATE_SWITCHING) {
|
||||||
|
if (ether_addr_equal(e->dmac, dmac) &&
|
||||||
|
(e->vlan == vlan) && (e->lport == port))
|
||||||
|
goto exists;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (first_free) {
|
||||||
|
e = first_free;
|
||||||
|
goto found;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
found:
|
||||||
|
/* The entry we found may be an inactive entry that is
|
||||||
|
* presently in the hash table. We need to remove it.
|
||||||
|
*/
|
||||||
|
if (e->state < L2T_STATE_SWITCHING)
|
||||||
|
for (p = &d->l2tab[e->hash].first; *p; p = &(*p)->next)
|
||||||
|
if (*p == e) {
|
||||||
|
*p = e->next;
|
||||||
|
e->next = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
e->state = L2T_STATE_UNUSED;
|
||||||
|
|
||||||
|
exists:
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Called when an L2T entry has no more users. The entry is left in the hash
|
||||||
|
* table since it is likely to be reused but we also bump nfree to indicate
|
||||||
|
* that the entry can be reallocated for a different neighbor. We also drop
|
||||||
|
* the existing neighbor reference in case the neighbor is going away and is
|
||||||
|
* waiting on our reference.
|
||||||
|
*
|
||||||
|
* Because entries can be reallocated to other neighbors once their ref count
|
||||||
|
* drops to 0 we need to take the entry's lock to avoid races with a new
|
||||||
|
* incarnation.
|
||||||
*/
|
*/
|
||||||
|
static void _t4_l2e_free(struct l2t_entry *e)
|
||||||
|
{
|
||||||
|
struct l2t_data *d;
|
||||||
|
|
||||||
|
if (atomic_read(&e->refcnt) == 0) { /* hasn't been recycled */
|
||||||
|
if (e->neigh) {
|
||||||
|
neigh_release(e->neigh);
|
||||||
|
e->neigh = NULL;
|
||||||
|
}
|
||||||
|
while (e->arpq_head) {
|
||||||
|
struct sk_buff *skb = e->arpq_head;
|
||||||
|
|
||||||
|
e->arpq_head = skb->next;
|
||||||
|
kfree_skb(skb);
|
||||||
|
}
|
||||||
|
e->arpq_tail = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
d = container_of(e, struct l2t_data, l2tab[e->idx]);
|
||||||
|
atomic_inc(&d->nfree);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Locked version of _t4_l2e_free */
|
||||||
static void t4_l2e_free(struct l2t_entry *e)
|
static void t4_l2e_free(struct l2t_entry *e)
|
||||||
{
|
{
|
||||||
struct l2t_data *d;
|
struct l2t_data *d;
|
||||||
|
@ -529,33 +602,57 @@ void t4_l2t_update(struct adapter *adap, struct neighbour *neigh)
|
||||||
* explicitly freed and while busy they are not on any hash chain, so normal
|
* explicitly freed and while busy they are not on any hash chain, so normal
|
||||||
* address resolution updates do not see them.
|
* address resolution updates do not see them.
|
||||||
*/
|
*/
|
||||||
struct l2t_entry *t4_l2t_alloc_switching(struct l2t_data *d)
|
struct l2t_entry *t4_l2t_alloc_switching(struct adapter *adap, u16 vlan,
|
||||||
|
u8 port, u8 *eth_addr)
|
||||||
{
|
{
|
||||||
|
struct l2t_data *d = adap->l2t;
|
||||||
struct l2t_entry *e;
|
struct l2t_entry *e;
|
||||||
|
int ret;
|
||||||
|
|
||||||
write_lock_bh(&d->lock);
|
write_lock_bh(&d->lock);
|
||||||
e = alloc_l2e(d);
|
e = find_or_alloc_l2e(d, vlan, port, eth_addr);
|
||||||
if (e) {
|
if (e) {
|
||||||
spin_lock(&e->lock); /* avoid race with t4_l2t_free */
|
spin_lock(&e->lock); /* avoid race with t4_l2t_free */
|
||||||
e->state = L2T_STATE_SWITCHING;
|
if (!atomic_read(&e->refcnt)) {
|
||||||
atomic_set(&e->refcnt, 1);
|
e->state = L2T_STATE_SWITCHING;
|
||||||
|
e->vlan = vlan;
|
||||||
|
e->lport = port;
|
||||||
|
ether_addr_copy(e->dmac, eth_addr);
|
||||||
|
atomic_set(&e->refcnt, 1);
|
||||||
|
ret = write_l2e(adap, e, 0);
|
||||||
|
if (ret < 0) {
|
||||||
|
_t4_l2e_free(e);
|
||||||
|
spin_unlock(&e->lock);
|
||||||
|
write_unlock_bh(&d->lock);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
atomic_inc(&e->refcnt);
|
||||||
|
}
|
||||||
|
|
||||||
spin_unlock(&e->lock);
|
spin_unlock(&e->lock);
|
||||||
}
|
}
|
||||||
write_unlock_bh(&d->lock);
|
write_unlock_bh(&d->lock);
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Sets/updates the contents of a switching L2T entry that has been allocated
|
/**
|
||||||
* with an earlier call to @t4_l2t_alloc_switching.
|
* @dev: net_device pointer
|
||||||
|
* @vlan: VLAN Id
|
||||||
|
* @port: Associated port
|
||||||
|
* @dmac: Destination MAC address to add to L2T
|
||||||
|
* Returns pointer to the allocated l2t entry
|
||||||
|
*
|
||||||
|
* Allocates an L2T entry for use by switching rule of a filter
|
||||||
*/
|
*/
|
||||||
int t4_l2t_set_switching(struct adapter *adap, struct l2t_entry *e, u16 vlan,
|
struct l2t_entry *cxgb4_l2t_alloc_switching(struct net_device *dev, u16 vlan,
|
||||||
u8 port, u8 *eth_addr)
|
u8 port, u8 *dmac)
|
||||||
{
|
{
|
||||||
e->vlan = vlan;
|
struct adapter *adap = netdev2adap(dev);
|
||||||
e->lport = port;
|
|
||||||
memcpy(e->dmac, eth_addr, ETH_ALEN);
|
return t4_l2t_alloc_switching(adap, vlan, port, dmac);
|
||||||
return write_l2e(adap, e, 0);
|
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(cxgb4_l2t_alloc_switching);
|
||||||
|
|
||||||
struct l2t_data *t4_init_l2t(unsigned int l2t_start, unsigned int l2t_end)
|
struct l2t_data *t4_init_l2t(unsigned int l2t_start, unsigned int l2t_end)
|
||||||
{
|
{
|
||||||
|
|
|
@ -114,10 +114,11 @@ struct l2t_entry *cxgb4_l2t_get(struct l2t_data *d, struct neighbour *neigh,
|
||||||
unsigned int priority);
|
unsigned int priority);
|
||||||
u64 cxgb4_select_ntuple(struct net_device *dev,
|
u64 cxgb4_select_ntuple(struct net_device *dev,
|
||||||
const struct l2t_entry *l2t);
|
const struct l2t_entry *l2t);
|
||||||
|
struct l2t_entry *cxgb4_l2t_alloc_switching(struct net_device *dev, u16 vlan,
|
||||||
|
u8 port, u8 *dmac);
|
||||||
void t4_l2t_update(struct adapter *adap, struct neighbour *neigh);
|
void t4_l2t_update(struct adapter *adap, struct neighbour *neigh);
|
||||||
struct l2t_entry *t4_l2t_alloc_switching(struct l2t_data *d);
|
struct l2t_entry *t4_l2t_alloc_switching(struct adapter *adap, u16 vlan,
|
||||||
int t4_l2t_set_switching(struct adapter *adap, struct l2t_entry *e, u16 vlan,
|
u8 port, u8 *dmac);
|
||||||
u8 port, u8 *eth_addr);
|
|
||||||
struct l2t_data *t4_init_l2t(unsigned int l2t_start, unsigned int l2t_end);
|
struct l2t_data *t4_init_l2t(unsigned int l2t_start, unsigned int l2t_end);
|
||||||
void do_l2t_write_rpl(struct adapter *p, const struct cpl_l2t_write_rpl *rpl);
|
void do_l2t_write_rpl(struct adapter *p, const struct cpl_l2t_write_rpl *rpl);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue