aboutsummaryrefslogtreecommitdiffstats
path: root/net/llc
diff options
context:
space:
mode:
Diffstat (limited to 'net/llc')
-rw-r--r--net/llc/llc_conn.c70
-rw-r--r--net/llc/llc_core.c6
-rw-r--r--net/llc/llc_proc.c44
-rw-r--r--net/llc/llc_sap.c11
4 files changed, 95 insertions, 36 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();
503again: 505again:
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;
518found: 527found:
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/** 555static 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 */
557static 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();
564again: 564again:
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;
579found: 586found:
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 */
601static 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
584static struct sock *__llc_lookup(struct llc_sap *sap, 613static 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 */
683void llc_sap_add_socket(struct llc_sap *sap, struct sock *sk) 712void 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 */
705void llc_sap_remove_socket(struct llc_sap *sap, struct sock *sk) 736void 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}
diff --git a/net/llc/llc_core.c b/net/llc/llc_core.c
index 5276b9722077..0c9ef8bc7655 100644
--- a/net/llc/llc_core.c
+++ b/net/llc/llc_core.c
@@ -33,12 +33,14 @@ DEFINE_RWLOCK(llc_sap_list_lock);
33static struct llc_sap *llc_sap_alloc(void) 33static struct llc_sap *llc_sap_alloc(void)
34{ 34{
35 struct llc_sap *sap = kzalloc(sizeof(*sap), GFP_ATOMIC); 35 struct llc_sap *sap = kzalloc(sizeof(*sap), GFP_ATOMIC);
36 int i;
36 37
37 if (sap) { 38 if (sap) {
38 /* sap->laddr.mac - leave as a null, it's filled by bind */ 39 /* sap->laddr.mac - leave as a null, it's filled by bind */
39 sap->state = LLC_SAP_STATE_ACTIVE; 40 sap->state = LLC_SAP_STATE_ACTIVE;
40 spin_lock_init(&sap->sk_lock); 41 spin_lock_init(&sap->sk_lock);
41 INIT_HLIST_NULLS_HEAD(&sap->sk_list, 0); 42 for (i = 0; i < LLC_SK_LADDR_HASH_ENTRIES; i++)
43 INIT_HLIST_NULLS_HEAD(&sap->sk_laddr_hash[i], i);
42 atomic_set(&sap->refcnt, 1); 44 atomic_set(&sap->refcnt, 1);
43 } 45 }
44 return sap; 46 return sap;
@@ -143,7 +145,7 @@ out:
143 */ 145 */
144void llc_sap_close(struct llc_sap *sap) 146void llc_sap_close(struct llc_sap *sap)
145{ 147{
146 WARN_ON(!hlist_nulls_empty(&sap->sk_list)); 148 WARN_ON(sap->sk_count);
147 llc_del_sap(sap); 149 llc_del_sap(sap);
148 kfree(sap); 150 kfree(sap);
149} 151}
diff --git a/net/llc/llc_proc.c b/net/llc/llc_proc.c
index 6b3d033b3236..09dec6307206 100644
--- a/net/llc/llc_proc.c
+++ b/net/llc/llc_proc.c
@@ -34,17 +34,22 @@ static struct sock *llc_get_sk_idx(loff_t pos)
34{ 34{
35 struct list_head *sap_entry; 35 struct list_head *sap_entry;
36 struct llc_sap *sap; 36 struct llc_sap *sap;
37 struct hlist_nulls_node *node;
38 struct sock *sk = NULL; 37 struct sock *sk = NULL;
38 int i;
39 39
40 list_for_each(sap_entry, &llc_sap_list) { 40 list_for_each(sap_entry, &llc_sap_list) {
41 sap = list_entry(sap_entry, struct llc_sap, node); 41 sap = list_entry(sap_entry, struct llc_sap, node);
42 42
43 spin_lock_bh(&sap->sk_lock); 43 spin_lock_bh(&sap->sk_lock);
44 sk_nulls_for_each(sk, node, &sap->sk_list) { 44 for (i = 0; i < LLC_SK_LADDR_HASH_ENTRIES; i++) {
45 if (!pos) 45 struct hlist_nulls_head *head = &sap->sk_laddr_hash[i];
46 goto found; 46 struct hlist_nulls_node *node;
47 --pos; 47
48 sk_nulls_for_each(sk, node, head) {
49 if (!pos)
50 goto found; /* keep the lock */
51 --pos;
52 }
48 } 53 }
49 spin_unlock_bh(&sap->sk_lock); 54 spin_unlock_bh(&sap->sk_lock);
50 } 55 }
@@ -61,6 +66,19 @@ static void *llc_seq_start(struct seq_file *seq, loff_t *pos)
61 return l ? llc_get_sk_idx(--l) : SEQ_START_TOKEN; 66 return l ? llc_get_sk_idx(--l) : SEQ_START_TOKEN;
62} 67}
63 68
69static struct sock *laddr_hash_next(struct llc_sap *sap, int bucket)
70{
71 struct hlist_nulls_node *node;
72 struct sock *sk = NULL;
73
74 while (++bucket < LLC_SK_LADDR_HASH_ENTRIES)
75 sk_nulls_for_each(sk, node, &sap->sk_laddr_hash[bucket])
76 goto out;
77
78out:
79 return sk;
80}
81
64static void *llc_seq_next(struct seq_file *seq, void *v, loff_t *pos) 82static void *llc_seq_next(struct seq_file *seq, void *v, loff_t *pos)
65{ 83{
66 struct sock* sk, *next; 84 struct sock* sk, *next;
@@ -80,17 +98,15 @@ static void *llc_seq_next(struct seq_file *seq, void *v, loff_t *pos)
80 } 98 }
81 llc = llc_sk(sk); 99 llc = llc_sk(sk);
82 sap = llc->sap; 100 sap = llc->sap;
101 sk = laddr_hash_next(sap, llc_sk_laddr_hashfn(sap, &llc->laddr));
102 if (sk)
103 goto out;
83 spin_unlock_bh(&sap->sk_lock); 104 spin_unlock_bh(&sap->sk_lock);
84 sk = NULL; 105 list_for_each_entry_continue(sap, &llc_sap_list, node) {
85 for (;;) {
86 if (sap->node.next == &llc_sap_list)
87 break;
88 sap = list_entry(sap->node.next, struct llc_sap, node);
89 spin_lock_bh(&sap->sk_lock); 106 spin_lock_bh(&sap->sk_lock);
90 if (!hlist_nulls_empty(&sap->sk_list)) { 107 sk = laddr_hash_next(sap, -1);
91 sk = sk_nulls_head(&sap->sk_list); 108 if (sk)
92 break; 109 break; /* keep the lock */
93 }
94 spin_unlock_bh(&sap->sk_lock); 110 spin_unlock_bh(&sap->sk_lock);
95 } 111 }
96out: 112out:
diff --git a/net/llc/llc_sap.c b/net/llc/llc_sap.c
index 94cb706f6cc4..ad6e6e1cf22f 100644
--- a/net/llc/llc_sap.c
+++ b/net/llc/llc_sap.c
@@ -321,10 +321,12 @@ static struct sock *llc_lookup_dgram(struct llc_sap *sap,
321{ 321{
322 struct sock *rc; 322 struct sock *rc;
323 struct hlist_nulls_node *node; 323 struct hlist_nulls_node *node;
324 int slot = llc_sk_laddr_hashfn(sap, laddr);
325 struct hlist_nulls_head *laddr_hb = &sap->sk_laddr_hash[slot];
324 326
325 rcu_read_lock_bh(); 327 rcu_read_lock_bh();
326again: 328again:
327 sk_nulls_for_each_rcu(rc, node, &sap->sk_list) { 329 sk_nulls_for_each_rcu(rc, node, laddr_hb) {
328 if (llc_dgram_match(sap, laddr, rc)) { 330 if (llc_dgram_match(sap, laddr, rc)) {
329 /* Extra checks required by SLAB_DESTROY_BY_RCU */ 331 /* Extra checks required by SLAB_DESTROY_BY_RCU */
330 if (unlikely(!atomic_inc_not_zero(&rc->sk_refcnt))) 332 if (unlikely(!atomic_inc_not_zero(&rc->sk_refcnt)))
@@ -338,6 +340,13 @@ again:
338 } 340 }
339 } 341 }
340 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;
341found: 350found:
342 rcu_read_unlock_bh(); 351 rcu_read_unlock_bh();
343 return rc; 352 return rc;