diff options
Diffstat (limited to 'net/llc/llc_conn.c')
-rw-r--r-- | net/llc/llc_conn.c | 70 |
1 files changed, 51 insertions, 19 deletions
diff --git a/net/llc/llc_conn.c b/net/llc/llc_conn.c index 10cdfe2db830..a8dde9b010da 100644 --- a/net/llc/llc_conn.c +++ b/net/llc/llc_conn.c | |||
@@ -498,10 +498,12 @@ static struct sock *__llc_lookup_established(struct llc_sap *sap, | |||
498 | { | 498 | { |
499 | struct sock *rc; | 499 | struct sock *rc; |
500 | struct hlist_nulls_node *node; | 500 | struct hlist_nulls_node *node; |
501 | int slot = llc_sk_laddr_hashfn(sap, laddr); | ||
502 | struct hlist_nulls_head *laddr_hb = &sap->sk_laddr_hash[slot]; | ||
501 | 503 | ||
502 | rcu_read_lock(); | 504 | rcu_read_lock(); |
503 | again: | 505 | again: |
504 | sk_nulls_for_each_rcu(rc, node, &sap->sk_list) { | 506 | sk_nulls_for_each_rcu(rc, node, laddr_hb) { |
505 | if (llc_estab_match(sap, daddr, laddr, rc)) { | 507 | if (llc_estab_match(sap, daddr, laddr, rc)) { |
506 | /* Extra checks required by SLAB_DESTROY_BY_RCU */ | 508 | /* Extra checks required by SLAB_DESTROY_BY_RCU */ |
507 | if (unlikely(!atomic_inc_not_zero(&rc->sk_refcnt))) | 509 | if (unlikely(!atomic_inc_not_zero(&rc->sk_refcnt))) |
@@ -515,6 +517,13 @@ again: | |||
515 | } | 517 | } |
516 | } | 518 | } |
517 | rc = NULL; | 519 | rc = NULL; |
520 | /* | ||
521 | * if the nulls value we got at the end of this lookup is | ||
522 | * not the expected one, we must restart lookup. | ||
523 | * We probably met an item that was moved to another chain. | ||
524 | */ | ||
525 | if (unlikely(get_nulls_value(node) != slot)) | ||
526 | goto again; | ||
518 | found: | 527 | found: |
519 | rcu_read_unlock(); | 528 | rcu_read_unlock(); |
520 | return rc; | 529 | return rc; |
@@ -540,29 +549,20 @@ static inline bool llc_listener_match(const struct llc_sap *sap, | |||
540 | 549 | ||
541 | return sk->sk_type == SOCK_STREAM && sk->sk_state == TCP_LISTEN && | 550 | return sk->sk_type == SOCK_STREAM && sk->sk_state == TCP_LISTEN && |
542 | llc->laddr.lsap == laddr->lsap && | 551 | llc->laddr.lsap == laddr->lsap && |
543 | (llc_mac_match(llc->laddr.mac, laddr->mac) || | 552 | llc_mac_match(llc->laddr.mac, laddr->mac); |
544 | llc_mac_null(llc->laddr.mac)); | ||
545 | } | 553 | } |
546 | 554 | ||
547 | /** | 555 | static struct sock *__llc_lookup_listener(struct llc_sap *sap, |
548 | * llc_lookup_listener - Finds listener for local MAC + SAP | 556 | struct llc_addr *laddr) |
549 | * @sap: SAP | ||
550 | * @laddr: address of local LLC (MAC + SAP) | ||
551 | * | ||
552 | * Search connection list of the SAP and finds connection listening on | ||
553 | * local mac, and local sap. Returns pointer for parent socket found, | ||
554 | * %NULL otherwise. | ||
555 | * Caller has to make sure local_bh is disabled. | ||
556 | */ | ||
557 | static struct sock *llc_lookup_listener(struct llc_sap *sap, | ||
558 | struct llc_addr *laddr) | ||
559 | { | 557 | { |
560 | struct sock *rc; | 558 | struct sock *rc; |
561 | struct hlist_nulls_node *node; | 559 | struct hlist_nulls_node *node; |
560 | int slot = llc_sk_laddr_hashfn(sap, laddr); | ||
561 | struct hlist_nulls_head *laddr_hb = &sap->sk_laddr_hash[slot]; | ||
562 | 562 | ||
563 | rcu_read_lock(); | 563 | rcu_read_lock(); |
564 | again: | 564 | again: |
565 | sk_nulls_for_each_rcu(rc, node, &sap->sk_list) { | 565 | sk_nulls_for_each_rcu(rc, node, laddr_hb) { |
566 | if (llc_listener_match(sap, laddr, rc)) { | 566 | if (llc_listener_match(sap, laddr, rc)) { |
567 | /* Extra checks required by SLAB_DESTROY_BY_RCU */ | 567 | /* Extra checks required by SLAB_DESTROY_BY_RCU */ |
568 | if (unlikely(!atomic_inc_not_zero(&rc->sk_refcnt))) | 568 | if (unlikely(!atomic_inc_not_zero(&rc->sk_refcnt))) |
@@ -576,11 +576,40 @@ again: | |||
576 | } | 576 | } |
577 | } | 577 | } |
578 | rc = NULL; | 578 | rc = NULL; |
579 | /* | ||
580 | * if the nulls value we got at the end of this lookup is | ||
581 | * not the expected one, we must restart lookup. | ||
582 | * We probably met an item that was moved to another chain. | ||
583 | */ | ||
584 | if (unlikely(get_nulls_value(node) != slot)) | ||
585 | goto again; | ||
579 | found: | 586 | found: |
580 | rcu_read_unlock(); | 587 | rcu_read_unlock(); |
581 | return rc; | 588 | return rc; |
582 | } | 589 | } |
583 | 590 | ||
591 | /** | ||
592 | * llc_lookup_listener - Finds listener for local MAC + SAP | ||
593 | * @sap: SAP | ||
594 | * @laddr: address of local LLC (MAC + SAP) | ||
595 | * | ||
596 | * Search connection list of the SAP and finds connection listening on | ||
597 | * local mac, and local sap. Returns pointer for parent socket found, | ||
598 | * %NULL otherwise. | ||
599 | * Caller has to make sure local_bh is disabled. | ||
600 | */ | ||
601 | static struct sock *llc_lookup_listener(struct llc_sap *sap, | ||
602 | struct llc_addr *laddr) | ||
603 | { | ||
604 | static struct llc_addr null_addr; | ||
605 | struct sock *rc = __llc_lookup_listener(sap, laddr); | ||
606 | |||
607 | if (!rc) | ||
608 | rc = __llc_lookup_listener(sap, &null_addr); | ||
609 | |||
610 | return rc; | ||
611 | } | ||
612 | |||
584 | static struct sock *__llc_lookup(struct llc_sap *sap, | 613 | static struct sock *__llc_lookup(struct llc_sap *sap, |
585 | struct llc_addr *daddr, | 614 | struct llc_addr *daddr, |
586 | struct llc_addr *laddr) | 615 | struct llc_addr *laddr) |
@@ -678,18 +707,20 @@ static int llc_find_offset(int state, int ev_type) | |||
678 | * @sap: SAP | 707 | * @sap: SAP |
679 | * @sk: socket | 708 | * @sk: socket |
680 | * | 709 | * |
681 | * This function adds a socket to sk_list of a SAP. | 710 | * This function adds a socket to the hash tables of a SAP. |
682 | */ | 711 | */ |
683 | void llc_sap_add_socket(struct llc_sap *sap, struct sock *sk) | 712 | void llc_sap_add_socket(struct llc_sap *sap, struct sock *sk) |
684 | { | 713 | { |
685 | struct llc_sock *llc = llc_sk(sk); | 714 | struct llc_sock *llc = llc_sk(sk); |
686 | struct hlist_head *dev_hb = llc_sk_dev_hash(sap, llc->dev->ifindex); | 715 | struct hlist_head *dev_hb = llc_sk_dev_hash(sap, llc->dev->ifindex); |
716 | struct hlist_nulls_head *laddr_hb = llc_sk_laddr_hash(sap, &llc->laddr); | ||
687 | 717 | ||
688 | llc_sap_hold(sap); | 718 | llc_sap_hold(sap); |
689 | llc_sk(sk)->sap = sap; | 719 | llc_sk(sk)->sap = sap; |
690 | 720 | ||
691 | spin_lock_bh(&sap->sk_lock); | 721 | spin_lock_bh(&sap->sk_lock); |
692 | sk_nulls_add_node_rcu(sk, &sap->sk_list); | 722 | sap->sk_count++; |
723 | sk_nulls_add_node_rcu(sk, laddr_hb); | ||
693 | hlist_add_head(&llc->dev_hash_node, dev_hb); | 724 | hlist_add_head(&llc->dev_hash_node, dev_hb); |
694 | spin_unlock_bh(&sap->sk_lock); | 725 | spin_unlock_bh(&sap->sk_lock); |
695 | } | 726 | } |
@@ -699,7 +730,7 @@ void llc_sap_add_socket(struct llc_sap *sap, struct sock *sk) | |||
699 | * @sap: SAP | 730 | * @sap: SAP |
700 | * @sk: socket | 731 | * @sk: socket |
701 | * | 732 | * |
702 | * This function removes a connection from sk_list of a SAP if | 733 | * This function removes a connection from the hash tables of a SAP if |
703 | * the connection was in this list. | 734 | * the connection was in this list. |
704 | */ | 735 | */ |
705 | void llc_sap_remove_socket(struct llc_sap *sap, struct sock *sk) | 736 | void llc_sap_remove_socket(struct llc_sap *sap, struct sock *sk) |
@@ -709,6 +740,7 @@ void llc_sap_remove_socket(struct llc_sap *sap, struct sock *sk) | |||
709 | spin_lock_bh(&sap->sk_lock); | 740 | spin_lock_bh(&sap->sk_lock); |
710 | sk_nulls_del_node_init_rcu(sk); | 741 | sk_nulls_del_node_init_rcu(sk); |
711 | hlist_del(&llc->dev_hash_node); | 742 | hlist_del(&llc->dev_hash_node); |
743 | sap->sk_count--; | ||
712 | spin_unlock_bh(&sap->sk_lock); | 744 | spin_unlock_bh(&sap->sk_lock); |
713 | llc_sap_put(sap); | 745 | llc_sap_put(sap); |
714 | } | 746 | } |