Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * IPVS An implementation of the IP virtual server support for the | |
3 | * LINUX operating system. IPVS is now implemented as a module | |
4 | * over the Netfilter framework. IPVS can be used to build a | |
5 | * high-performance and highly available server based on a | |
6 | * cluster of servers. | |
7 | * | |
1da177e4 LT |
8 | * Authors: Wensong Zhang <wensong@linuxvirtualserver.org> |
9 | * Peter Kese <peter.kese@ijs.si> | |
10 | * | |
11 | * This program is free software; you can redistribute it and/or | |
12 | * modify it under the terms of the GNU General Public License | |
13 | * as published by the Free Software Foundation; either version | |
14 | * 2 of the License, or (at your option) any later version. | |
15 | * | |
16 | * Changes: | |
17 | * | |
18 | */ | |
19 | ||
20 | #include <linux/module.h> | |
1da177e4 | 21 | #include <linux/spinlock.h> |
4ffd2e49 | 22 | #include <linux/interrupt.h> |
1da177e4 LT |
23 | #include <asm/string.h> |
24 | #include <linux/kmod.h> | |
90754f8e | 25 | #include <linux/sysctl.h> |
1da177e4 LT |
26 | |
27 | #include <net/ip_vs.h> | |
28 | ||
29 | /* | |
30 | * IPVS scheduler list | |
31 | */ | |
32 | static LIST_HEAD(ip_vs_schedulers); | |
33 | ||
34 | /* lock for service table */ | |
35 | static DEFINE_RWLOCK(__ip_vs_sched_lock); | |
36 | ||
37 | ||
38 | /* | |
39 | * Bind a service with a scheduler | |
40 | */ | |
41 | int ip_vs_bind_scheduler(struct ip_vs_service *svc, | |
42 | struct ip_vs_scheduler *scheduler) | |
43 | { | |
44 | int ret; | |
45 | ||
46 | if (svc == NULL) { | |
47 | IP_VS_ERR("ip_vs_bind_scheduler(): svc arg NULL\n"); | |
48 | return -EINVAL; | |
49 | } | |
50 | if (scheduler == NULL) { | |
51 | IP_VS_ERR("ip_vs_bind_scheduler(): scheduler arg NULL\n"); | |
52 | return -EINVAL; | |
53 | } | |
54 | ||
55 | svc->scheduler = scheduler; | |
56 | ||
57 | if (scheduler->init_service) { | |
58 | ret = scheduler->init_service(svc); | |
59 | if (ret) { | |
60 | IP_VS_ERR("ip_vs_bind_scheduler(): init error\n"); | |
61 | return ret; | |
62 | } | |
63 | } | |
64 | ||
65 | return 0; | |
66 | } | |
67 | ||
68 | ||
69 | /* | |
70 | * Unbind a service with its scheduler | |
71 | */ | |
72 | int ip_vs_unbind_scheduler(struct ip_vs_service *svc) | |
73 | { | |
74 | struct ip_vs_scheduler *sched; | |
75 | ||
76 | if (svc == NULL) { | |
77 | IP_VS_ERR("ip_vs_unbind_scheduler(): svc arg NULL\n"); | |
78 | return -EINVAL; | |
79 | } | |
80 | ||
81 | sched = svc->scheduler; | |
82 | if (sched == NULL) { | |
83 | IP_VS_ERR("ip_vs_unbind_scheduler(): svc isn't bound\n"); | |
84 | return -EINVAL; | |
85 | } | |
86 | ||
87 | if (sched->done_service) { | |
88 | if (sched->done_service(svc) != 0) { | |
89 | IP_VS_ERR("ip_vs_unbind_scheduler(): done error\n"); | |
90 | return -EINVAL; | |
91 | } | |
92 | } | |
93 | ||
94 | svc->scheduler = NULL; | |
95 | return 0; | |
96 | } | |
97 | ||
98 | ||
99 | /* | |
100 | * Get scheduler in the scheduler list by name | |
101 | */ | |
102 | static struct ip_vs_scheduler *ip_vs_sched_getbyname(const char *sched_name) | |
103 | { | |
104 | struct ip_vs_scheduler *sched; | |
105 | ||
106 | IP_VS_DBG(2, "ip_vs_sched_getbyname(): sched_name \"%s\"\n", | |
107 | sched_name); | |
108 | ||
109 | read_lock_bh(&__ip_vs_sched_lock); | |
110 | ||
111 | list_for_each_entry(sched, &ip_vs_schedulers, n_list) { | |
112 | /* | |
113 | * Test and get the modules atomically | |
114 | */ | |
115 | if (sched->module && !try_module_get(sched->module)) { | |
116 | /* | |
117 | * This scheduler is just deleted | |
118 | */ | |
119 | continue; | |
120 | } | |
121 | if (strcmp(sched_name, sched->name)==0) { | |
122 | /* HIT */ | |
123 | read_unlock_bh(&__ip_vs_sched_lock); | |
124 | return sched; | |
125 | } | |
126 | if (sched->module) | |
127 | module_put(sched->module); | |
128 | } | |
129 | ||
130 | read_unlock_bh(&__ip_vs_sched_lock); | |
131 | return NULL; | |
132 | } | |
133 | ||
134 | ||
135 | /* | |
136 | * Lookup scheduler and try to load it if it doesn't exist | |
137 | */ | |
138 | struct ip_vs_scheduler *ip_vs_scheduler_get(const char *sched_name) | |
139 | { | |
140 | struct ip_vs_scheduler *sched; | |
141 | ||
142 | /* | |
143 | * Search for the scheduler by sched_name | |
144 | */ | |
145 | sched = ip_vs_sched_getbyname(sched_name); | |
146 | ||
147 | /* | |
148 | * If scheduler not found, load the module and search again | |
149 | */ | |
150 | if (sched == NULL) { | |
151 | request_module("ip_vs_%s", sched_name); | |
152 | sched = ip_vs_sched_getbyname(sched_name); | |
153 | } | |
154 | ||
155 | return sched; | |
156 | } | |
157 | ||
158 | void ip_vs_scheduler_put(struct ip_vs_scheduler *scheduler) | |
159 | { | |
160 | if (scheduler->module) | |
161 | module_put(scheduler->module); | |
162 | } | |
163 | ||
164 | ||
165 | /* | |
166 | * Register a scheduler in the scheduler list | |
167 | */ | |
168 | int register_ip_vs_scheduler(struct ip_vs_scheduler *scheduler) | |
169 | { | |
170 | struct ip_vs_scheduler *sched; | |
171 | ||
172 | if (!scheduler) { | |
173 | IP_VS_ERR("register_ip_vs_scheduler(): NULL arg\n"); | |
174 | return -EINVAL; | |
175 | } | |
176 | ||
177 | if (!scheduler->name) { | |
178 | IP_VS_ERR("register_ip_vs_scheduler(): NULL scheduler_name\n"); | |
179 | return -EINVAL; | |
180 | } | |
181 | ||
182 | /* increase the module use count */ | |
183 | ip_vs_use_count_inc(); | |
184 | ||
1da177e4 LT |
185 | write_lock_bh(&__ip_vs_sched_lock); |
186 | ||
187 | if (scheduler->n_list.next != &scheduler->n_list) { | |
188 | write_unlock_bh(&__ip_vs_sched_lock); | |
189 | ip_vs_use_count_dec(); | |
190 | IP_VS_ERR("register_ip_vs_scheduler(): [%s] scheduler " | |
191 | "already linked\n", scheduler->name); | |
192 | return -EINVAL; | |
193 | } | |
194 | ||
4ac63ad6 PE |
195 | /* |
196 | * Make sure that the scheduler with this name doesn't exist | |
197 | * in the scheduler list. | |
198 | */ | |
199 | list_for_each_entry(sched, &ip_vs_schedulers, n_list) { | |
200 | if (strcmp(scheduler->name, sched->name) == 0) { | |
201 | write_unlock_bh(&__ip_vs_sched_lock); | |
202 | ip_vs_use_count_dec(); | |
203 | IP_VS_ERR("register_ip_vs_scheduler(): [%s] scheduler " | |
204 | "already existed in the system\n", | |
205 | scheduler->name); | |
206 | return -EINVAL; | |
207 | } | |
208 | } | |
1da177e4 LT |
209 | /* |
210 | * Add it into the d-linked scheduler list | |
211 | */ | |
212 | list_add(&scheduler->n_list, &ip_vs_schedulers); | |
213 | write_unlock_bh(&__ip_vs_sched_lock); | |
214 | ||
215 | IP_VS_INFO("[%s] scheduler registered.\n", scheduler->name); | |
216 | ||
217 | return 0; | |
218 | } | |
219 | ||
220 | ||
221 | /* | |
222 | * Unregister a scheduler from the scheduler list | |
223 | */ | |
224 | int unregister_ip_vs_scheduler(struct ip_vs_scheduler *scheduler) | |
225 | { | |
226 | if (!scheduler) { | |
227 | IP_VS_ERR( "unregister_ip_vs_scheduler(): NULL arg\n"); | |
228 | return -EINVAL; | |
229 | } | |
230 | ||
231 | write_lock_bh(&__ip_vs_sched_lock); | |
232 | if (scheduler->n_list.next == &scheduler->n_list) { | |
233 | write_unlock_bh(&__ip_vs_sched_lock); | |
234 | IP_VS_ERR("unregister_ip_vs_scheduler(): [%s] scheduler " | |
235 | "is not in the list. failed\n", scheduler->name); | |
236 | return -EINVAL; | |
237 | } | |
238 | ||
239 | /* | |
240 | * Remove it from the d-linked scheduler list | |
241 | */ | |
242 | list_del(&scheduler->n_list); | |
243 | write_unlock_bh(&__ip_vs_sched_lock); | |
244 | ||
245 | /* decrease the module use count */ | |
246 | ip_vs_use_count_dec(); | |
247 | ||
248 | IP_VS_INFO("[%s] scheduler unregistered.\n", scheduler->name); | |
249 | ||
250 | return 0; | |
251 | } |