diff options
author | Octavian Purdila <opurdila@ixiacom.com> | 2009-12-26 06:51:05 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-12-26 23:45:32 -0500 |
commit | 52d58aef5ee460fedd7f250f05e79081019f2c79 (patch) | |
tree | 9c8d2bf8374570038820a47325a2305bcb83111d /net/llc | |
parent | 6d2e3ea284463d5ab34e9cf2a41d0b8627b95d02 (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>
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; |