aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/netdevice.h6
-rw-r--r--net/core/dst.c4
-rw-r--r--net/core/neighbour.c99
3 files changed, 69 insertions, 40 deletions
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 6abcef67b178..4160db3721ba 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -281,6 +281,12 @@ struct hh_cache {
281 unsigned long hh_data[HH_DATA_ALIGN(LL_MAX_HEADER) / sizeof(long)]; 281 unsigned long hh_data[HH_DATA_ALIGN(LL_MAX_HEADER) / sizeof(long)];
282}; 282};
283 283
284static inline void hh_cache_put(struct hh_cache *hh)
285{
286 if (atomic_dec_and_test(&hh->hh_refcnt))
287 kfree(hh);
288}
289
284/* Reserve HH_DATA_MOD byte aligned hard_header_len, but at least that much. 290/* Reserve HH_DATA_MOD byte aligned hard_header_len, but at least that much.
285 * Alternative is: 291 * Alternative is:
286 * dev->hard_header_len ? (dev->hard_header_len + 292 * dev->hard_header_len ? (dev->hard_header_len +
diff --git a/net/core/dst.c b/net/core/dst.c
index 6c41b1fac3db..978a1ee1f7d0 100644
--- a/net/core/dst.c
+++ b/net/core/dst.c
@@ -228,8 +228,8 @@ again:
228 child = dst->child; 228 child = dst->child;
229 229
230 dst->hh = NULL; 230 dst->hh = NULL;
231 if (hh && atomic_dec_and_test(&hh->hh_refcnt)) 231 if (hh)
232 kfree(hh); 232 hh_cache_put(hh);
233 233
234 if (neigh) { 234 if (neigh) {
235 dst->neighbour = NULL; 235 dst->neighbour = NULL;
diff --git a/net/core/neighbour.c b/net/core/neighbour.c
index 3ffafaa0414c..2044906ecd1a 100644
--- a/net/core/neighbour.c
+++ b/net/core/neighbour.c
@@ -709,8 +709,7 @@ void neigh_destroy(struct neighbour *neigh)
709 write_seqlock_bh(&hh->hh_lock); 709 write_seqlock_bh(&hh->hh_lock);
710 hh->hh_output = neigh_blackhole; 710 hh->hh_output = neigh_blackhole;
711 write_sequnlock_bh(&hh->hh_lock); 711 write_sequnlock_bh(&hh->hh_lock);
712 if (atomic_dec_and_test(&hh->hh_refcnt)) 712 hh_cache_put(hh);
713 kfree(hh);
714 } 713 }
715 714
716 skb_queue_purge(&neigh->arp_queue); 715 skb_queue_purge(&neigh->arp_queue);
@@ -1210,39 +1209,67 @@ struct neighbour *neigh_event_ns(struct neigh_table *tbl,
1210} 1209}
1211EXPORT_SYMBOL(neigh_event_ns); 1210EXPORT_SYMBOL(neigh_event_ns);
1212 1211
1212static inline bool neigh_hh_lookup(struct neighbour *n, struct dst_entry *dst,
1213 __be16 protocol)
1214{
1215 struct hh_cache *hh;
1216
1217 for (hh = n->hh; hh; hh = hh->hh_next) {
1218 if (hh->hh_type == protocol) {
1219 atomic_inc(&hh->hh_refcnt);
1220 if (unlikely(cmpxchg(&dst->hh, NULL, hh) != NULL))
1221 hh_cache_put(hh);
1222 return true;
1223 }
1224 }
1225 return false;
1226}
1227
1228/* called with read_lock_bh(&n->lock); */
1213static void neigh_hh_init(struct neighbour *n, struct dst_entry *dst, 1229static void neigh_hh_init(struct neighbour *n, struct dst_entry *dst,
1214 __be16 protocol) 1230 __be16 protocol)
1215{ 1231{
1216 struct hh_cache *hh; 1232 struct hh_cache *hh;
1217 struct net_device *dev = dst->dev; 1233 struct net_device *dev = dst->dev;
1218 1234
1219 for (hh = n->hh; hh; hh = hh->hh_next) 1235 if (likely(neigh_hh_lookup(n, dst, protocol)))
1220 if (hh->hh_type == protocol) 1236 return;
1221 break;
1222 1237
1223 if (!hh && (hh = kzalloc(sizeof(*hh), GFP_ATOMIC)) != NULL) { 1238 /* slow path */
1224 seqlock_init(&hh->hh_lock); 1239 hh = kzalloc(sizeof(*hh), GFP_ATOMIC);
1225 hh->hh_type = protocol; 1240 if (!hh)
1226 atomic_set(&hh->hh_refcnt, 0); 1241 return;
1227 hh->hh_next = NULL;
1228 1242
1229 if (dev->header_ops->cache(n, hh)) { 1243 seqlock_init(&hh->hh_lock);
1230 kfree(hh); 1244 hh->hh_type = protocol;
1231 hh = NULL; 1245 atomic_set(&hh->hh_refcnt, 2);
1232 } else { 1246
1233 atomic_inc(&hh->hh_refcnt); 1247 if (dev->header_ops->cache(n, hh)) {
1234 hh->hh_next = n->hh; 1248 kfree(hh);
1235 n->hh = hh; 1249 return;
1236 if (n->nud_state & NUD_CONNECTED)
1237 hh->hh_output = n->ops->hh_output;
1238 else
1239 hh->hh_output = n->ops->output;
1240 }
1241 } 1250 }
1242 if (hh) { 1251 read_unlock(&n->lock);
1243 atomic_inc(&hh->hh_refcnt); 1252 write_lock(&n->lock);
1244 dst->hh = hh; 1253
1254 /* must check if another thread already did the insert */
1255 if (neigh_hh_lookup(n, dst, protocol)) {
1256 kfree(hh);
1257 goto end;
1245 } 1258 }
1259
1260 if (n->nud_state & NUD_CONNECTED)
1261 hh->hh_output = n->ops->hh_output;
1262 else
1263 hh->hh_output = n->ops->output;
1264
1265 hh->hh_next = n->hh;
1266 n->hh = hh;
1267
1268 if (unlikely(cmpxchg(&dst->hh, NULL, hh) != NULL))
1269 hh_cache_put(hh);
1270end:
1271 write_unlock(&n->lock);
1272 read_lock(&n->lock);
1246} 1273}
1247 1274
1248/* This function can be used in contexts, where only old dev_queue_xmit 1275/* This function can be used in contexts, where only old dev_queue_xmit
@@ -1281,21 +1308,17 @@ int neigh_resolve_output(struct sk_buff *skb)
1281 if (!neigh_event_send(neigh, skb)) { 1308 if (!neigh_event_send(neigh, skb)) {
1282 int err; 1309 int err;
1283 struct net_device *dev = neigh->dev; 1310 struct net_device *dev = neigh->dev;
1311
1312 read_lock_bh(&neigh->lock);
1284 if (dev->header_ops->cache && 1313 if (dev->header_ops->cache &&
1285 !dst->hh && 1314 !dst->hh &&
1286 !(dst->flags & DST_NOCACHE)) { 1315 !(dst->flags & DST_NOCACHE))
1287 write_lock_bh(&neigh->lock); 1316 neigh_hh_init(neigh, dst, dst->ops->protocol);
1288 if (!dst->hh) 1317
1289 neigh_hh_init(neigh, dst, dst->ops->protocol); 1318 err = dev_hard_header(skb, dev, ntohs(skb->protocol),
1290 err = dev_hard_header(skb, dev, ntohs(skb->protocol), 1319 neigh->ha, NULL, skb->len);
1291 neigh->ha, NULL, skb->len); 1320 read_unlock_bh(&neigh->lock);
1292 write_unlock_bh(&neigh->lock); 1321
1293 } else {
1294 read_lock_bh(&neigh->lock);
1295 err = dev_hard_header(skb, dev, ntohs(skb->protocol),
1296 neigh->ha, NULL, skb->len);
1297 read_unlock_bh(&neigh->lock);
1298 }
1299 if (err >= 0) 1322 if (err >= 0)
1300 rc = neigh->ops->queue_xmit(skb); 1323 rc = neigh->ops->queue_xmit(skb);
1301 else 1324 else