diff options
author | Eric Dumazet <eric.dumazet@gmail.com> | 2010-10-07 06:44:07 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-10-11 15:54:04 -0400 |
commit | 0ed8ddf4045fcfcac36bad753dc4046118c603ec (patch) | |
tree | cf1d9eb14668c4d2257b3519ed7deec8c5cb396d /include/net/neighbour.h | |
parent | d122179a3c0fdc71b88cb9e3605f372b1651a9ff (diff) |
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/net/neighbour.h')
-rw-r--r-- | include/net/neighbour.h | 16 |
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 { | |||
105 | atomic_t refcnt; | 105 | atomic_t refcnt; |
106 | atomic_t probes; | 106 | atomic_t probes; |
107 | rwlock_t lock; | 107 | rwlock_t lock; |
108 | seqlock_t ha_lock; | ||
108 | unsigned char ha[ALIGN(MAX_ADDR_LEN, sizeof(unsigned long))]; | 109 | unsigned char ha[ALIGN(MAX_ADDR_LEN, sizeof(unsigned long))]; |
109 | struct hh_cache *hh; | 110 | struct hh_cache *hh; |
110 | int (*output)(struct sk_buff *skb); | 111 | int (*output)(struct sk_buff *skb); |
@@ -302,7 +303,10 @@ static inline void neigh_confirm(struct neighbour *neigh) | |||
302 | 303 | ||
303 | static inline int neigh_event_send(struct neighbour *neigh, struct sk_buff *skb) | 304 | static inline int neigh_event_send(struct neighbour *neigh, struct sk_buff *skb) |
304 | { | 305 | { |
305 | neigh->used = jiffies; | 306 | unsigned long now = ACCESS_ONCE(jiffies); |
307 | |||
308 | if (neigh->used != now) | ||
309 | neigh->used = now; | ||
306 | if (!(neigh->nud_state&(NUD_CONNECTED|NUD_DELAY|NUD_PROBE))) | 310 | if (!(neigh->nud_state&(NUD_CONNECTED|NUD_DELAY|NUD_PROBE))) |
307 | return __neigh_event_send(neigh, skb); | 311 | return __neigh_event_send(neigh, skb); |
308 | return 0; | 312 | return 0; |
@@ -373,4 +377,14 @@ struct neighbour_cb { | |||
373 | 377 | ||
374 | #define NEIGH_CB(skb) ((struct neighbour_cb *)(skb)->cb) | 378 | #define NEIGH_CB(skb) ((struct neighbour_cb *)(skb)->cb) |
375 | 379 | ||
380 | static inline void neigh_ha_snapshot(char *dst, const struct neighbour *n, | ||
381 | const struct net_device *dev) | ||
382 | { | ||
383 | unsigned int seq; | ||
384 | |||
385 | do { | ||
386 | seq = read_seqbegin(&n->ha_lock); | ||
387 | memcpy(dst, n->ha, dev->addr_len); | ||
388 | } while (read_seqretry(&n->ha_lock, seq)); | ||
389 | } | ||
376 | #endif | 390 | #endif |