summaryrefslogtreecommitdiffstats
path: root/include
diff options
context:
space:
mode:
authorEric Dumazet <eric.dumazet@gmail.com>2010-10-07 10:44:07 +0000
committerDavid S. Miller <davem@davemloft.net>2010-10-11 12:54:04 -0700
commit0ed8ddf4045fcfcac36bad753dc4046118c603ec (patch)
treecf1d9eb14668c4d2257b3519ed7deec8c5cb396d /include
parentd122179a3c0fdc71b88cb9e3605f372b1651a9ff (diff)
downloadlinux-0ed8ddf4045fcfcac36bad753dc4046118c603ec.tar.bz2
neigh: Protect neigh->ha[] with a seqlock
Add a seqlock in struct neighbour to protect neigh->ha[], and avoid dirtying neighbour in stress situation (many different flows / dsts) Dirtying takes place because of read_lock(&n->lock) and n->used writes. Switching to a seqlock, and writing n->used only on jiffies changes permits less dirtying. Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'include')
-rw-r--r--include/net/neighbour.h16
1 files changed, 15 insertions, 1 deletions
diff --git a/include/net/neighbour.h b/include/net/neighbour.h
index a4538d553704..f04e7a2522c5 100644
--- a/include/net/neighbour.h
+++ b/include/net/neighbour.h
@@ -105,6 +105,7 @@ struct neighbour {
atomic_t refcnt;
atomic_t probes;
rwlock_t lock;
+ seqlock_t ha_lock;
unsigned char ha[ALIGN(MAX_ADDR_LEN, sizeof(unsigned long))];
struct hh_cache *hh;
int (*output)(struct sk_buff *skb);
@@ -302,7 +303,10 @@ static inline void neigh_confirm(struct neighbour *neigh)
static inline int neigh_event_send(struct neighbour *neigh, struct sk_buff *skb)
{
- neigh->used = jiffies;
+ unsigned long now = ACCESS_ONCE(jiffies);
+
+ if (neigh->used != now)
+ neigh->used = now;
if (!(neigh->nud_state&(NUD_CONNECTED|NUD_DELAY|NUD_PROBE)))
return __neigh_event_send(neigh, skb);
return 0;
@@ -373,4 +377,14 @@ struct neighbour_cb {
#define NEIGH_CB(skb) ((struct neighbour_cb *)(skb)->cb)
+static inline void neigh_ha_snapshot(char *dst, const struct neighbour *n,
+ const struct net_device *dev)
+{
+ unsigned int seq;
+
+ do {
+ seq = read_seqbegin(&n->ha_lock);
+ memcpy(dst, n->ha, dev->addr_len);
+ } while (read_seqretry(&n->ha_lock, seq));
+}
#endif