diff options
Diffstat (limited to 'net/ipv4/tcp_cong.c')
-rw-r--r-- | net/ipv4/tcp_cong.c | 91 |
1 files changed, 90 insertions, 1 deletions
diff --git a/net/ipv4/tcp_cong.c b/net/ipv4/tcp_cong.c index 1e2982f4acd4..5ca7723d0798 100644 --- a/net/ipv4/tcp_cong.c +++ b/net/ipv4/tcp_cong.c | |||
@@ -113,7 +113,7 @@ int tcp_set_default_congestion_control(const char *name) | |||
113 | spin_lock(&tcp_cong_list_lock); | 113 | spin_lock(&tcp_cong_list_lock); |
114 | ca = tcp_ca_find(name); | 114 | ca = tcp_ca_find(name); |
115 | #ifdef CONFIG_KMOD | 115 | #ifdef CONFIG_KMOD |
116 | if (!ca) { | 116 | if (!ca && capable(CAP_SYS_MODULE)) { |
117 | spin_unlock(&tcp_cong_list_lock); | 117 | spin_unlock(&tcp_cong_list_lock); |
118 | 118 | ||
119 | request_module("tcp_%s", name); | 119 | request_module("tcp_%s", name); |
@@ -123,6 +123,7 @@ int tcp_set_default_congestion_control(const char *name) | |||
123 | #endif | 123 | #endif |
124 | 124 | ||
125 | if (ca) { | 125 | if (ca) { |
126 | ca->non_restricted = 1; /* default is always allowed */ | ||
126 | list_move(&ca->list, &tcp_cong_list); | 127 | list_move(&ca->list, &tcp_cong_list); |
127 | ret = 0; | 128 | ret = 0; |
128 | } | 129 | } |
@@ -139,6 +140,22 @@ static int __init tcp_congestion_default(void) | |||
139 | late_initcall(tcp_congestion_default); | 140 | late_initcall(tcp_congestion_default); |
140 | 141 | ||
141 | 142 | ||
143 | /* Build string with list of available congestion control values */ | ||
144 | void tcp_get_available_congestion_control(char *buf, size_t maxlen) | ||
145 | { | ||
146 | struct tcp_congestion_ops *ca; | ||
147 | size_t offs = 0; | ||
148 | |||
149 | rcu_read_lock(); | ||
150 | list_for_each_entry_rcu(ca, &tcp_cong_list, list) { | ||
151 | offs += snprintf(buf + offs, maxlen - offs, | ||
152 | "%s%s", | ||
153 | offs == 0 ? "" : " ", ca->name); | ||
154 | |||
155 | } | ||
156 | rcu_read_unlock(); | ||
157 | } | ||
158 | |||
142 | /* Get current default congestion control */ | 159 | /* Get current default congestion control */ |
143 | void tcp_get_default_congestion_control(char *name) | 160 | void tcp_get_default_congestion_control(char *name) |
144 | { | 161 | { |
@@ -152,6 +169,64 @@ void tcp_get_default_congestion_control(char *name) | |||
152 | rcu_read_unlock(); | 169 | rcu_read_unlock(); |
153 | } | 170 | } |
154 | 171 | ||
172 | /* Built list of non-restricted congestion control values */ | ||
173 | void tcp_get_allowed_congestion_control(char *buf, size_t maxlen) | ||
174 | { | ||
175 | struct tcp_congestion_ops *ca; | ||
176 | size_t offs = 0; | ||
177 | |||
178 | *buf = '\0'; | ||
179 | rcu_read_lock(); | ||
180 | list_for_each_entry_rcu(ca, &tcp_cong_list, list) { | ||
181 | if (!ca->non_restricted) | ||
182 | continue; | ||
183 | offs += snprintf(buf + offs, maxlen - offs, | ||
184 | "%s%s", | ||
185 | offs == 0 ? "" : " ", ca->name); | ||
186 | |||
187 | } | ||
188 | rcu_read_unlock(); | ||
189 | } | ||
190 | |||
191 | /* Change list of non-restricted congestion control */ | ||
192 | int tcp_set_allowed_congestion_control(char *val) | ||
193 | { | ||
194 | struct tcp_congestion_ops *ca; | ||
195 | char *clone, *name; | ||
196 | int ret = 0; | ||
197 | |||
198 | clone = kstrdup(val, GFP_USER); | ||
199 | if (!clone) | ||
200 | return -ENOMEM; | ||
201 | |||
202 | spin_lock(&tcp_cong_list_lock); | ||
203 | /* pass 1 check for bad entries */ | ||
204 | while ((name = strsep(&clone, " ")) && *name) { | ||
205 | ca = tcp_ca_find(name); | ||
206 | if (!ca) { | ||
207 | ret = -ENOENT; | ||
208 | goto out; | ||
209 | } | ||
210 | } | ||
211 | |||
212 | /* pass 2 clear */ | ||
213 | list_for_each_entry_rcu(ca, &tcp_cong_list, list) | ||
214 | ca->non_restricted = 0; | ||
215 | |||
216 | /* pass 3 mark as allowed */ | ||
217 | while ((name = strsep(&val, " ")) && *name) { | ||
218 | ca = tcp_ca_find(name); | ||
219 | WARN_ON(!ca); | ||
220 | if (ca) | ||
221 | ca->non_restricted = 1; | ||
222 | } | ||
223 | out: | ||
224 | spin_unlock(&tcp_cong_list_lock); | ||
225 | |||
226 | return ret; | ||
227 | } | ||
228 | |||
229 | |||
155 | /* Change congestion control for socket */ | 230 | /* Change congestion control for socket */ |
156 | int tcp_set_congestion_control(struct sock *sk, const char *name) | 231 | int tcp_set_congestion_control(struct sock *sk, const char *name) |
157 | { | 232 | { |
@@ -161,12 +236,25 @@ int tcp_set_congestion_control(struct sock *sk, const char *name) | |||
161 | 236 | ||
162 | rcu_read_lock(); | 237 | rcu_read_lock(); |
163 | ca = tcp_ca_find(name); | 238 | ca = tcp_ca_find(name); |
239 | /* no change asking for existing value */ | ||
164 | if (ca == icsk->icsk_ca_ops) | 240 | if (ca == icsk->icsk_ca_ops) |
165 | goto out; | 241 | goto out; |
166 | 242 | ||
243 | #ifdef CONFIG_KMOD | ||
244 | /* not found attempt to autoload module */ | ||
245 | if (!ca && capable(CAP_SYS_MODULE)) { | ||
246 | rcu_read_unlock(); | ||
247 | request_module("tcp_%s", name); | ||
248 | rcu_read_lock(); | ||
249 | ca = tcp_ca_find(name); | ||
250 | } | ||
251 | #endif | ||
167 | if (!ca) | 252 | if (!ca) |
168 | err = -ENOENT; | 253 | err = -ENOENT; |
169 | 254 | ||
255 | else if (!(ca->non_restricted || capable(CAP_NET_ADMIN))) | ||
256 | err = -EPERM; | ||
257 | |||
170 | else if (!try_module_get(ca->owner)) | 258 | else if (!try_module_get(ca->owner)) |
171 | err = -EBUSY; | 259 | err = -EBUSY; |
172 | 260 | ||
@@ -268,6 +356,7 @@ EXPORT_SYMBOL_GPL(tcp_reno_min_cwnd); | |||
268 | 356 | ||
269 | struct tcp_congestion_ops tcp_reno = { | 357 | struct tcp_congestion_ops tcp_reno = { |
270 | .name = "reno", | 358 | .name = "reno", |
359 | .non_restricted = 1, | ||
271 | .owner = THIS_MODULE, | 360 | .owner = THIS_MODULE, |
272 | .ssthresh = tcp_reno_ssthresh, | 361 | .ssthresh = tcp_reno_ssthresh, |
273 | .cong_avoid = tcp_reno_cong_avoid, | 362 | .cong_avoid = tcp_reno_cong_avoid, |