diff options
Diffstat (limited to 'net/ipv4/tcp_cong.c')
-rw-r--r-- | net/ipv4/tcp_cong.c | 76 |
1 files changed, 36 insertions, 40 deletions
diff --git a/net/ipv4/tcp_cong.c b/net/ipv4/tcp_cong.c index 2f26124fd160..bc6c02f16243 100644 --- a/net/ipv4/tcp_cong.c +++ b/net/ipv4/tcp_cong.c | |||
@@ -33,9 +33,11 @@ static struct tcp_congestion_ops *tcp_ca_find(const char *name) | |||
33 | } | 33 | } |
34 | 34 | ||
35 | /* Must be called with rcu lock held */ | 35 | /* Must be called with rcu lock held */ |
36 | static const struct tcp_congestion_ops *__tcp_ca_find_autoload(const char *name) | 36 | static struct tcp_congestion_ops *tcp_ca_find_autoload(struct net *net, |
37 | const char *name) | ||
37 | { | 38 | { |
38 | const struct tcp_congestion_ops *ca = tcp_ca_find(name); | 39 | struct tcp_congestion_ops *ca = tcp_ca_find(name); |
40 | |||
39 | #ifdef CONFIG_MODULES | 41 | #ifdef CONFIG_MODULES |
40 | if (!ca && capable(CAP_NET_ADMIN)) { | 42 | if (!ca && capable(CAP_NET_ADMIN)) { |
41 | rcu_read_unlock(); | 43 | rcu_read_unlock(); |
@@ -115,7 +117,7 @@ void tcp_unregister_congestion_control(struct tcp_congestion_ops *ca) | |||
115 | } | 117 | } |
116 | EXPORT_SYMBOL_GPL(tcp_unregister_congestion_control); | 118 | EXPORT_SYMBOL_GPL(tcp_unregister_congestion_control); |
117 | 119 | ||
118 | u32 tcp_ca_get_key_by_name(const char *name, bool *ecn_ca) | 120 | u32 tcp_ca_get_key_by_name(struct net *net, const char *name, bool *ecn_ca) |
119 | { | 121 | { |
120 | const struct tcp_congestion_ops *ca; | 122 | const struct tcp_congestion_ops *ca; |
121 | u32 key = TCP_CA_UNSPEC; | 123 | u32 key = TCP_CA_UNSPEC; |
@@ -123,7 +125,7 @@ u32 tcp_ca_get_key_by_name(const char *name, bool *ecn_ca) | |||
123 | might_sleep(); | 125 | might_sleep(); |
124 | 126 | ||
125 | rcu_read_lock(); | 127 | rcu_read_lock(); |
126 | ca = __tcp_ca_find_autoload(name); | 128 | ca = tcp_ca_find_autoload(net, name); |
127 | if (ca) { | 129 | if (ca) { |
128 | key = ca->key; | 130 | key = ca->key; |
129 | *ecn_ca = ca->flags & TCP_CONG_NEEDS_ECN; | 131 | *ecn_ca = ca->flags & TCP_CONG_NEEDS_ECN; |
@@ -153,23 +155,18 @@ EXPORT_SYMBOL_GPL(tcp_ca_get_name_by_key); | |||
153 | /* Assign choice of congestion control. */ | 155 | /* Assign choice of congestion control. */ |
154 | void tcp_assign_congestion_control(struct sock *sk) | 156 | void tcp_assign_congestion_control(struct sock *sk) |
155 | { | 157 | { |
158 | struct net *net = sock_net(sk); | ||
156 | struct inet_connection_sock *icsk = inet_csk(sk); | 159 | struct inet_connection_sock *icsk = inet_csk(sk); |
157 | struct tcp_congestion_ops *ca; | 160 | const struct tcp_congestion_ops *ca; |
158 | 161 | ||
159 | rcu_read_lock(); | 162 | rcu_read_lock(); |
160 | list_for_each_entry_rcu(ca, &tcp_cong_list, list) { | 163 | ca = rcu_dereference(net->ipv4.tcp_congestion_control); |
161 | if (likely(try_module_get(ca->owner))) { | 164 | if (unlikely(!try_module_get(ca->owner))) |
162 | icsk->icsk_ca_ops = ca; | 165 | ca = &tcp_reno; |
163 | goto out; | 166 | icsk->icsk_ca_ops = ca; |
164 | } | ||
165 | /* Fallback to next available. The last really | ||
166 | * guaranteed fallback is Reno from this list. | ||
167 | */ | ||
168 | } | ||
169 | out: | ||
170 | rcu_read_unlock(); | 167 | rcu_read_unlock(); |
171 | memset(icsk->icsk_ca_priv, 0, sizeof(icsk->icsk_ca_priv)); | ||
172 | 168 | ||
169 | memset(icsk->icsk_ca_priv, 0, sizeof(icsk->icsk_ca_priv)); | ||
173 | if (ca->flags & TCP_CONG_NEEDS_ECN) | 170 | if (ca->flags & TCP_CONG_NEEDS_ECN) |
174 | INET_ECN_xmit(sk); | 171 | INET_ECN_xmit(sk); |
175 | else | 172 | else |
@@ -214,29 +211,27 @@ void tcp_cleanup_congestion_control(struct sock *sk) | |||
214 | } | 211 | } |
215 | 212 | ||
216 | /* Used by sysctl to change default congestion control */ | 213 | /* Used by sysctl to change default congestion control */ |
217 | int tcp_set_default_congestion_control(const char *name) | 214 | int tcp_set_default_congestion_control(struct net *net, const char *name) |
218 | { | 215 | { |
219 | struct tcp_congestion_ops *ca; | 216 | struct tcp_congestion_ops *ca; |
220 | int ret = -ENOENT; | 217 | const struct tcp_congestion_ops *prev; |
221 | 218 | int ret; | |
222 | spin_lock(&tcp_cong_list_lock); | ||
223 | ca = tcp_ca_find(name); | ||
224 | #ifdef CONFIG_MODULES | ||
225 | if (!ca && capable(CAP_NET_ADMIN)) { | ||
226 | spin_unlock(&tcp_cong_list_lock); | ||
227 | 219 | ||
228 | request_module("tcp_%s", name); | 220 | rcu_read_lock(); |
229 | spin_lock(&tcp_cong_list_lock); | 221 | ca = tcp_ca_find_autoload(net, name); |
230 | ca = tcp_ca_find(name); | 222 | if (!ca) { |
231 | } | 223 | ret = -ENOENT; |
232 | #endif | 224 | } else if (!try_module_get(ca->owner)) { |
225 | ret = -EBUSY; | ||
226 | } else { | ||
227 | prev = xchg(&net->ipv4.tcp_congestion_control, ca); | ||
228 | if (prev) | ||
229 | module_put(prev->owner); | ||
233 | 230 | ||
234 | if (ca) { | 231 | ca->flags |= TCP_CONG_NON_RESTRICTED; |
235 | ca->flags |= TCP_CONG_NON_RESTRICTED; /* default is always allowed */ | ||
236 | list_move(&ca->list, &tcp_cong_list); | ||
237 | ret = 0; | 232 | ret = 0; |
238 | } | 233 | } |
239 | spin_unlock(&tcp_cong_list_lock); | 234 | rcu_read_unlock(); |
240 | 235 | ||
241 | return ret; | 236 | return ret; |
242 | } | 237 | } |
@@ -244,7 +239,8 @@ int tcp_set_default_congestion_control(const char *name) | |||
244 | /* Set default value from kernel configuration at bootup */ | 239 | /* Set default value from kernel configuration at bootup */ |
245 | static int __init tcp_congestion_default(void) | 240 | static int __init tcp_congestion_default(void) |
246 | { | 241 | { |
247 | return tcp_set_default_congestion_control(CONFIG_DEFAULT_TCP_CONG); | 242 | return tcp_set_default_congestion_control(&init_net, |
243 | CONFIG_DEFAULT_TCP_CONG); | ||
248 | } | 244 | } |
249 | late_initcall(tcp_congestion_default); | 245 | late_initcall(tcp_congestion_default); |
250 | 246 | ||
@@ -264,14 +260,12 @@ void tcp_get_available_congestion_control(char *buf, size_t maxlen) | |||
264 | } | 260 | } |
265 | 261 | ||
266 | /* Get current default congestion control */ | 262 | /* Get current default congestion control */ |
267 | void tcp_get_default_congestion_control(char *name) | 263 | void tcp_get_default_congestion_control(struct net *net, char *name) |
268 | { | 264 | { |
269 | struct tcp_congestion_ops *ca; | 265 | const struct tcp_congestion_ops *ca; |
270 | /* We will always have reno... */ | ||
271 | BUG_ON(list_empty(&tcp_cong_list)); | ||
272 | 266 | ||
273 | rcu_read_lock(); | 267 | rcu_read_lock(); |
274 | ca = list_entry(tcp_cong_list.next, struct tcp_congestion_ops, list); | 268 | ca = rcu_dereference(net->ipv4.tcp_congestion_control); |
275 | strncpy(name, ca->name, TCP_CA_NAME_MAX); | 269 | strncpy(name, ca->name, TCP_CA_NAME_MAX); |
276 | rcu_read_unlock(); | 270 | rcu_read_unlock(); |
277 | } | 271 | } |
@@ -351,12 +345,14 @@ int tcp_set_congestion_control(struct sock *sk, const char *name, bool load, boo | |||
351 | if (!load) | 345 | if (!load) |
352 | ca = tcp_ca_find(name); | 346 | ca = tcp_ca_find(name); |
353 | else | 347 | else |
354 | ca = __tcp_ca_find_autoload(name); | 348 | ca = tcp_ca_find_autoload(sock_net(sk), name); |
349 | |||
355 | /* No change asking for existing value */ | 350 | /* No change asking for existing value */ |
356 | if (ca == icsk->icsk_ca_ops) { | 351 | if (ca == icsk->icsk_ca_ops) { |
357 | icsk->icsk_ca_setsockopt = 1; | 352 | icsk->icsk_ca_setsockopt = 1; |
358 | goto out; | 353 | goto out; |
359 | } | 354 | } |
355 | |||
360 | if (!ca) { | 356 | if (!ca) { |
361 | err = -ENOENT; | 357 | err = -ENOENT; |
362 | } else if (!load) { | 358 | } else if (!load) { |