aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4/tcp_metrics.c
diff options
context:
space:
mode:
authorEric W. Biederman <ebiederm@xmission.com>2015-03-13 01:07:44 -0400
committerDavid S. Miller <davem@davemloft.net>2015-03-13 01:57:07 -0400
commit098a697b497e3154a1a583c1d34c67568acaadcc (patch)
tree4440bf30fb2500af39ea2d084c9caba0d5ca064f /net/ipv4/tcp_metrics.c
parent04f721c671656f93de888b1d176ba30b7336cca3 (diff)
tcp_metrics: Use a single hash table for all network namespaces.
Now that all of the operations are safe on a single hash table accross network namespaces, allocate a single global hash table and update the code to use it. Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4/tcp_metrics.c')
-rw-r--r--net/ipv4/tcp_metrics.c66
1 files changed, 30 insertions, 36 deletions
diff --git a/net/ipv4/tcp_metrics.c b/net/ipv4/tcp_metrics.c
index baccb070427d..366728cbee4a 100644
--- a/net/ipv4/tcp_metrics.c
+++ b/net/ipv4/tcp_metrics.c
@@ -97,6 +97,9 @@ struct tcpm_hash_bucket {
97 struct tcp_metrics_block __rcu *chain; 97 struct tcp_metrics_block __rcu *chain;
98}; 98};
99 99
100static struct tcpm_hash_bucket *tcp_metrics_hash __read_mostly;
101static unsigned int tcp_metrics_hash_log __read_mostly;
102
100static DEFINE_SPINLOCK(tcp_metrics_lock); 103static DEFINE_SPINLOCK(tcp_metrics_lock);
101 104
102static void tcpm_suck_dst(struct tcp_metrics_block *tm, 105static void tcpm_suck_dst(struct tcp_metrics_block *tm,
@@ -177,7 +180,7 @@ static struct tcp_metrics_block *tcpm_new(struct dst_entry *dst,
177 if (unlikely(reclaim)) { 180 if (unlikely(reclaim)) {
178 struct tcp_metrics_block *oldest; 181 struct tcp_metrics_block *oldest;
179 182
180 oldest = rcu_dereference(net->ipv4.tcp_metrics_hash[hash].chain); 183 oldest = rcu_dereference(tcp_metrics_hash[hash].chain);
181 for (tm = rcu_dereference(oldest->tcpm_next); tm; 184 for (tm = rcu_dereference(oldest->tcpm_next); tm;
182 tm = rcu_dereference(tm->tcpm_next)) { 185 tm = rcu_dereference(tm->tcpm_next)) {
183 if (time_before(tm->tcpm_stamp, oldest->tcpm_stamp)) 186 if (time_before(tm->tcpm_stamp, oldest->tcpm_stamp))
@@ -196,8 +199,8 @@ static struct tcp_metrics_block *tcpm_new(struct dst_entry *dst,
196 tcpm_suck_dst(tm, dst, true); 199 tcpm_suck_dst(tm, dst, true);
197 200
198 if (likely(!reclaim)) { 201 if (likely(!reclaim)) {
199 tm->tcpm_next = net->ipv4.tcp_metrics_hash[hash].chain; 202 tm->tcpm_next = tcp_metrics_hash[hash].chain;
200 rcu_assign_pointer(net->ipv4.tcp_metrics_hash[hash].chain, tm); 203 rcu_assign_pointer(tcp_metrics_hash[hash].chain, tm);
201 } 204 }
202 205
203out_unlock: 206out_unlock:
@@ -221,7 +224,7 @@ static struct tcp_metrics_block *__tcp_get_metrics(const struct inetpeer_addr *s
221 struct tcp_metrics_block *tm; 224 struct tcp_metrics_block *tm;
222 int depth = 0; 225 int depth = 0;
223 226
224 for (tm = rcu_dereference(net->ipv4.tcp_metrics_hash[hash].chain); tm; 227 for (tm = rcu_dereference(tcp_metrics_hash[hash].chain); tm;
225 tm = rcu_dereference(tm->tcpm_next)) { 228 tm = rcu_dereference(tm->tcpm_next)) {
226 if (addr_same(&tm->tcpm_saddr, saddr) && 229 if (addr_same(&tm->tcpm_saddr, saddr) &&
227 addr_same(&tm->tcpm_daddr, daddr) && 230 addr_same(&tm->tcpm_daddr, daddr) &&
@@ -261,9 +264,9 @@ static struct tcp_metrics_block *__tcp_get_metrics_req(struct request_sock *req,
261 264
262 net = dev_net(dst->dev); 265 net = dev_net(dst->dev);
263 hash ^= net_hash_mix(net); 266 hash ^= net_hash_mix(net);
264 hash = hash_32(hash, net->ipv4.tcp_metrics_hash_log); 267 hash = hash_32(hash, tcp_metrics_hash_log);
265 268
266 for (tm = rcu_dereference(net->ipv4.tcp_metrics_hash[hash].chain); tm; 269 for (tm = rcu_dereference(tcp_metrics_hash[hash].chain); tm;
267 tm = rcu_dereference(tm->tcpm_next)) { 270 tm = rcu_dereference(tm->tcpm_next)) {
268 if (addr_same(&tm->tcpm_saddr, &saddr) && 271 if (addr_same(&tm->tcpm_saddr, &saddr) &&
269 addr_same(&tm->tcpm_daddr, &daddr) && 272 addr_same(&tm->tcpm_daddr, &daddr) &&
@@ -310,9 +313,9 @@ static struct tcp_metrics_block *__tcp_get_metrics_tw(struct inet_timewait_sock
310 313
311 net = twsk_net(tw); 314 net = twsk_net(tw);
312 hash ^= net_hash_mix(net); 315 hash ^= net_hash_mix(net);
313 hash = hash_32(hash, net->ipv4.tcp_metrics_hash_log); 316 hash = hash_32(hash, tcp_metrics_hash_log);
314 317
315 for (tm = rcu_dereference(net->ipv4.tcp_metrics_hash[hash].chain); tm; 318 for (tm = rcu_dereference(tcp_metrics_hash[hash].chain); tm;
316 tm = rcu_dereference(tm->tcpm_next)) { 319 tm = rcu_dereference(tm->tcpm_next)) {
317 if (addr_same(&tm->tcpm_saddr, &saddr) && 320 if (addr_same(&tm->tcpm_saddr, &saddr) &&
318 addr_same(&tm->tcpm_daddr, &daddr) && 321 addr_same(&tm->tcpm_daddr, &daddr) &&
@@ -360,7 +363,7 @@ static struct tcp_metrics_block *tcp_get_metrics(struct sock *sk,
360 363
361 net = dev_net(dst->dev); 364 net = dev_net(dst->dev);
362 hash ^= net_hash_mix(net); 365 hash ^= net_hash_mix(net);
363 hash = hash_32(hash, net->ipv4.tcp_metrics_hash_log); 366 hash = hash_32(hash, tcp_metrics_hash_log);
364 367
365 tm = __tcp_get_metrics(&saddr, &daddr, net, hash); 368 tm = __tcp_get_metrics(&saddr, &daddr, net, hash);
366 if (tm == TCP_METRICS_RECLAIM_PTR) 369 if (tm == TCP_METRICS_RECLAIM_PTR)
@@ -911,13 +914,13 @@ static int tcp_metrics_nl_dump(struct sk_buff *skb,
911 struct netlink_callback *cb) 914 struct netlink_callback *cb)
912{ 915{
913 struct net *net = sock_net(skb->sk); 916 struct net *net = sock_net(skb->sk);
914 unsigned int max_rows = 1U << net->ipv4.tcp_metrics_hash_log; 917 unsigned int max_rows = 1U << tcp_metrics_hash_log;
915 unsigned int row, s_row = cb->args[0]; 918 unsigned int row, s_row = cb->args[0];
916 int s_col = cb->args[1], col = s_col; 919 int s_col = cb->args[1], col = s_col;
917 920
918 for (row = s_row; row < max_rows; row++, s_col = 0) { 921 for (row = s_row; row < max_rows; row++, s_col = 0) {
919 struct tcp_metrics_block *tm; 922 struct tcp_metrics_block *tm;
920 struct tcpm_hash_bucket *hb = net->ipv4.tcp_metrics_hash + row; 923 struct tcpm_hash_bucket *hb = tcp_metrics_hash + row;
921 924
922 rcu_read_lock(); 925 rcu_read_lock();
923 for (col = 0, tm = rcu_dereference(hb->chain); tm; 926 for (col = 0, tm = rcu_dereference(hb->chain); tm;
@@ -1010,10 +1013,10 @@ static int tcp_metrics_nl_cmd_get(struct sk_buff *skb, struct genl_info *info)
1010 goto nla_put_failure; 1013 goto nla_put_failure;
1011 1014
1012 hash ^= net_hash_mix(net); 1015 hash ^= net_hash_mix(net);
1013 hash = hash_32(hash, net->ipv4.tcp_metrics_hash_log); 1016 hash = hash_32(hash, tcp_metrics_hash_log);
1014 ret = -ESRCH; 1017 ret = -ESRCH;
1015 rcu_read_lock(); 1018 rcu_read_lock();
1016 for (tm = rcu_dereference(net->ipv4.tcp_metrics_hash[hash].chain); tm; 1019 for (tm = rcu_dereference(tcp_metrics_hash[hash].chain); tm;
1017 tm = rcu_dereference(tm->tcpm_next)) { 1020 tm = rcu_dereference(tm->tcpm_next)) {
1018 if (addr_same(&tm->tcpm_daddr, &daddr) && 1021 if (addr_same(&tm->tcpm_daddr, &daddr) &&
1019 (!src || addr_same(&tm->tcpm_saddr, &saddr)) && 1022 (!src || addr_same(&tm->tcpm_saddr, &saddr)) &&
@@ -1045,8 +1048,8 @@ out_free:
1045 1048
1046static void tcp_metrics_flush_all(struct net *net) 1049static void tcp_metrics_flush_all(struct net *net)
1047{ 1050{
1048 unsigned int max_rows = 1U << net->ipv4.tcp_metrics_hash_log; 1051 unsigned int max_rows = 1U << tcp_metrics_hash_log;
1049 struct tcpm_hash_bucket *hb = net->ipv4.tcp_metrics_hash; 1052 struct tcpm_hash_bucket *hb = tcp_metrics_hash;
1050 struct tcp_metrics_block *tm; 1053 struct tcp_metrics_block *tm;
1051 unsigned int row; 1054 unsigned int row;
1052 1055
@@ -1090,8 +1093,8 @@ static int tcp_metrics_nl_cmd_del(struct sk_buff *skb, struct genl_info *info)
1090 src = false; 1093 src = false;
1091 1094
1092 hash ^= net_hash_mix(net); 1095 hash ^= net_hash_mix(net);
1093 hash = hash_32(hash, net->ipv4.tcp_metrics_hash_log); 1096 hash = hash_32(hash, tcp_metrics_hash_log);
1094 hb = net->ipv4.tcp_metrics_hash + hash; 1097 hb = tcp_metrics_hash + hash;
1095 pp = &hb->chain; 1098 pp = &hb->chain;
1096 spin_lock_bh(&tcp_metrics_lock); 1099 spin_lock_bh(&tcp_metrics_lock);
1097 for (tm = deref_locked_genl(*pp); tm; tm = deref_locked_genl(*pp)) { 1100 for (tm = deref_locked_genl(*pp); tm; tm = deref_locked_genl(*pp)) {
@@ -1147,6 +1150,9 @@ static int __net_init tcp_net_metrics_init(struct net *net)
1147 size_t size; 1150 size_t size;
1148 unsigned int slots; 1151 unsigned int slots;
1149 1152
1153 if (!net_eq(net, &init_net))
1154 return 0;
1155
1150 slots = tcpmhash_entries; 1156 slots = tcpmhash_entries;
1151 if (!slots) { 1157 if (!slots) {
1152 if (totalram_pages >= 128 * 1024) 1158 if (totalram_pages >= 128 * 1024)
@@ -1155,14 +1161,14 @@ static int __net_init tcp_net_metrics_init(struct net *net)
1155 slots = 8 * 1024; 1161 slots = 8 * 1024;
1156 } 1162 }
1157 1163
1158 net->ipv4.tcp_metrics_hash_log = order_base_2(slots); 1164 tcp_metrics_hash_log = order_base_2(slots);
1159 size = sizeof(struct tcpm_hash_bucket) << net->ipv4.tcp_metrics_hash_log; 1165 size = sizeof(struct tcpm_hash_bucket) << tcp_metrics_hash_log;
1160 1166
1161 net->ipv4.tcp_metrics_hash = kzalloc(size, GFP_KERNEL | __GFP_NOWARN); 1167 tcp_metrics_hash = kzalloc(size, GFP_KERNEL | __GFP_NOWARN);
1162 if (!net->ipv4.tcp_metrics_hash) 1168 if (!tcp_metrics_hash)
1163 net->ipv4.tcp_metrics_hash = vzalloc(size); 1169 tcp_metrics_hash = vzalloc(size);
1164 1170
1165 if (!net->ipv4.tcp_metrics_hash) 1171 if (!tcp_metrics_hash)
1166 return -ENOMEM; 1172 return -ENOMEM;
1167 1173
1168 return 0; 1174 return 0;
@@ -1170,19 +1176,7 @@ static int __net_init tcp_net_metrics_init(struct net *net)
1170 1176
1171static void __net_exit tcp_net_metrics_exit(struct net *net) 1177static void __net_exit tcp_net_metrics_exit(struct net *net)
1172{ 1178{
1173 unsigned int i; 1179 tcp_metrics_flush_all(net);
1174
1175 for (i = 0; i < (1U << net->ipv4.tcp_metrics_hash_log) ; i++) {
1176 struct tcp_metrics_block *tm, *next;
1177
1178 tm = rcu_dereference_protected(net->ipv4.tcp_metrics_hash[i].chain, 1);
1179 while (tm) {
1180 next = rcu_dereference_protected(tm->tcpm_next, 1);
1181 kfree(tm);
1182 tm = next;
1183 }
1184 }
1185 kvfree(net->ipv4.tcp_metrics_hash);
1186} 1180}
1187 1181
1188static __net_initdata struct pernet_operations tcp_net_metrics_ops = { 1182static __net_initdata struct pernet_operations tcp_net_metrics_ops = {