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