aboutsummaryrefslogtreecommitdiffstats
path: root/net/llc/llc_conn.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/llc/llc_conn.c')
-rw-r--r--net/llc/llc_conn.c147
1 files changed, 110 insertions, 37 deletions
diff --git a/net/llc/llc_conn.c b/net/llc/llc_conn.c
index c6bab39b018e..ba137a6a224d 100644
--- a/net/llc/llc_conn.c
+++ b/net/llc/llc_conn.c
@@ -13,6 +13,7 @@
13 */ 13 */
14 14
15#include <linux/init.h> 15#include <linux/init.h>
16#include <linux/slab.h>
16#include <net/llc_sap.h> 17#include <net/llc_sap.h>
17#include <net/llc_conn.h> 18#include <net/llc_conn.h>
18#include <net/sock.h> 19#include <net/sock.h>
@@ -468,6 +469,19 @@ static int llc_exec_conn_trans_actions(struct sock *sk,
468 return rc; 469 return rc;
469} 470}
470 471
472static inline bool llc_estab_match(const struct llc_sap *sap,
473 const struct llc_addr *daddr,
474 const struct llc_addr *laddr,
475 const struct sock *sk)
476{
477 struct llc_sock *llc = llc_sk(sk);
478
479 return llc->laddr.lsap == laddr->lsap &&
480 llc->daddr.lsap == daddr->lsap &&
481 llc_mac_match(llc->laddr.mac, laddr->mac) &&
482 llc_mac_match(llc->daddr.mac, daddr->mac);
483}
484
471/** 485/**
472 * __llc_lookup_established - Finds connection for the remote/local sap/mac 486 * __llc_lookup_established - Finds connection for the remote/local sap/mac
473 * @sap: SAP 487 * @sap: SAP
@@ -484,23 +498,35 @@ static struct sock *__llc_lookup_established(struct llc_sap *sap,
484 struct llc_addr *laddr) 498 struct llc_addr *laddr)
485{ 499{
486 struct sock *rc; 500 struct sock *rc;
487 struct hlist_node *node; 501 struct hlist_nulls_node *node;
488 502 int slot = llc_sk_laddr_hashfn(sap, laddr);
489 read_lock(&sap->sk_list.lock); 503 struct hlist_nulls_head *laddr_hb = &sap->sk_laddr_hash[slot];
490 sk_for_each(rc, node, &sap->sk_list.list) { 504
491 struct llc_sock *llc = llc_sk(rc); 505 rcu_read_lock();
492 506again:
493 if (llc->laddr.lsap == laddr->lsap && 507 sk_nulls_for_each_rcu(rc, node, laddr_hb) {
494 llc->daddr.lsap == daddr->lsap && 508 if (llc_estab_match(sap, daddr, laddr, rc)) {
495 llc_mac_match(llc->laddr.mac, laddr->mac) && 509 /* Extra checks required by SLAB_DESTROY_BY_RCU */
496 llc_mac_match(llc->daddr.mac, daddr->mac)) { 510 if (unlikely(!atomic_inc_not_zero(&rc->sk_refcnt)))
497 sock_hold(rc); 511 goto again;
512 if (unlikely(llc_sk(rc)->sap != sap ||
513 !llc_estab_match(sap, daddr, laddr, rc))) {
514 sock_put(rc);
515 continue;
516 }
498 goto found; 517 goto found;
499 } 518 }
500 } 519 }
501 rc = NULL; 520 rc = NULL;
521 /*
522 * if the nulls value we got at the end of this lookup is
523 * not the expected one, we must restart lookup.
524 * We probably met an item that was moved to another chain.
525 */
526 if (unlikely(get_nulls_value(node) != slot))
527 goto again;
502found: 528found:
503 read_unlock(&sap->sk_list.lock); 529 rcu_read_unlock();
504 return rc; 530 return rc;
505} 531}
506 532
@@ -516,6 +542,53 @@ struct sock *llc_lookup_established(struct llc_sap *sap,
516 return sk; 542 return sk;
517} 543}
518 544
545static inline bool llc_listener_match(const struct llc_sap *sap,
546 const struct llc_addr *laddr,
547 const struct sock *sk)
548{
549 struct llc_sock *llc = llc_sk(sk);
550
551 return sk->sk_type == SOCK_STREAM && sk->sk_state == TCP_LISTEN &&
552 llc->laddr.lsap == laddr->lsap &&
553 llc_mac_match(llc->laddr.mac, laddr->mac);
554}
555
556static struct sock *__llc_lookup_listener(struct llc_sap *sap,
557 struct llc_addr *laddr)
558{
559 struct sock *rc;
560 struct hlist_nulls_node *node;
561 int slot = llc_sk_laddr_hashfn(sap, laddr);
562 struct hlist_nulls_head *laddr_hb = &sap->sk_laddr_hash[slot];
563
564 rcu_read_lock();
565again:
566 sk_nulls_for_each_rcu(rc, node, laddr_hb) {
567 if (llc_listener_match(sap, laddr, rc)) {
568 /* Extra checks required by SLAB_DESTROY_BY_RCU */
569 if (unlikely(!atomic_inc_not_zero(&rc->sk_refcnt)))
570 goto again;
571 if (unlikely(llc_sk(rc)->sap != sap ||
572 !llc_listener_match(sap, laddr, rc))) {
573 sock_put(rc);
574 continue;
575 }
576 goto found;
577 }
578 }
579 rc = NULL;
580 /*
581 * if the nulls value we got at the end of this lookup is
582 * not the expected one, we must restart lookup.
583 * We probably met an item that was moved to another chain.
584 */
585 if (unlikely(get_nulls_value(node) != slot))
586 goto again;
587found:
588 rcu_read_unlock();
589 return rc;
590}
591
519/** 592/**
520 * llc_lookup_listener - Finds listener for local MAC + SAP 593 * llc_lookup_listener - Finds listener for local MAC + SAP
521 * @sap: SAP 594 * @sap: SAP
@@ -529,24 +602,12 @@ struct sock *llc_lookup_established(struct llc_sap *sap,
529static struct sock *llc_lookup_listener(struct llc_sap *sap, 602static struct sock *llc_lookup_listener(struct llc_sap *sap,
530 struct llc_addr *laddr) 603 struct llc_addr *laddr)
531{ 604{
532 struct sock *rc; 605 static struct llc_addr null_addr;
533 struct hlist_node *node; 606 struct sock *rc = __llc_lookup_listener(sap, laddr);
534 607
535 read_lock(&sap->sk_list.lock); 608 if (!rc)
536 sk_for_each(rc, node, &sap->sk_list.list) { 609 rc = __llc_lookup_listener(sap, &null_addr);
537 struct llc_sock *llc = llc_sk(rc);
538 610
539 if (rc->sk_type == SOCK_STREAM && rc->sk_state == TCP_LISTEN &&
540 llc->laddr.lsap == laddr->lsap &&
541 (llc_mac_match(llc->laddr.mac, laddr->mac) ||
542 llc_mac_null(llc->laddr.mac))) {
543 sock_hold(rc);
544 goto found;
545 }
546 }
547 rc = NULL;
548found:
549 read_unlock(&sap->sk_list.lock);
550 return rc; 611 return rc;
551} 612}
552 613
@@ -647,15 +708,22 @@ static int llc_find_offset(int state, int ev_type)
647 * @sap: SAP 708 * @sap: SAP
648 * @sk: socket 709 * @sk: socket
649 * 710 *
650 * This function adds a socket to sk_list of a SAP. 711 * This function adds a socket to the hash tables of a SAP.
651 */ 712 */
652void llc_sap_add_socket(struct llc_sap *sap, struct sock *sk) 713void llc_sap_add_socket(struct llc_sap *sap, struct sock *sk)
653{ 714{
715 struct llc_sock *llc = llc_sk(sk);
716 struct hlist_head *dev_hb = llc_sk_dev_hash(sap, llc->dev->ifindex);
717 struct hlist_nulls_head *laddr_hb = llc_sk_laddr_hash(sap, &llc->laddr);
718
654 llc_sap_hold(sap); 719 llc_sap_hold(sap);
655 write_lock_bh(&sap->sk_list.lock);
656 llc_sk(sk)->sap = sap; 720 llc_sk(sk)->sap = sap;
657 sk_add_node(sk, &sap->sk_list.list); 721
658 write_unlock_bh(&sap->sk_list.lock); 722 spin_lock_bh(&sap->sk_lock);
723 sap->sk_count++;
724 sk_nulls_add_node_rcu(sk, laddr_hb);
725 hlist_add_head(&llc->dev_hash_node, dev_hb);
726 spin_unlock_bh(&sap->sk_lock);
659} 727}
660 728
661/** 729/**
@@ -663,14 +731,18 @@ void llc_sap_add_socket(struct llc_sap *sap, struct sock *sk)
663 * @sap: SAP 731 * @sap: SAP
664 * @sk: socket 732 * @sk: socket
665 * 733 *
666 * This function removes a connection from sk_list.list of a SAP if 734 * This function removes a connection from the hash tables of a SAP if
667 * the connection was in this list. 735 * the connection was in this list.
668 */ 736 */
669void llc_sap_remove_socket(struct llc_sap *sap, struct sock *sk) 737void llc_sap_remove_socket(struct llc_sap *sap, struct sock *sk)
670{ 738{
671 write_lock_bh(&sap->sk_list.lock); 739 struct llc_sock *llc = llc_sk(sk);
672 sk_del_node_init(sk); 740
673 write_unlock_bh(&sap->sk_list.lock); 741 spin_lock_bh(&sap->sk_lock);
742 sk_nulls_del_node_init_rcu(sk);
743 hlist_del(&llc->dev_hash_node);
744 sap->sk_count--;
745 spin_unlock_bh(&sap->sk_lock);
674 llc_sap_put(sap); 746 llc_sap_put(sap);
675} 747}
676 748
@@ -756,7 +828,8 @@ void llc_conn_handler(struct llc_sap *sap, struct sk_buff *skb)
756 else { 828 else {
757 dprintk("%s: adding to backlog...\n", __func__); 829 dprintk("%s: adding to backlog...\n", __func__);
758 llc_set_backlog_type(skb, LLC_PACKET); 830 llc_set_backlog_type(skb, LLC_PACKET);
759 sk_add_backlog(sk, skb); 831 if (sk_add_backlog(sk, skb))
832 goto drop_unlock;
760 } 833 }
761out: 834out:
762 bh_unlock_sock(sk); 835 bh_unlock_sock(sk);