diff options
author | Pavel Emelyanov <xemul@openvz.org> | 2007-12-04 03:45:06 -0500 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2007-12-05 08:37:27 -0500 |
commit | 4ac63ad6c52e9cdefbcb54ec4575ab12b78b49d9 (patch) | |
tree | e8ff9d5a9bbe01c68a95f71f1707cc24afa770d1 /net | |
parent | a014bc8f0f0a3a0cac4fef656f101cdfb77b71eb (diff) |
[IPVS]: Fix sched registration race when checking for name collision.
The register_ip_vs_scheduler() checks for the scheduler with the
same name under the read-locked __ip_vs_sched_lock, then drops,
takes it for writing and puts the scheduler in list.
This is racy, since we can have a race window between the lock
being re-locked for writing.
The fix is to search the scheduler with the given name right under
the write-locked __ip_vs_sched_lock.
Signed-off-by: Pavel Emelyanov <xemul@openvz.org>
Acked-by: Simon Horman <horms@verge.net.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/ipv4/ipvs/ip_vs_sched.c | 27 |
1 files changed, 14 insertions, 13 deletions
diff --git a/net/ipv4/ipvs/ip_vs_sched.c b/net/ipv4/ipvs/ip_vs_sched.c index 1602304abbf9..432235861908 100644 --- a/net/ipv4/ipvs/ip_vs_sched.c +++ b/net/ipv4/ipvs/ip_vs_sched.c | |||
@@ -183,19 +183,6 @@ int register_ip_vs_scheduler(struct ip_vs_scheduler *scheduler) | |||
183 | /* increase the module use count */ | 183 | /* increase the module use count */ |
184 | ip_vs_use_count_inc(); | 184 | ip_vs_use_count_inc(); |
185 | 185 | ||
186 | /* | ||
187 | * Make sure that the scheduler with this name doesn't exist | ||
188 | * in the scheduler list. | ||
189 | */ | ||
190 | sched = ip_vs_sched_getbyname(scheduler->name); | ||
191 | if (sched) { | ||
192 | ip_vs_scheduler_put(sched); | ||
193 | ip_vs_use_count_dec(); | ||
194 | IP_VS_ERR("register_ip_vs_scheduler(): [%s] scheduler " | ||
195 | "already existed in the system\n", scheduler->name); | ||
196 | return -EINVAL; | ||
197 | } | ||
198 | |||
199 | write_lock_bh(&__ip_vs_sched_lock); | 186 | write_lock_bh(&__ip_vs_sched_lock); |
200 | 187 | ||
201 | if (scheduler->n_list.next != &scheduler->n_list) { | 188 | if (scheduler->n_list.next != &scheduler->n_list) { |
@@ -207,6 +194,20 @@ int register_ip_vs_scheduler(struct ip_vs_scheduler *scheduler) | |||
207 | } | 194 | } |
208 | 195 | ||
209 | /* | 196 | /* |
197 | * Make sure that the scheduler with this name doesn't exist | ||
198 | * in the scheduler list. | ||
199 | */ | ||
200 | list_for_each_entry(sched, &ip_vs_schedulers, n_list) { | ||
201 | if (strcmp(scheduler->name, sched->name) == 0) { | ||
202 | write_unlock_bh(&__ip_vs_sched_lock); | ||
203 | ip_vs_use_count_dec(); | ||
204 | IP_VS_ERR("register_ip_vs_scheduler(): [%s] scheduler " | ||
205 | "already existed in the system\n", | ||
206 | scheduler->name); | ||
207 | return -EINVAL; | ||
208 | } | ||
209 | } | ||
210 | /* | ||
210 | * Add it into the d-linked scheduler list | 211 | * Add it into the d-linked scheduler list |
211 | */ | 212 | */ |
212 | list_add(&scheduler->n_list, &ip_vs_schedulers); | 213 | list_add(&scheduler->n_list, &ip_vs_schedulers); |