diff options
Diffstat (limited to 'net/llc')
-rw-r--r-- | net/llc/llc_conn.c | 70 | ||||
-rw-r--r-- | net/llc/llc_core.c | 6 | ||||
-rw-r--r-- | net/llc/llc_proc.c | 44 | ||||
-rw-r--r-- | net/llc/llc_sap.c | 11 |
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(); |
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 | } |
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); | |||
33 | static struct llc_sap *llc_sap_alloc(void) | 33 | static 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 | */ |
144 | void llc_sap_close(struct llc_sap *sap) | 146 | void 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 | ||
69 | static 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 | |||
78 | out: | ||
79 | return sk; | ||
80 | } | ||
81 | |||
64 | static void *llc_seq_next(struct seq_file *seq, void *v, loff_t *pos) | 82 | static 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 | } |
96 | out: | 112 | out: |
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(); |
326 | again: | 328 | again: |
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; | ||
341 | found: | 350 | found: |
342 | rcu_read_unlock_bh(); | 351 | rcu_read_unlock_bh(); |
343 | return rc; | 352 | return rc; |