aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOctavian Purdila <opurdila@ixiacom.com>2009-12-26 06:51:05 -0500
committerDavid S. Miller <davem@davemloft.net>2009-12-26 23:45:32 -0500
commit52d58aef5ee460fedd7f250f05e79081019f2c79 (patch)
tree9c8d2bf8374570038820a47325a2305bcb83111d
parent6d2e3ea284463d5ab34e9cf2a41d0b8627b95d02 (diff)
llc: replace the socket list with a local address based hash
For the cases where a lot of interfaces are used in conjunction with a lot of LLC sockets bound to the same SAP, the iteration of the socket list becomes prohibitively expensive. Replacing the list with a a local address based hash significantly improves the bind and listener lookup operations as well as the datagram delivery. Connected sockets delivery is also improved, but this patch does not address the case where we have lots of sockets with the same local address connected to different remote addresses. In order to keep the socket sanity checks alive and fast a socket counter was added to the SAP structure. Signed-off-by: Octavian Purdila <opurdila@ixiacom.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/net/llc.h21
-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
5 files changed, 115 insertions, 37 deletions
diff --git a/include/net/llc.h b/include/net/llc.h
index dbef5917905b..709a9b37e239 100644
--- a/include/net/llc.h
+++ b/include/net/llc.h
@@ -17,6 +17,8 @@
17#include <linux/list.h> 17#include <linux/list.h>
18#include <linux/spinlock.h> 18#include <linux/spinlock.h>
19#include <linux/rculist_nulls.h> 19#include <linux/rculist_nulls.h>
20#include <linux/hash.h>
21#include <linux/jhash.h>
20 22
21#include <asm/atomic.h> 23#include <asm/atomic.h>
22 24
@@ -35,6 +37,9 @@ struct llc_addr {
35#define LLC_SK_DEV_HASH_BITS 6 37#define LLC_SK_DEV_HASH_BITS 6
36#define LLC_SK_DEV_HASH_ENTRIES (1<<LLC_SK_DEV_HASH_BITS) 38#define LLC_SK_DEV_HASH_ENTRIES (1<<LLC_SK_DEV_HASH_BITS)
37 39
40#define LLC_SK_LADDR_HASH_BITS 6
41#define LLC_SK_LADDR_HASH_ENTRIES (1<<LLC_SK_LADDR_HASH_BITS)
42
38/** 43/**
39 * struct llc_sap - Defines the SAP component 44 * struct llc_sap - Defines the SAP component
40 * 45 *
@@ -58,7 +63,8 @@ struct llc_sap {
58 struct llc_addr laddr; 63 struct llc_addr laddr;
59 struct list_head node; 64 struct list_head node;
60 spinlock_t sk_lock; 65 spinlock_t sk_lock;
61 struct hlist_nulls_head sk_list; 66 int sk_count;
67 struct hlist_nulls_head sk_laddr_hash[LLC_SK_LADDR_HASH_ENTRIES];
62 struct hlist_head sk_dev_hash[LLC_SK_DEV_HASH_ENTRIES]; 68 struct hlist_head sk_dev_hash[LLC_SK_DEV_HASH_ENTRIES];
63}; 69};
64 70
@@ -68,6 +74,19 @@ struct hlist_head *llc_sk_dev_hash(struct llc_sap *sap, int ifindex)
68 return &sap->sk_dev_hash[ifindex % LLC_SK_DEV_HASH_ENTRIES]; 74 return &sap->sk_dev_hash[ifindex % LLC_SK_DEV_HASH_ENTRIES];
69} 75}
70 76
77static inline
78u32 llc_sk_laddr_hashfn(struct llc_sap *sap, const struct llc_addr *laddr)
79{
80 return hash_32(jhash(laddr->mac, sizeof(laddr->mac), 0),
81 LLC_SK_LADDR_HASH_BITS);
82}
83
84static inline
85struct hlist_nulls_head *llc_sk_laddr_hash(struct llc_sap *sap,
86 const struct llc_addr *laddr)
87{
88 return &sap->sk_laddr_hash[llc_sk_laddr_hashfn(sap, laddr)];
89}
71 90
72#define LLC_DEST_INVALID 0 /* Invalid LLC PDU type */ 91#define LLC_DEST_INVALID 0 /* Invalid LLC PDU type */
73#define LLC_DEST_SAP 1 /* Type 1 goes here */ 92#define LLC_DEST_SAP 1 /* Type 1 goes here */
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;