]> rtime.felk.cvut.cz Git - mcf548x/linux.git/blob - net/netfilter/ipvs/ip_vs_sched.c
Initial 2.6.37
[mcf548x/linux.git] / net / netfilter / ipvs / ip_vs_sched.c
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  *
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 #define KMSG_COMPONENT "IPVS"
21 #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
22
23 #include <linux/module.h>
24 #include <linux/spinlock.h>
25 #include <linux/interrupt.h>
26 #include <asm/string.h>
27 #include <linux/kmod.h>
28 #include <linux/sysctl.h>
29
30 #include <net/ip_vs.h>
31
32 /*
33  *  IPVS scheduler list
34  */
35 static LIST_HEAD(ip_vs_schedulers);
36
37 /* lock for service table */
38 static DEFINE_SPINLOCK(ip_vs_sched_lock);
39
40
41 /*
42  *  Bind a service with a scheduler
43  */
44 int ip_vs_bind_scheduler(struct ip_vs_service *svc,
45                          struct ip_vs_scheduler *scheduler)
46 {
47         int ret;
48
49         svc->scheduler = scheduler;
50
51         if (scheduler->init_service) {
52                 ret = scheduler->init_service(svc);
53                 if (ret) {
54                         pr_err("%s(): init error\n", __func__);
55                         return ret;
56                 }
57         }
58
59         return 0;
60 }
61
62
63 /*
64  *  Unbind a service with its scheduler
65  */
66 int ip_vs_unbind_scheduler(struct ip_vs_service *svc)
67 {
68         struct ip_vs_scheduler *sched = svc->scheduler;
69
70         if (!sched)
71                 return 0;
72
73         if (sched->done_service) {
74                 if (sched->done_service(svc) != 0) {
75                         pr_err("%s(): done error\n", __func__);
76                         return -EINVAL;
77                 }
78         }
79
80         svc->scheduler = NULL;
81         return 0;
82 }
83
84
85 /*
86  *  Get scheduler in the scheduler list by name
87  */
88 static struct ip_vs_scheduler *ip_vs_sched_getbyname(const char *sched_name)
89 {
90         struct ip_vs_scheduler *sched;
91
92         IP_VS_DBG(2, "%s(): sched_name \"%s\"\n", __func__, sched_name);
93
94         spin_lock_bh(&ip_vs_sched_lock);
95
96         list_for_each_entry(sched, &ip_vs_schedulers, n_list) {
97                 /*
98                  * Test and get the modules atomically
99                  */
100                 if (sched->module && !try_module_get(sched->module)) {
101                         /*
102                          * This scheduler is just deleted
103                          */
104                         continue;
105                 }
106                 if (strcmp(sched_name, sched->name)==0) {
107                         /* HIT */
108                         spin_unlock_bh(&ip_vs_sched_lock);
109                         return sched;
110                 }
111                 if (sched->module)
112                         module_put(sched->module);
113         }
114
115         spin_unlock_bh(&ip_vs_sched_lock);
116         return NULL;
117 }
118
119
120 /*
121  *  Lookup scheduler and try to load it if it doesn't exist
122  */
123 struct ip_vs_scheduler *ip_vs_scheduler_get(const char *sched_name)
124 {
125         struct ip_vs_scheduler *sched;
126
127         /*
128          *  Search for the scheduler by sched_name
129          */
130         sched = ip_vs_sched_getbyname(sched_name);
131
132         /*
133          *  If scheduler not found, load the module and search again
134          */
135         if (sched == NULL) {
136                 request_module("ip_vs_%s", sched_name);
137                 sched = ip_vs_sched_getbyname(sched_name);
138         }
139
140         return sched;
141 }
142
143 void ip_vs_scheduler_put(struct ip_vs_scheduler *scheduler)
144 {
145         if (scheduler && scheduler->module)
146                 module_put(scheduler->module);
147 }
148
149
150 /*
151  *  Register a scheduler in the scheduler list
152  */
153 int register_ip_vs_scheduler(struct ip_vs_scheduler *scheduler)
154 {
155         struct ip_vs_scheduler *sched;
156
157         if (!scheduler) {
158                 pr_err("%s(): NULL arg\n", __func__);
159                 return -EINVAL;
160         }
161
162         if (!scheduler->name) {
163                 pr_err("%s(): NULL scheduler_name\n", __func__);
164                 return -EINVAL;
165         }
166
167         /* increase the module use count */
168         ip_vs_use_count_inc();
169
170         spin_lock_bh(&ip_vs_sched_lock);
171
172         if (!list_empty(&scheduler->n_list)) {
173                 spin_unlock_bh(&ip_vs_sched_lock);
174                 ip_vs_use_count_dec();
175                 pr_err("%s(): [%s] scheduler already linked\n",
176                        __func__, scheduler->name);
177                 return -EINVAL;
178         }
179
180         /*
181          *  Make sure that the scheduler with this name doesn't exist
182          *  in the scheduler list.
183          */
184         list_for_each_entry(sched, &ip_vs_schedulers, n_list) {
185                 if (strcmp(scheduler->name, sched->name) == 0) {
186                         spin_unlock_bh(&ip_vs_sched_lock);
187                         ip_vs_use_count_dec();
188                         pr_err("%s(): [%s] scheduler already existed "
189                                "in the system\n", __func__, scheduler->name);
190                         return -EINVAL;
191                 }
192         }
193         /*
194          *      Add it into the d-linked scheduler list
195          */
196         list_add(&scheduler->n_list, &ip_vs_schedulers);
197         spin_unlock_bh(&ip_vs_sched_lock);
198
199         pr_info("[%s] scheduler registered.\n", scheduler->name);
200
201         return 0;
202 }
203
204
205 /*
206  *  Unregister a scheduler from the scheduler list
207  */
208 int unregister_ip_vs_scheduler(struct ip_vs_scheduler *scheduler)
209 {
210         if (!scheduler) {
211                 pr_err("%s(): NULL arg\n", __func__);
212                 return -EINVAL;
213         }
214
215         spin_lock_bh(&ip_vs_sched_lock);
216         if (list_empty(&scheduler->n_list)) {
217                 spin_unlock_bh(&ip_vs_sched_lock);
218                 pr_err("%s(): [%s] scheduler is not in the list. failed\n",
219                        __func__, scheduler->name);
220                 return -EINVAL;
221         }
222
223         /*
224          *      Remove it from the d-linked scheduler list
225          */
226         list_del(&scheduler->n_list);
227         spin_unlock_bh(&ip_vs_sched_lock);
228
229         /* decrease the module use count */
230         ip_vs_use_count_dec();
231
232         pr_info("[%s] scheduler unregistered.\n", scheduler->name);
233
234         return 0;
235 }