diff options
author | Eric Dumazet <eric.dumazet@gmail.com> | 2009-11-08 05:17:58 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-11-08 23:53:06 -0500 |
commit | 512615b6b843ff3ff5ad583f34c39b3f302f5f26 (patch) | |
tree | 7420705a314bc691bc478225148935dc67f71904 | |
parent | d4cada4ae1c012815f95fa507eb86a0ae9d607d7 (diff) |
udp: secondary hash on (local port, local address)
Extends udp_table to contain a secondary hash table.
socket anchor for this second hash is free, because UDP
doesnt use skc_bind_node : We define an union to hold
both skc_bind_node & a new hlist_nulls_node udp_portaddr_node
udp_lib_get_port() inserts sockets into second hash chain
(additional cost of one atomic op)
udp_lib_unhash() deletes socket from second hash chain
(additional cost of one atomic op)
Note : No spinlock lockdep annotation is needed, because
lock for the secondary hash chain is always get after
lock for primary hash chain.
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/linux/udp.h | 1 | ||||
-rw-r--r-- | include/net/sock.h | 8 | ||||
-rw-r--r-- | include/net/udp.h | 22 | ||||
-rw-r--r-- | net/ipv4/udp.c | 31 |
4 files changed, 53 insertions, 9 deletions
diff --git a/include/linux/udp.h b/include/linux/udp.h index 5b4b5274e683..59f0ddf2d284 100644 --- a/include/linux/udp.h +++ b/include/linux/udp.h | |||
@@ -57,6 +57,7 @@ struct udp_sock { | |||
57 | struct inet_sock inet; | 57 | struct inet_sock inet; |
58 | #define udp_port_hash inet.sk.__sk_common.skc_u16hashes[0] | 58 | #define udp_port_hash inet.sk.__sk_common.skc_u16hashes[0] |
59 | #define udp_portaddr_hash inet.sk.__sk_common.skc_u16hashes[1] | 59 | #define udp_portaddr_hash inet.sk.__sk_common.skc_u16hashes[1] |
60 | #define udp_portaddr_node inet.sk.__sk_common.skc_portaddr_node | ||
60 | int pending; /* Any pending frames ? */ | 61 | int pending; /* Any pending frames ? */ |
61 | unsigned int corkflag; /* Cork is required */ | 62 | unsigned int corkflag; /* Cork is required */ |
62 | __u16 encap_type; /* Is this an Encapsulation socket? */ | 63 | __u16 encap_type; /* Is this an Encapsulation socket? */ |
diff --git a/include/net/sock.h b/include/net/sock.h index 827366b62680..3f1a4804bb3f 100644 --- a/include/net/sock.h +++ b/include/net/sock.h | |||
@@ -105,7 +105,7 @@ struct net; | |||
105 | /** | 105 | /** |
106 | * struct sock_common - minimal network layer representation of sockets | 106 | * struct sock_common - minimal network layer representation of sockets |
107 | * @skc_node: main hash linkage for various protocol lookup tables | 107 | * @skc_node: main hash linkage for various protocol lookup tables |
108 | * @skc_nulls_node: main hash linkage for UDP/UDP-Lite protocol | 108 | * @skc_nulls_node: main hash linkage for TCP/UDP/UDP-Lite protocol |
109 | * @skc_refcnt: reference count | 109 | * @skc_refcnt: reference count |
110 | * @skc_tx_queue_mapping: tx queue number for this connection | 110 | * @skc_tx_queue_mapping: tx queue number for this connection |
111 | * @skc_hash: hash value used with various protocol lookup tables | 111 | * @skc_hash: hash value used with various protocol lookup tables |
@@ -115,6 +115,7 @@ struct net; | |||
115 | * @skc_reuse: %SO_REUSEADDR setting | 115 | * @skc_reuse: %SO_REUSEADDR setting |
116 | * @skc_bound_dev_if: bound device index if != 0 | 116 | * @skc_bound_dev_if: bound device index if != 0 |
117 | * @skc_bind_node: bind hash linkage for various protocol lookup tables | 117 | * @skc_bind_node: bind hash linkage for various protocol lookup tables |
118 | * @skc_portaddr_node: second hash linkage for UDP/UDP-Lite protocol | ||
118 | * @skc_prot: protocol handlers inside a network family | 119 | * @skc_prot: protocol handlers inside a network family |
119 | * @skc_net: reference to the network namespace of this socket | 120 | * @skc_net: reference to the network namespace of this socket |
120 | * | 121 | * |
@@ -140,7 +141,10 @@ struct sock_common { | |||
140 | volatile unsigned char skc_state; | 141 | volatile unsigned char skc_state; |
141 | unsigned char skc_reuse; | 142 | unsigned char skc_reuse; |
142 | int skc_bound_dev_if; | 143 | int skc_bound_dev_if; |
143 | struct hlist_node skc_bind_node; | 144 | union { |
145 | struct hlist_node skc_bind_node; | ||
146 | struct hlist_nulls_node skc_portaddr_node; | ||
147 | }; | ||
144 | struct proto *skc_prot; | 148 | struct proto *skc_prot; |
145 | #ifdef CONFIG_NET_NS | 149 | #ifdef CONFIG_NET_NS |
146 | struct net *skc_net; | 150 | struct net *skc_net; |
diff --git a/include/net/udp.h b/include/net/udp.h index 9167281e47dc..af41850f742a 100644 --- a/include/net/udp.h +++ b/include/net/udp.h | |||
@@ -63,10 +63,19 @@ struct udp_hslot { | |||
63 | spinlock_t lock; | 63 | spinlock_t lock; |
64 | } __attribute__((aligned(2 * sizeof(long)))); | 64 | } __attribute__((aligned(2 * sizeof(long)))); |
65 | 65 | ||
66 | /** | ||
67 | * struct udp_table - UDP table | ||
68 | * | ||
69 | * @hash: hash table, sockets are hashed on (local port) | ||
70 | * @hash2: hash table, sockets are hashed on (local port, local address) | ||
71 | * @mask: number of slots in hash tables, minus 1 | ||
72 | * @log: log2(number of slots in hash table) | ||
73 | */ | ||
66 | struct udp_table { | 74 | struct udp_table { |
67 | struct udp_hslot *hash; | 75 | struct udp_hslot *hash; |
68 | unsigned int mask; | 76 | struct udp_hslot *hash2; |
69 | unsigned int log; | 77 | unsigned int mask; |
78 | unsigned int log; | ||
70 | }; | 79 | }; |
71 | extern struct udp_table udp_table; | 80 | extern struct udp_table udp_table; |
72 | extern void udp_table_init(struct udp_table *, const char *); | 81 | extern void udp_table_init(struct udp_table *, const char *); |
@@ -75,6 +84,15 @@ static inline struct udp_hslot *udp_hashslot(struct udp_table *table, | |||
75 | { | 84 | { |
76 | return &table->hash[udp_hashfn(net, num, table->mask)]; | 85 | return &table->hash[udp_hashfn(net, num, table->mask)]; |
77 | } | 86 | } |
87 | /* | ||
88 | * For secondary hash, net_hash_mix() is performed before calling | ||
89 | * udp_hashslot2(), this explains difference with udp_hashslot() | ||
90 | */ | ||
91 | static inline struct udp_hslot *udp_hashslot2(struct udp_table *table, | ||
92 | unsigned int hash) | ||
93 | { | ||
94 | return &table->hash2[hash & table->mask]; | ||
95 | } | ||
78 | 96 | ||
79 | /* Note: this must match 'valbool' in sock_setsockopt */ | 97 | /* Note: this must match 'valbool' in sock_setsockopt */ |
80 | #define UDP_CSUM_NOXMIT 1 | 98 | #define UDP_CSUM_NOXMIT 1 |
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index af72de1c8690..5f04216f35ce 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c | |||
@@ -163,7 +163,7 @@ int udp_lib_get_port(struct sock *sk, unsigned short snum, | |||
163 | int (*saddr_comp)(const struct sock *sk1, | 163 | int (*saddr_comp)(const struct sock *sk1, |
164 | const struct sock *sk2)) | 164 | const struct sock *sk2)) |
165 | { | 165 | { |
166 | struct udp_hslot *hslot; | 166 | struct udp_hslot *hslot, *hslot2; |
167 | struct udp_table *udptable = sk->sk_prot->h.udp_table; | 167 | struct udp_table *udptable = sk->sk_prot->h.udp_table; |
168 | int error = 1; | 168 | int error = 1; |
169 | struct net *net = sock_net(sk); | 169 | struct net *net = sock_net(sk); |
@@ -222,6 +222,13 @@ found: | |||
222 | sk_nulls_add_node_rcu(sk, &hslot->head); | 222 | sk_nulls_add_node_rcu(sk, &hslot->head); |
223 | hslot->count++; | 223 | hslot->count++; |
224 | sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1); | 224 | sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1); |
225 | |||
226 | hslot2 = udp_hashslot2(udptable, udp_sk(sk)->udp_portaddr_hash); | ||
227 | spin_lock(&hslot2->lock); | ||
228 | hlist_nulls_add_head_rcu(&udp_sk(sk)->udp_portaddr_node, | ||
229 | &hslot2->head); | ||
230 | hslot2->count++; | ||
231 | spin_unlock(&hslot2->lock); | ||
225 | } | 232 | } |
226 | error = 0; | 233 | error = 0; |
227 | fail_unlock: | 234 | fail_unlock: |
@@ -1062,14 +1069,22 @@ void udp_lib_unhash(struct sock *sk) | |||
1062 | { | 1069 | { |
1063 | if (sk_hashed(sk)) { | 1070 | if (sk_hashed(sk)) { |
1064 | struct udp_table *udptable = sk->sk_prot->h.udp_table; | 1071 | struct udp_table *udptable = sk->sk_prot->h.udp_table; |
1065 | struct udp_hslot *hslot = udp_hashslot(udptable, sock_net(sk), | 1072 | struct udp_hslot *hslot, *hslot2; |
1066 | udp_sk(sk)->udp_port_hash); | 1073 | |
1074 | hslot = udp_hashslot(udptable, sock_net(sk), | ||
1075 | udp_sk(sk)->udp_port_hash); | ||
1076 | hslot2 = udp_hashslot2(udptable, udp_sk(sk)->udp_portaddr_hash); | ||
1067 | 1077 | ||
1068 | spin_lock_bh(&hslot->lock); | 1078 | spin_lock_bh(&hslot->lock); |
1069 | if (sk_nulls_del_node_init_rcu(sk)) { | 1079 | if (sk_nulls_del_node_init_rcu(sk)) { |
1070 | hslot->count--; | 1080 | hslot->count--; |
1071 | inet_sk(sk)->inet_num = 0; | 1081 | inet_sk(sk)->inet_num = 0; |
1072 | sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1); | 1082 | sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1); |
1083 | |||
1084 | spin_lock(&hslot2->lock); | ||
1085 | hlist_nulls_del_init_rcu(&udp_sk(sk)->udp_portaddr_node); | ||
1086 | hslot2->count--; | ||
1087 | spin_unlock(&hslot2->lock); | ||
1073 | } | 1088 | } |
1074 | spin_unlock_bh(&hslot->lock); | 1089 | spin_unlock_bh(&hslot->lock); |
1075 | } | 1090 | } |
@@ -1857,7 +1872,7 @@ void __init udp_table_init(struct udp_table *table, const char *name) | |||
1857 | 1872 | ||
1858 | if (!CONFIG_BASE_SMALL) | 1873 | if (!CONFIG_BASE_SMALL) |
1859 | table->hash = alloc_large_system_hash(name, | 1874 | table->hash = alloc_large_system_hash(name, |
1860 | sizeof(struct udp_hslot), | 1875 | 2 * sizeof(struct udp_hslot), |
1861 | uhash_entries, | 1876 | uhash_entries, |
1862 | 21, /* one slot per 2 MB */ | 1877 | 21, /* one slot per 2 MB */ |
1863 | 0, | 1878 | 0, |
@@ -1869,17 +1884,23 @@ void __init udp_table_init(struct udp_table *table, const char *name) | |||
1869 | */ | 1884 | */ |
1870 | if (CONFIG_BASE_SMALL || table->mask < UDP_HTABLE_SIZE_MIN - 1) { | 1885 | if (CONFIG_BASE_SMALL || table->mask < UDP_HTABLE_SIZE_MIN - 1) { |
1871 | table->hash = kmalloc(UDP_HTABLE_SIZE_MIN * | 1886 | table->hash = kmalloc(UDP_HTABLE_SIZE_MIN * |
1872 | sizeof(struct udp_hslot), GFP_KERNEL); | 1887 | 2 * sizeof(struct udp_hslot), GFP_KERNEL); |
1873 | if (!table->hash) | 1888 | if (!table->hash) |
1874 | panic(name); | 1889 | panic(name); |
1875 | table->log = ilog2(UDP_HTABLE_SIZE_MIN); | 1890 | table->log = ilog2(UDP_HTABLE_SIZE_MIN); |
1876 | table->mask = UDP_HTABLE_SIZE_MIN - 1; | 1891 | table->mask = UDP_HTABLE_SIZE_MIN - 1; |
1877 | } | 1892 | } |
1893 | table->hash2 = table->hash + (table->mask + 1); | ||
1878 | for (i = 0; i <= table->mask; i++) { | 1894 | for (i = 0; i <= table->mask; i++) { |
1879 | INIT_HLIST_NULLS_HEAD(&table->hash[i].head, i); | 1895 | INIT_HLIST_NULLS_HEAD(&table->hash[i].head, i); |
1880 | table->hash[i].count = 0; | 1896 | table->hash[i].count = 0; |
1881 | spin_lock_init(&table->hash[i].lock); | 1897 | spin_lock_init(&table->hash[i].lock); |
1882 | } | 1898 | } |
1899 | for (i = 0; i <= table->mask; i++) { | ||
1900 | INIT_HLIST_NULLS_HEAD(&table->hash2[i].head, i); | ||
1901 | table->hash2[i].count = 0; | ||
1902 | spin_lock_init(&table->hash2[i].lock); | ||
1903 | } | ||
1883 | } | 1904 | } |
1884 | 1905 | ||
1885 | void __init udp_init(void) | 1906 | void __init udp_init(void) |