diff options
Diffstat (limited to 'net/llc/llc_sap.c')
-rw-r--r-- | net/llc/llc_sap.c | 111 |
1 files changed, 82 insertions, 29 deletions
diff --git a/net/llc/llc_sap.c b/net/llc/llc_sap.c index 008de1fc42ca..ad6e6e1cf22f 100644 --- a/net/llc/llc_sap.c +++ b/net/llc/llc_sap.c | |||
@@ -297,6 +297,17 @@ static void llc_sap_rcv(struct llc_sap *sap, struct sk_buff *skb, | |||
297 | llc_sap_state_process(sap, skb); | 297 | llc_sap_state_process(sap, skb); |
298 | } | 298 | } |
299 | 299 | ||
300 | static inline bool llc_dgram_match(const struct llc_sap *sap, | ||
301 | const struct llc_addr *laddr, | ||
302 | const struct sock *sk) | ||
303 | { | ||
304 | struct llc_sock *llc = llc_sk(sk); | ||
305 | |||
306 | return sk->sk_type == SOCK_DGRAM && | ||
307 | llc->laddr.lsap == laddr->lsap && | ||
308 | llc_mac_match(llc->laddr.mac, laddr->mac); | ||
309 | } | ||
310 | |||
300 | /** | 311 | /** |
301 | * llc_lookup_dgram - Finds dgram socket for the local sap/mac | 312 | * llc_lookup_dgram - Finds dgram socket for the local sap/mac |
302 | * @sap: SAP | 313 | * @sap: SAP |
@@ -309,25 +320,68 @@ static struct sock *llc_lookup_dgram(struct llc_sap *sap, | |||
309 | const struct llc_addr *laddr) | 320 | const struct llc_addr *laddr) |
310 | { | 321 | { |
311 | struct sock *rc; | 322 | struct sock *rc; |
312 | struct hlist_node *node; | 323 | struct hlist_nulls_node *node; |
313 | 324 | int slot = llc_sk_laddr_hashfn(sap, laddr); | |
314 | read_lock_bh(&sap->sk_list.lock); | 325 | struct hlist_nulls_head *laddr_hb = &sap->sk_laddr_hash[slot]; |
315 | sk_for_each(rc, node, &sap->sk_list.list) { | 326 | |
316 | struct llc_sock *llc = llc_sk(rc); | 327 | rcu_read_lock_bh(); |
317 | 328 | again: | |
318 | if (rc->sk_type == SOCK_DGRAM && | 329 | sk_nulls_for_each_rcu(rc, node, laddr_hb) { |
319 | llc->laddr.lsap == laddr->lsap && | 330 | if (llc_dgram_match(sap, laddr, rc)) { |
320 | llc_mac_match(llc->laddr.mac, laddr->mac)) { | 331 | /* Extra checks required by SLAB_DESTROY_BY_RCU */ |
321 | sock_hold(rc); | 332 | if (unlikely(!atomic_inc_not_zero(&rc->sk_refcnt))) |
333 | goto again; | ||
334 | if (unlikely(llc_sk(rc)->sap != sap || | ||
335 | !llc_dgram_match(sap, laddr, rc))) { | ||
336 | sock_put(rc); | ||
337 | continue; | ||
338 | } | ||
322 | goto found; | 339 | goto found; |
323 | } | 340 | } |
324 | } | 341 | } |
325 | rc = NULL; | 342 | rc = NULL; |
343 | /* | ||
344 | * if the nulls value we got at the end of this lookup is | ||
345 | * not the expected one, we must restart lookup. | ||
346 | * We probably met an item that was moved to another chain. | ||
347 | */ | ||
348 | if (unlikely(get_nulls_value(node) != slot)) | ||
349 | goto again; | ||
326 | found: | 350 | found: |
327 | read_unlock_bh(&sap->sk_list.lock); | 351 | rcu_read_unlock_bh(); |
328 | return rc; | 352 | return rc; |
329 | } | 353 | } |
330 | 354 | ||
355 | static inline bool llc_mcast_match(const struct llc_sap *sap, | ||
356 | const struct llc_addr *laddr, | ||
357 | const struct sk_buff *skb, | ||
358 | const struct sock *sk) | ||
359 | { | ||
360 | struct llc_sock *llc = llc_sk(sk); | ||
361 | |||
362 | return sk->sk_type == SOCK_DGRAM && | ||
363 | llc->laddr.lsap == laddr->lsap && | ||
364 | llc->dev == skb->dev; | ||
365 | } | ||
366 | |||
367 | static void llc_do_mcast(struct llc_sap *sap, struct sk_buff *skb, | ||
368 | struct sock **stack, int count) | ||
369 | { | ||
370 | struct sk_buff *skb1; | ||
371 | int i; | ||
372 | |||
373 | for (i = 0; i < count; i++) { | ||
374 | skb1 = skb_clone(skb, GFP_ATOMIC); | ||
375 | if (!skb1) { | ||
376 | sock_put(stack[i]); | ||
377 | continue; | ||
378 | } | ||
379 | |||
380 | llc_sap_rcv(sap, skb1, stack[i]); | ||
381 | sock_put(stack[i]); | ||
382 | } | ||
383 | } | ||
384 | |||
331 | /** | 385 | /** |
332 | * llc_sap_mcast - Deliver multicast PDU's to all matching datagram sockets. | 386 | * llc_sap_mcast - Deliver multicast PDU's to all matching datagram sockets. |
333 | * @sap: SAP | 387 | * @sap: SAP |
@@ -340,32 +394,31 @@ static void llc_sap_mcast(struct llc_sap *sap, | |||
340 | const struct llc_addr *laddr, | 394 | const struct llc_addr *laddr, |
341 | struct sk_buff *skb) | 395 | struct sk_buff *skb) |
342 | { | 396 | { |
343 | struct sock *sk; | 397 | int i = 0, count = 256 / sizeof(struct sock *); |
398 | struct sock *sk, *stack[count]; | ||
344 | struct hlist_node *node; | 399 | struct hlist_node *node; |
400 | struct llc_sock *llc; | ||
401 | struct hlist_head *dev_hb = llc_sk_dev_hash(sap, skb->dev->ifindex); | ||
345 | 402 | ||
346 | read_lock_bh(&sap->sk_list.lock); | 403 | spin_lock_bh(&sap->sk_lock); |
347 | sk_for_each(sk, node, &sap->sk_list.list) { | 404 | hlist_for_each_entry(llc, node, dev_hb, dev_hash_node) { |
348 | struct llc_sock *llc = llc_sk(sk); | ||
349 | struct sk_buff *skb1; | ||
350 | 405 | ||
351 | if (sk->sk_type != SOCK_DGRAM) | 406 | sk = &llc->sk; |
352 | continue; | ||
353 | 407 | ||
354 | if (llc->laddr.lsap != laddr->lsap) | 408 | if (!llc_mcast_match(sap, laddr, skb, sk)) |
355 | continue; | 409 | continue; |
356 | 410 | ||
357 | if (llc->dev != skb->dev) | ||
358 | continue; | ||
359 | |||
360 | skb1 = skb_clone(skb, GFP_ATOMIC); | ||
361 | if (!skb1) | ||
362 | break; | ||
363 | |||
364 | sock_hold(sk); | 411 | sock_hold(sk); |
365 | llc_sap_rcv(sap, skb1, sk); | 412 | if (i < count) |
366 | sock_put(sk); | 413 | stack[i++] = sk; |
414 | else { | ||
415 | llc_do_mcast(sap, skb, stack, i); | ||
416 | i = 0; | ||
417 | } | ||
367 | } | 418 | } |
368 | read_unlock_bh(&sap->sk_list.lock); | 419 | spin_unlock_bh(&sap->sk_lock); |
420 | |||
421 | llc_do_mcast(sap, skb, stack, i); | ||
369 | } | 422 | } |
370 | 423 | ||
371 | 424 | ||