diff options
Diffstat (limited to 'net/ipv4/tcp_metrics.c')
-rw-r--r-- | net/ipv4/tcp_metrics.c | 66 |
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 | ||
100 | static struct tcpm_hash_bucket *tcp_metrics_hash __read_mostly; | ||
101 | static unsigned int tcp_metrics_hash_log __read_mostly; | ||
102 | |||
100 | static DEFINE_SPINLOCK(tcp_metrics_lock); | 103 | static DEFINE_SPINLOCK(tcp_metrics_lock); |
101 | 104 | ||
102 | static void tcpm_suck_dst(struct tcp_metrics_block *tm, | 105 | static 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 | ||
203 | out_unlock: | 206 | out_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 | ||
1046 | static void tcp_metrics_flush_all(struct net *net) | 1049 | static 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 | ||
1171 | static void __net_exit tcp_net_metrics_exit(struct net *net) | 1177 | static 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 | ||
1188 | static __net_initdata struct pernet_operations tcp_net_metrics_ops = { | 1182 | static __net_initdata struct pernet_operations tcp_net_metrics_ops = { |