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