Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
ip_vs_sched.c
Go to the documentation of this file.
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 <[email protected]>
9  * Peter Kese <[email protected]>
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 
33 /*
34  * IPVS scheduler list
35  */
36 static LIST_HEAD(ip_vs_schedulers);
37 
38 /* lock for service table */
39 static DEFINE_SPINLOCK(ip_vs_sched_lock);
40 
41 
42 /*
43  * Bind a service with a scheduler
44  */
46  struct ip_vs_scheduler *scheduler)
47 {
48  int ret;
49 
50  svc->scheduler = scheduler;
51 
52  if (scheduler->init_service) {
53  ret = scheduler->init_service(svc);
54  if (ret) {
55  pr_err("%s(): init error\n", __func__);
56  return ret;
57  }
58  }
59 
60  return 0;
61 }
62 
63 
64 /*
65  * Unbind a service with its scheduler
66  */
68 {
69  struct ip_vs_scheduler *sched = svc->scheduler;
70 
71  if (!sched)
72  return 0;
73 
74  if (sched->done_service) {
75  if (sched->done_service(svc) != 0) {
76  pr_err("%s(): done error\n", __func__);
77  return -EINVAL;
78  }
79  }
80 
81  svc->scheduler = NULL;
82  return 0;
83 }
84 
85 
86 /*
87  * Get scheduler in the scheduler list by name
88  */
89 static struct ip_vs_scheduler *ip_vs_sched_getbyname(const char *sched_name)
90 {
91  struct ip_vs_scheduler *sched;
92 
93  IP_VS_DBG(2, "%s(): sched_name \"%s\"\n", __func__, sched_name);
94 
95  spin_lock_bh(&ip_vs_sched_lock);
96 
97  list_for_each_entry(sched, &ip_vs_schedulers, n_list) {
98  /*
99  * Test and get the modules atomically
100  */
101  if (sched->module && !try_module_get(sched->module)) {
102  /*
103  * This scheduler is just deleted
104  */
105  continue;
106  }
107  if (strcmp(sched_name, sched->name)==0) {
108  /* HIT */
109  spin_unlock_bh(&ip_vs_sched_lock);
110  return sched;
111  }
112  if (sched->module)
113  module_put(sched->module);
114  }
115 
116  spin_unlock_bh(&ip_vs_sched_lock);
117  return NULL;
118 }
119 
120 
121 /*
122  * Lookup scheduler and try to load it if it doesn't exist
123  */
124 struct ip_vs_scheduler *ip_vs_scheduler_get(const char *sched_name)
125 {
126  struct ip_vs_scheduler *sched;
127 
128  /*
129  * Search for the scheduler by sched_name
130  */
131  sched = ip_vs_sched_getbyname(sched_name);
132 
133  /*
134  * If scheduler not found, load the module and search again
135  */
136  if (sched == NULL) {
137  request_module("ip_vs_%s", sched_name);
138  sched = ip_vs_sched_getbyname(sched_name);
139  }
140 
141  return sched;
142 }
143 
145 {
146  if (scheduler && scheduler->module)
147  module_put(scheduler->module);
148 }
149 
150 /*
151  * Common error output helper for schedulers
152  */
153 
154 void ip_vs_scheduler_err(struct ip_vs_service *svc, const char *msg)
155 {
156  if (svc->fwmark) {
157  IP_VS_ERR_RL("%s: FWM %u 0x%08X - %s\n",
158  svc->scheduler->name, svc->fwmark,
159  svc->fwmark, msg);
160 #ifdef CONFIG_IP_VS_IPV6
161  } else if (svc->af == AF_INET6) {
162  IP_VS_ERR_RL("%s: %s [%pI6]:%d - %s\n",
163  svc->scheduler->name,
165  &svc->addr.in6, ntohs(svc->port), msg);
166 #endif
167  } else {
168  IP_VS_ERR_RL("%s: %s %pI4:%d - %s\n",
169  svc->scheduler->name,
171  &svc->addr.ip, ntohs(svc->port), msg);
172  }
173 }
174 
175 /*
176  * Register a scheduler in the scheduler list
177  */
179 {
180  struct ip_vs_scheduler *sched;
181 
182  if (!scheduler) {
183  pr_err("%s(): NULL arg\n", __func__);
184  return -EINVAL;
185  }
186 
187  if (!scheduler->name) {
188  pr_err("%s(): NULL scheduler_name\n", __func__);
189  return -EINVAL;
190  }
191 
192  /* increase the module use count */
194 
195  spin_lock_bh(&ip_vs_sched_lock);
196 
197  if (!list_empty(&scheduler->n_list)) {
198  spin_unlock_bh(&ip_vs_sched_lock);
200  pr_err("%s(): [%s] scheduler already linked\n",
201  __func__, scheduler->name);
202  return -EINVAL;
203  }
204 
205  /*
206  * Make sure that the scheduler with this name doesn't exist
207  * in the scheduler list.
208  */
209  list_for_each_entry(sched, &ip_vs_schedulers, n_list) {
210  if (strcmp(scheduler->name, sched->name) == 0) {
211  spin_unlock_bh(&ip_vs_sched_lock);
213  pr_err("%s(): [%s] scheduler already existed "
214  "in the system\n", __func__, scheduler->name);
215  return -EINVAL;
216  }
217  }
218  /*
219  * Add it into the d-linked scheduler list
220  */
221  list_add(&scheduler->n_list, &ip_vs_schedulers);
222  spin_unlock_bh(&ip_vs_sched_lock);
223 
224  pr_info("[%s] scheduler registered.\n", scheduler->name);
225 
226  return 0;
227 }
228 
229 
230 /*
231  * Unregister a scheduler from the scheduler list
232  */
234 {
235  if (!scheduler) {
236  pr_err("%s(): NULL arg\n", __func__);
237  return -EINVAL;
238  }
239 
240  spin_lock_bh(&ip_vs_sched_lock);
241  if (list_empty(&scheduler->n_list)) {
242  spin_unlock_bh(&ip_vs_sched_lock);
243  pr_err("%s(): [%s] scheduler is not in the list. failed\n",
244  __func__, scheduler->name);
245  return -EINVAL;
246  }
247 
248  /*
249  * Remove it from the d-linked scheduler list
250  */
251  list_del(&scheduler->n_list);
252  spin_unlock_bh(&ip_vs_sched_lock);
253 
254  /* decrease the module use count */
256 
257  pr_info("[%s] scheduler unregistered.\n", scheduler->name);
258 
259  return 0;
260 }