diff options
author | Stephen Hemminger <shemminger@osdl.org> | 2006-12-07 18:08:17 -0500 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2006-12-08 20:19:20 -0500 |
commit | 3644f0cee77494190452de132e82245107939284 (patch) | |
tree | 7aeb1dd32c68e372cc2aaa9d26703dd238b48a53 | |
parent | eb991b39385c7b04923d701764a34694ec54b53d (diff) |
[NET]: Convert hh_lock to seqlock.
The hard header cache is in the main output path, so using
seqlock instead of reader/writer lock should reduce overhead.
Signed-off-by: Stephen Hemminger <shemminger@osdl.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/linux/netdevice.h | 2 | ||||
-rw-r--r-- | include/net/neighbour.h | 18 | ||||
-rw-r--r-- | net/core/neighbour.c | 11 | ||||
-rw-r--r-- | net/ipv4/ip_output.c | 14 | ||||
-rw-r--r-- | net/ipv6/ip6_output.c | 17 |
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 | ||
312 | static 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 | |||
312 | static inline struct neighbour * | 330 | static 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); | |||
164 | static inline int ip_finish_output2(struct sk_buff *skb) | 164 | static 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 | ||
73 | static inline int ip6_output_finish(struct sk_buff *skb) | 73 | static 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); |