aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/netdevice.h2
-rw-r--r--include/net/neighbour.h18
-rw-r--r--net/core/neighbour.c11
-rw-r--r--net/ipv4/ip_output.c14
-rw-r--r--net/ipv6/ip6_output.c17
5 files changed, 32 insertions, 30 deletions
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index c57088f575a3..631cec4ff5e1 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -199,7 +199,7 @@ struct hh_cache
199 */ 199 */
200 u16 hh_len; /* length of header */ 200 u16 hh_len; /* length of header */
201 int (*hh_output)(struct sk_buff *skb); 201 int (*hh_output)(struct sk_buff *skb);
202 rwlock_t hh_lock; 202 seqlock_t hh_lock;
203 203
204 /* cached hardware header; allow for machine alignment needs. */ 204 /* cached hardware header; allow for machine alignment needs. */
205#define HH_DATA_MOD 16 205#define HH_DATA_MOD 16
diff --git a/include/net/neighbour.h b/include/net/neighbour.h
index 23967031ddb7..3725b93c52f3 100644
--- a/include/net/neighbour.h
+++ b/include/net/neighbour.h
@@ -309,6 +309,24 @@ static inline int neigh_event_send(struct neighbour *neigh, struct sk_buff *skb)
309 return 0; 309 return 0;
310} 310}
311 311
312static inline int neigh_hh_output(struct hh_cache *hh, struct sk_buff *skb)
313{
314 unsigned seq;
315 int hh_len;
316
317 do {
318 int hh_alen;
319
320 seq = read_seqbegin(&hh->hh_lock);
321 hh_len = hh->hh_len;
322 hh_alen = HH_DATA_ALIGN(hh_len);
323 memcpy(skb->data - hh_alen, hh->hh_data, hh_alen);
324 } while (read_seqretry(&hh->hh_lock, seq));
325
326 skb_push(skb, hh_len);
327 return hh->hh_output(skb);
328}
329
312static inline struct neighbour * 330static inline struct neighbour *
313__neigh_lookup(struct neigh_table *tbl, const void *pkey, struct net_device *dev, int creat) 331__neigh_lookup(struct neigh_table *tbl, const void *pkey, struct net_device *dev, int creat)
314{ 332{
diff --git a/net/core/neighbour.c b/net/core/neighbour.c
index 0ab1987b9348..e7300b6b4079 100644
--- a/net/core/neighbour.c
+++ b/net/core/neighbour.c
@@ -577,9 +577,10 @@ void neigh_destroy(struct neighbour *neigh)
577 while ((hh = neigh->hh) != NULL) { 577 while ((hh = neigh->hh) != NULL) {
578 neigh->hh = hh->hh_next; 578 neigh->hh = hh->hh_next;
579 hh->hh_next = NULL; 579 hh->hh_next = NULL;
580 write_lock_bh(&hh->hh_lock); 580
581 write_seqlock_bh(&hh->hh_lock);
581 hh->hh_output = neigh_blackhole; 582 hh->hh_output = neigh_blackhole;
582 write_unlock_bh(&hh->hh_lock); 583 write_sequnlock_bh(&hh->hh_lock);
583 if (atomic_dec_and_test(&hh->hh_refcnt)) 584 if (atomic_dec_and_test(&hh->hh_refcnt))
584 kfree(hh); 585 kfree(hh);
585 } 586 }
@@ -897,9 +898,9 @@ static void neigh_update_hhs(struct neighbour *neigh)
897 898
898 if (update) { 899 if (update) {
899 for (hh = neigh->hh; hh; hh = hh->hh_next) { 900 for (hh = neigh->hh; hh; hh = hh->hh_next) {
900 write_lock_bh(&hh->hh_lock); 901 write_seqlock_bh(&hh->hh_lock);
901 update(hh, neigh->dev, neigh->ha); 902 update(hh, neigh->dev, neigh->ha);
902 write_unlock_bh(&hh->hh_lock); 903 write_sequnlock_bh(&hh->hh_lock);
903 } 904 }
904 } 905 }
905} 906}
@@ -1089,7 +1090,7 @@ static void neigh_hh_init(struct neighbour *n, struct dst_entry *dst,
1089 break; 1090 break;
1090 1091
1091 if (!hh && (hh = kzalloc(sizeof(*hh), GFP_ATOMIC)) != NULL) { 1092 if (!hh && (hh = kzalloc(sizeof(*hh), GFP_ATOMIC)) != NULL) {
1092 rwlock_init(&hh->hh_lock); 1093 seqlock_init(&hh->hh_lock);
1093 hh->hh_type = protocol; 1094 hh->hh_type = protocol;
1094 atomic_set(&hh->hh_refcnt, 0); 1095 atomic_set(&hh->hh_refcnt, 0);
1095 hh->hh_next = NULL; 1096 hh->hh_next = NULL;
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
index a35209d517ad..f071f84808fa 100644
--- a/net/ipv4/ip_output.c
+++ b/net/ipv4/ip_output.c
@@ -164,7 +164,6 @@ EXPORT_SYMBOL_GPL(ip_build_and_send_pkt);
164static inline int ip_finish_output2(struct sk_buff *skb) 164static inline int ip_finish_output2(struct sk_buff *skb)
165{ 165{
166 struct dst_entry *dst = skb->dst; 166 struct dst_entry *dst = skb->dst;
167 struct hh_cache *hh = dst->hh;
168 struct net_device *dev = dst->dev; 167 struct net_device *dev = dst->dev;
169 int hh_len = LL_RESERVED_SPACE(dev); 168 int hh_len = LL_RESERVED_SPACE(dev);
170 169
@@ -183,16 +182,9 @@ static inline int ip_finish_output2(struct sk_buff *skb)
183 skb = skb2; 182 skb = skb2;
184 } 183 }
185 184
186 if (hh) { 185 if (dst->hh)
187 int hh_alen; 186 return neigh_hh_output(dst->hh, skb);
188 187 else if (dst->neighbour)
189 read_lock_bh(&hh->hh_lock);
190 hh_alen = HH_DATA_ALIGN(hh->hh_len);
191 memcpy(skb->data - hh_alen, hh->hh_data, hh_alen);
192 read_unlock_bh(&hh->hh_lock);
193 skb_push(skb, hh->hh_len);
194 return hh->hh_output(skb);
195 } else if (dst->neighbour)
196 return dst->neighbour->output(skb); 188 return dst->neighbour->output(skb);
197 189
198 if (net_ratelimit()) 190 if (net_ratelimit())
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index e9212c7ff5cf..7b7bd44fbf47 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -72,20 +72,11 @@ static __inline__ void ipv6_select_ident(struct sk_buff *skb, struct frag_hdr *f
72 72
73static inline int ip6_output_finish(struct sk_buff *skb) 73static inline int ip6_output_finish(struct sk_buff *skb)
74{ 74{
75
76 struct dst_entry *dst = skb->dst; 75 struct dst_entry *dst = skb->dst;
77 struct hh_cache *hh = dst->hh; 76
78 77 if (dst->hh)
79 if (hh) { 78 return neigh_hh_output(dst->hh, skb);
80 int hh_alen; 79 else if (dst->neighbour)
81
82 read_lock_bh(&hh->hh_lock);
83 hh_alen = HH_DATA_ALIGN(hh->hh_len);
84 memcpy(skb->data - hh_alen, hh->hh_data, hh_alen);
85 read_unlock_bh(&hh->hh_lock);
86 skb_push(skb, hh->hh_len);
87 return hh->hh_output(skb);
88 } else if (dst->neighbour)
89 return dst->neighbour->output(skb); 80 return dst->neighbour->output(skb);
90 81
91 IP6_INC_STATS_BH(ip6_dst_idev(dst), IPSTATS_MIB_OUTNOROUTES); 82 IP6_INC_STATS_BH(ip6_dst_idev(dst), IPSTATS_MIB_OUTNOROUTES);