diff options
author | Stephen Hemminger <shemminger@osdl.org> | 2005-06-23 23:37:36 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2005-06-23 23:37:36 -0400 |
commit | 5f8ef48d240963093451bcf83df89f1a1364f51d (patch) | |
tree | cecb30c2f59778f7f509a84b3aa7ea097c3f2b27 /net/ipv4/tcp_cong.c | |
parent | 51b0bdedb8e784d0d969a6b77151911130812400 (diff) |
[TCP]: Allow choosing TCP congestion control via sockopt.
Allow using setsockopt to set TCP congestion control to use on a per
socket basis.
Signed-off-by: Stephen Hemminger <shemminger@osdl.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4/tcp_cong.c')
-rw-r--r-- | net/ipv4/tcp_cong.c | 46 |
1 files changed, 44 insertions, 2 deletions
diff --git a/net/ipv4/tcp_cong.c b/net/ipv4/tcp_cong.c index 665394a63ae4..4970d10a7785 100644 --- a/net/ipv4/tcp_cong.c +++ b/net/ipv4/tcp_cong.c | |||
@@ -21,7 +21,7 @@ static struct tcp_congestion_ops *tcp_ca_find(const char *name) | |||
21 | { | 21 | { |
22 | struct tcp_congestion_ops *e; | 22 | struct tcp_congestion_ops *e; |
23 | 23 | ||
24 | list_for_each_entry(e, &tcp_cong_list, list) { | 24 | list_for_each_entry_rcu(e, &tcp_cong_list, list) { |
25 | if (strcmp(e->name, name) == 0) | 25 | if (strcmp(e->name, name) == 0) |
26 | return e; | 26 | return e; |
27 | } | 27 | } |
@@ -77,6 +77,9 @@ void tcp_init_congestion_control(struct tcp_sock *tp) | |||
77 | { | 77 | { |
78 | struct tcp_congestion_ops *ca; | 78 | struct tcp_congestion_ops *ca; |
79 | 79 | ||
80 | if (tp->ca_ops != &tcp_init_congestion_ops) | ||
81 | return; | ||
82 | |||
80 | rcu_read_lock(); | 83 | rcu_read_lock(); |
81 | list_for_each_entry_rcu(ca, &tcp_cong_list, list) { | 84 | list_for_each_entry_rcu(ca, &tcp_cong_list, list) { |
82 | if (try_module_get(ca->owner)) { | 85 | if (try_module_get(ca->owner)) { |
@@ -139,6 +142,34 @@ void tcp_get_default_congestion_control(char *name) | |||
139 | rcu_read_unlock(); | 142 | rcu_read_unlock(); |
140 | } | 143 | } |
141 | 144 | ||
145 | /* Change congestion control for socket */ | ||
146 | int tcp_set_congestion_control(struct tcp_sock *tp, const char *name) | ||
147 | { | ||
148 | struct tcp_congestion_ops *ca; | ||
149 | int err = 0; | ||
150 | |||
151 | rcu_read_lock(); | ||
152 | ca = tcp_ca_find(name); | ||
153 | if (ca == tp->ca_ops) | ||
154 | goto out; | ||
155 | |||
156 | if (!ca) | ||
157 | err = -ENOENT; | ||
158 | |||
159 | else if (!try_module_get(ca->owner)) | ||
160 | err = -EBUSY; | ||
161 | |||
162 | else { | ||
163 | tcp_cleanup_congestion_control(tp); | ||
164 | tp->ca_ops = ca; | ||
165 | if (tp->ca_ops->init) | ||
166 | tp->ca_ops->init(tp); | ||
167 | } | ||
168 | out: | ||
169 | rcu_read_unlock(); | ||
170 | return err; | ||
171 | } | ||
172 | |||
142 | /* | 173 | /* |
143 | * TCP Reno congestion control | 174 | * TCP Reno congestion control |
144 | * This is special case used for fallback as well. | 175 | * This is special case used for fallback as well. |
@@ -192,4 +223,15 @@ struct tcp_congestion_ops tcp_reno = { | |||
192 | .min_cwnd = tcp_reno_min_cwnd, | 223 | .min_cwnd = tcp_reno_min_cwnd, |
193 | }; | 224 | }; |
194 | 225 | ||
195 | EXPORT_SYMBOL_GPL(tcp_reno); | 226 | /* Initial congestion control used (until SYN) |
227 | * really reno under another name so we can tell difference | ||
228 | * during tcp_set_default_congestion_control | ||
229 | */ | ||
230 | struct tcp_congestion_ops tcp_init_congestion_ops = { | ||
231 | .name = "", | ||
232 | .owner = THIS_MODULE, | ||
233 | .ssthresh = tcp_reno_ssthresh, | ||
234 | .cong_avoid = tcp_reno_cong_avoid, | ||
235 | .min_cwnd = tcp_reno_min_cwnd, | ||
236 | }; | ||
237 | EXPORT_SYMBOL_GPL(tcp_init_congestion_ops); | ||