Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 215133
b: refs/heads/master
c: 34d101d
h: refs/heads/master
i:
  215131: 859fbb7
v: v3
  • Loading branch information
Eric Dumazet authored and David S. Miller committed Oct 11, 2010
1 parent d36cdc9 commit 3bf53ab
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 41 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 37f9fc452d138dfc4da2ee1ce5ae85094efc3606
refs/heads/master: 34d101dd6204bd100fc2e6f7b5f9a10f959ce2c9
6 changes: 6 additions & 0 deletions trunk/include/linux/netdevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,12 @@ struct hh_cache {
unsigned long hh_data[HH_DATA_ALIGN(LL_MAX_HEADER) / sizeof(long)];
};

static inline void hh_cache_put(struct hh_cache *hh)
{
if (atomic_dec_and_test(&hh->hh_refcnt))
kfree(hh);
}

/* Reserve HH_DATA_MOD byte aligned hard_header_len, but at least that much.
* Alternative is:
* dev->hard_header_len ? (dev->hard_header_len +
Expand Down
4 changes: 2 additions & 2 deletions trunk/net/core/dst.c
Original file line number Diff line number Diff line change
Expand Up @@ -228,8 +228,8 @@ struct dst_entry *dst_destroy(struct dst_entry * dst)
child = dst->child;

dst->hh = NULL;
if (hh && atomic_dec_and_test(&hh->hh_refcnt))
kfree(hh);
if (hh)
hh_cache_put(hh);

if (neigh) {
dst->neighbour = NULL;
Expand Down
99 changes: 61 additions & 38 deletions trunk/net/core/neighbour.c
Original file line number Diff line number Diff line change
Expand Up @@ -709,8 +709,7 @@ void neigh_destroy(struct neighbour *neigh)
write_seqlock_bh(&hh->hh_lock);
hh->hh_output = neigh_blackhole;
write_sequnlock_bh(&hh->hh_lock);
if (atomic_dec_and_test(&hh->hh_refcnt))
kfree(hh);
hh_cache_put(hh);
}

skb_queue_purge(&neigh->arp_queue);
Expand Down Expand Up @@ -1210,39 +1209,67 @@ struct neighbour *neigh_event_ns(struct neigh_table *tbl,
}
EXPORT_SYMBOL(neigh_event_ns);

static inline bool neigh_hh_lookup(struct neighbour *n, struct dst_entry *dst,
__be16 protocol)
{
struct hh_cache *hh;

for (hh = n->hh; hh; hh = hh->hh_next) {
if (hh->hh_type == protocol) {
atomic_inc(&hh->hh_refcnt);
if (unlikely(cmpxchg(&dst->hh, NULL, hh) != NULL))
hh_cache_put(hh);
return true;
}
}
return false;
}

/* called with read_lock_bh(&n->lock); */
static void neigh_hh_init(struct neighbour *n, struct dst_entry *dst,
__be16 protocol)
{
struct hh_cache *hh;
struct net_device *dev = dst->dev;

for (hh = n->hh; hh; hh = hh->hh_next)
if (hh->hh_type == protocol)
break;
if (likely(neigh_hh_lookup(n, dst, protocol)))
return;

if (!hh && (hh = kzalloc(sizeof(*hh), GFP_ATOMIC)) != NULL) {
seqlock_init(&hh->hh_lock);
hh->hh_type = protocol;
atomic_set(&hh->hh_refcnt, 0);
hh->hh_next = NULL;
/* slow path */
hh = kzalloc(sizeof(*hh), GFP_ATOMIC);
if (!hh)
return;

if (dev->header_ops->cache(n, hh)) {
kfree(hh);
hh = NULL;
} else {
atomic_inc(&hh->hh_refcnt);
hh->hh_next = n->hh;
n->hh = hh;
if (n->nud_state & NUD_CONNECTED)
hh->hh_output = n->ops->hh_output;
else
hh->hh_output = n->ops->output;
}
seqlock_init(&hh->hh_lock);
hh->hh_type = protocol;
atomic_set(&hh->hh_refcnt, 2);

if (dev->header_ops->cache(n, hh)) {
kfree(hh);
return;
}
if (hh) {
atomic_inc(&hh->hh_refcnt);
dst->hh = hh;
read_unlock(&n->lock);
write_lock(&n->lock);

/* must check if another thread already did the insert */
if (neigh_hh_lookup(n, dst, protocol)) {
kfree(hh);
goto end;
}

if (n->nud_state & NUD_CONNECTED)
hh->hh_output = n->ops->hh_output;
else
hh->hh_output = n->ops->output;

hh->hh_next = n->hh;
n->hh = hh;

if (unlikely(cmpxchg(&dst->hh, NULL, hh) != NULL))
hh_cache_put(hh);
end:
write_unlock(&n->lock);
read_lock(&n->lock);
}

/* This function can be used in contexts, where only old dev_queue_xmit
Expand Down Expand Up @@ -1281,21 +1308,17 @@ int neigh_resolve_output(struct sk_buff *skb)
if (!neigh_event_send(neigh, skb)) {
int err;
struct net_device *dev = neigh->dev;

read_lock_bh(&neigh->lock);
if (dev->header_ops->cache &&
!dst->hh &&
!(dst->flags & DST_NOCACHE)) {
write_lock_bh(&neigh->lock);
if (!dst->hh)
neigh_hh_init(neigh, dst, dst->ops->protocol);
err = dev_hard_header(skb, dev, ntohs(skb->protocol),
neigh->ha, NULL, skb->len);
write_unlock_bh(&neigh->lock);
} else {
read_lock_bh(&neigh->lock);
err = dev_hard_header(skb, dev, ntohs(skb->protocol),
neigh->ha, NULL, skb->len);
read_unlock_bh(&neigh->lock);
}
!(dst->flags & DST_NOCACHE))
neigh_hh_init(neigh, dst, dst->ops->protocol);

err = dev_hard_header(skb, dev, ntohs(skb->protocol),
neigh->ha, NULL, skb->len);
read_unlock_bh(&neigh->lock);

if (err >= 0)
rc = neigh->ops->queue_xmit(skb);
else
Expand Down

0 comments on commit 3bf53ab

Please sign in to comment.