linux/net/ipv4/ipvs/ip_vs_lblcr.c
<<
>>
Prefs
   1/*
   2 * IPVS:        Locality-Based Least-Connection with Replication scheduler
   3 *
   4 * Version:     $Id: ip_vs_lblcr.c,v 1.11 2002/09/15 08:14:08 wensong Exp $
   5 *
   6 * Authors:     Wensong Zhang <wensong@gnuchina.org>
   7 *
   8 *              This program is free software; you can redistribute it and/or
   9 *              modify it under the terms of the GNU General Public License
  10 *              as published by the Free Software Foundation; either version
  11 *              2 of the License, or (at your option) any later version.
  12 *
  13 * Changes:
  14 *     Julian Anastasov        :    Added the missing (dest->weight>0)
  15 *                                  condition in the ip_vs_dest_set_max.
  16 *
  17 */
  18
  19/*
  20 * The lblc/r algorithm is as follows (pseudo code):
  21 *
  22 *       if serverSet[dest_ip] is null then
  23 *               n, serverSet[dest_ip] <- {weighted least-conn node};
  24 *       else
  25 *               n <- {least-conn (alive) node in serverSet[dest_ip]};
  26 *               if (n is null) OR
  27 *                  (n.conns>n.weight AND
  28 *                   there is a node m with m.conns<m.weight/2) then
  29 *                   n <- {weighted least-conn node};
  30 *                   add n to serverSet[dest_ip];
  31 *               if |serverSet[dest_ip]| > 1 AND
  32 *                   now - serverSet[dest_ip].lastMod > T then
  33 *                   m <- {most conn node in serverSet[dest_ip]};
  34 *                   remove m from serverSet[dest_ip];
  35 *       if serverSet[dest_ip] changed then
  36 *               serverSet[dest_ip].lastMod <- now;
  37 *
  38 *       return n;
  39 *
  40 */
  41
  42#include <linux/ip.h>
  43#include <linux/module.h>
  44#include <linux/kernel.h>
  45#include <linux/skbuff.h>
  46#include <linux/jiffies.h>
  47
  48/* for sysctl */
  49#include <linux/fs.h>
  50#include <linux/sysctl.h>
  51#include <net/net_namespace.h>
  52
  53#include <net/ip_vs.h>
  54
  55
  56/*
  57 *    It is for garbage collection of stale IPVS lblcr entries,
  58 *    when the table is full.
  59 */
  60#define CHECK_EXPIRE_INTERVAL   (60*HZ)
  61#define ENTRY_TIMEOUT           (6*60*HZ)
  62
  63/*
  64 *    It is for full expiration check.
  65 *    When there is no partial expiration check (garbage collection)
  66 *    in a half hour, do a full expiration check to collect stale
  67 *    entries that haven't been touched for a day.
  68 */
  69#define COUNT_FOR_FULL_EXPIRATION   30
  70static int sysctl_ip_vs_lblcr_expiration = 24*60*60*HZ;
  71
  72
  73/*
  74 *     for IPVS lblcr entry hash table
  75 */
  76#ifndef CONFIG_IP_VS_LBLCR_TAB_BITS
  77#define CONFIG_IP_VS_LBLCR_TAB_BITS      10
  78#endif
  79#define IP_VS_LBLCR_TAB_BITS     CONFIG_IP_VS_LBLCR_TAB_BITS
  80#define IP_VS_LBLCR_TAB_SIZE     (1 << IP_VS_LBLCR_TAB_BITS)
  81#define IP_VS_LBLCR_TAB_MASK     (IP_VS_LBLCR_TAB_SIZE - 1)
  82
  83
  84/*
  85 *      IPVS destination set structure and operations
  86 */
  87struct ip_vs_dest_list {
  88        struct ip_vs_dest_list  *next;          /* list link */
  89        struct ip_vs_dest       *dest;          /* destination server */
  90};
  91
  92struct ip_vs_dest_set {
  93        atomic_t                size;           /* set size */
  94        unsigned long           lastmod;        /* last modified time */
  95        struct ip_vs_dest_list  *list;          /* destination list */
  96        rwlock_t                lock;           /* lock for this list */
  97};
  98
  99
 100static struct ip_vs_dest_list *
 101ip_vs_dest_set_insert(struct ip_vs_dest_set *set, struct ip_vs_dest *dest)
 102{
 103        struct ip_vs_dest_list *e;
 104
 105        for (e=set->list; e!=NULL; e=e->next) {
 106                if (e->dest == dest)
 107                        /* already existed */
 108                        return NULL;
 109        }
 110
 111        e = kmalloc(sizeof(struct ip_vs_dest_list), GFP_ATOMIC);
 112        if (e == NULL) {
 113                IP_VS_ERR("ip_vs_dest_set_insert(): no memory\n");
 114                return NULL;
 115        }
 116
 117        atomic_inc(&dest->refcnt);
 118        e->dest = dest;
 119
 120        /* link it to the list */
 121        write_lock(&set->lock);
 122        e->next = set->list;
 123        set->list = e;
 124        atomic_inc(&set->size);
 125        write_unlock(&set->lock);
 126
 127        set->lastmod = jiffies;
 128        return e;
 129}
 130
 131static void
 132ip_vs_dest_set_erase(struct ip_vs_dest_set *set, struct ip_vs_dest *dest)
 133{
 134        struct ip_vs_dest_list *e, **ep;
 135
 136        write_lock(&set->lock);
 137        for (ep=&set->list, e=*ep; e!=NULL; e=*ep) {
 138                if (e->dest == dest) {
 139                        /* HIT */
 140                        *ep = e->next;
 141                        atomic_dec(&set->size);
 142                        set->lastmod = jiffies;
 143                        atomic_dec(&e->dest->refcnt);
 144                        kfree(e);
 145                        break;
 146                }
 147                ep = &e->next;
 148        }
 149        write_unlock(&set->lock);
 150}
 151
 152static void ip_vs_dest_set_eraseall(struct ip_vs_dest_set *set)
 153{
 154        struct ip_vs_dest_list *e, **ep;
 155
 156        write_lock(&set->lock);
 157        for (ep=&set->list, e=*ep; e!=NULL; e=*ep) {
 158                *ep = e->next;
 159                /*
 160                 * We don't kfree dest because it is refered either
 161                 * by its service or by the trash dest list.
 162                 */
 163                atomic_dec(&e->dest->refcnt);
 164                kfree(e);
 165        }
 166        write_unlock(&set->lock);
 167}
 168
 169/* get weighted least-connection node in the destination set */
 170static inline struct ip_vs_dest *ip_vs_dest_set_min(struct ip_vs_dest_set *set)
 171{
 172        register struct ip_vs_dest_list *e;
 173        struct ip_vs_dest *dest, *least;
 174        int loh, doh;
 175
 176        if (set == NULL)
 177                return NULL;
 178
 179        read_lock(&set->lock);
 180        /* select the first destination server, whose weight > 0 */
 181        for (e=set->list; e!=NULL; e=e->next) {
 182                least = e->dest;
 183                if (least->flags & IP_VS_DEST_F_OVERLOAD)
 184                        continue;
 185
 186                if ((atomic_read(&least->weight) > 0)
 187                    && (least->flags & IP_VS_DEST_F_AVAILABLE)) {
 188                        loh = atomic_read(&least->activeconns) * 50
 189                                + atomic_read(&least->inactconns);
 190                        goto nextstage;
 191                }
 192        }
 193        read_unlock(&set->lock);
 194        return NULL;
 195
 196        /* find the destination with the weighted least load */
 197  nextstage:
 198        for (e=e->next; e!=NULL; e=e->next) {
 199                dest = e->dest;
 200                if (dest->flags & IP_VS_DEST_F_OVERLOAD)
 201                        continue;
 202
 203                doh = atomic_read(&dest->activeconns) * 50
 204                        + atomic_read(&dest->inactconns);
 205                if ((loh * atomic_read(&dest->weight) >
 206                     doh * atomic_read(&least->weight))
 207                    && (dest->flags & IP_VS_DEST_F_AVAILABLE)) {
 208                        least = dest;
 209                        loh = doh;
 210                }
 211        }
 212        read_unlock(&set->lock);
 213
 214        IP_VS_DBG(6, "ip_vs_dest_set_min: server %d.%d.%d.%d:%d "
 215                  "activeconns %d refcnt %d weight %d overhead %d\n",
 216                  NIPQUAD(least->addr), ntohs(least->port),
 217                  atomic_read(&least->activeconns),
 218                  atomic_read(&least->refcnt),
 219                  atomic_read(&least->weight), loh);
 220        return least;
 221}
 222
 223
 224/* get weighted most-connection node in the destination set */
 225static inline struct ip_vs_dest *ip_vs_dest_set_max(struct ip_vs_dest_set *set)
 226{
 227        register struct ip_vs_dest_list *e;
 228        struct ip_vs_dest *dest, *most;
 229        int moh, doh;
 230
 231        if (set == NULL)
 232                return NULL;
 233
 234        read_lock(&set->lock);
 235        /* select the first destination server, whose weight > 0 */
 236        for (e=set->list; e!=NULL; e=e->next) {
 237                most = e->dest;
 238                if (atomic_read(&most->weight) > 0) {
 239                        moh = atomic_read(&most->activeconns) * 50
 240                                + atomic_read(&most->inactconns);
 241                        goto nextstage;
 242                }
 243        }
 244        read_unlock(&set->lock);
 245        return NULL;
 246
 247        /* find the destination with the weighted most load */
 248  nextstage:
 249        for (e=e->next; e!=NULL; e=e->next) {
 250                dest = e->dest;
 251                doh = atomic_read(&dest->activeconns) * 50
 252                        + atomic_read(&dest->inactconns);
 253                /* moh/mw < doh/dw ==> moh*dw < doh*mw, where mw,dw>0 */
 254                if ((moh * atomic_read(&dest->weight) <
 255                     doh * atomic_read(&most->weight))
 256                    && (atomic_read(&dest->weight) > 0)) {
 257                        most = dest;
 258                        moh = doh;
 259                }
 260        }
 261        read_unlock(&set->lock);
 262
 263        IP_VS_DBG(6, "ip_vs_dest_set_max: server %d.%d.%d.%d:%d "
 264                  "activeconns %d refcnt %d weight %d overhead %d\n",
 265                  NIPQUAD(most->addr), ntohs(most->port),
 266                  atomic_read(&most->activeconns),
 267                  atomic_read(&most->refcnt),
 268                  atomic_read(&most->weight), moh);
 269        return most;
 270}
 271
 272
 273/*
 274 *      IPVS lblcr entry represents an association between destination
 275 *      IP address and its destination server set
 276 */
 277struct ip_vs_lblcr_entry {
 278        struct list_head        list;
 279        __be32                   addr;           /* destination IP address */
 280        struct ip_vs_dest_set   set;            /* destination server set */
 281        unsigned long           lastuse;        /* last used time */
 282};
 283
 284
 285/*
 286 *      IPVS lblcr hash table
 287 */
 288struct ip_vs_lblcr_table {
 289        rwlock_t                lock;           /* lock for this table */
 290        struct list_head        bucket[IP_VS_LBLCR_TAB_SIZE];  /* hash bucket */
 291        atomic_t                entries;        /* number of entries */
 292        int                     max_size;       /* maximum size of entries */
 293        struct timer_list       periodic_timer; /* collect stale entries */
 294        int                     rover;          /* rover for expire check */
 295        int                     counter;        /* counter for no expire */
 296};
 297
 298
 299/*
 300 *      IPVS LBLCR sysctl table
 301 */
 302
 303static ctl_table vs_vars_table[] = {
 304        {
 305                .procname       = "lblcr_expiration",
 306                .data           = &sysctl_ip_vs_lblcr_expiration,
 307                .maxlen         = sizeof(int),
 308                .mode           = 0644,
 309                .proc_handler   = &proc_dointvec_jiffies,
 310        },
 311        { .ctl_name = 0 }
 312};
 313
 314static struct ctl_table_header * sysctl_header;
 315
 316/*
 317 *      new/free a ip_vs_lblcr_entry, which is a mapping of a destination
 318 *      IP address to a server.
 319 */
 320static inline struct ip_vs_lblcr_entry *ip_vs_lblcr_new(__be32 daddr)
 321{
 322        struct ip_vs_lblcr_entry *en;
 323
 324        en = kmalloc(sizeof(struct ip_vs_lblcr_entry), GFP_ATOMIC);
 325        if (en == NULL) {
 326                IP_VS_ERR("ip_vs_lblcr_new(): no memory\n");
 327                return NULL;
 328        }
 329
 330        INIT_LIST_HEAD(&en->list);
 331        en->addr = daddr;
 332
 333        /* initilize its dest set */
 334        atomic_set(&(en->set.size), 0);
 335        en->set.list = NULL;
 336        rwlock_init(&en->set.lock);
 337
 338        return en;
 339}
 340
 341
 342static inline void ip_vs_lblcr_free(struct ip_vs_lblcr_entry *en)
 343{
 344        list_del(&en->list);
 345        ip_vs_dest_set_eraseall(&en->set);
 346        kfree(en);
 347}
 348
 349
 350/*
 351 *      Returns hash value for IPVS LBLCR entry
 352 */
 353static inline unsigned ip_vs_lblcr_hashkey(__be32 addr)
 354{
 355        return (ntohl(addr)*2654435761UL) & IP_VS_LBLCR_TAB_MASK;
 356}
 357
 358
 359/*
 360 *      Hash an entry in the ip_vs_lblcr_table.
 361 *      returns bool success.
 362 */
 363static int
 364ip_vs_lblcr_hash(struct ip_vs_lblcr_table *tbl, struct ip_vs_lblcr_entry *en)
 365{
 366        unsigned hash;
 367
 368        if (!list_empty(&en->list)) {
 369                IP_VS_ERR("ip_vs_lblcr_hash(): request for already hashed, "
 370                          "called from %p\n", __builtin_return_address(0));
 371                return 0;
 372        }
 373
 374        /*
 375         *      Hash by destination IP address
 376         */
 377        hash = ip_vs_lblcr_hashkey(en->addr);
 378
 379        write_lock(&tbl->lock);
 380        list_add(&en->list, &tbl->bucket[hash]);
 381        atomic_inc(&tbl->entries);
 382        write_unlock(&tbl->lock);
 383
 384        return 1;
 385}
 386
 387
 388/*
 389 *  Get ip_vs_lblcr_entry associated with supplied parameters.
 390 */
 391static inline struct ip_vs_lblcr_entry *
 392ip_vs_lblcr_get(struct ip_vs_lblcr_table *tbl, __be32 addr)
 393{
 394        unsigned hash;
 395        struct ip_vs_lblcr_entry *en;
 396
 397        hash = ip_vs_lblcr_hashkey(addr);
 398
 399        read_lock(&tbl->lock);
 400
 401        list_for_each_entry(en, &tbl->bucket[hash], list) {
 402                if (en->addr == addr) {
 403                        /* HIT */
 404                        read_unlock(&tbl->lock);
 405                        return en;
 406                }
 407        }
 408
 409        read_unlock(&tbl->lock);
 410
 411        return NULL;
 412}
 413
 414
 415/*
 416 *      Flush all the entries of the specified table.
 417 */
 418static void ip_vs_lblcr_flush(struct ip_vs_lblcr_table *tbl)
 419{
 420        int i;
 421        struct ip_vs_lblcr_entry *en, *nxt;
 422
 423        for (i=0; i<IP_VS_LBLCR_TAB_SIZE; i++) {
 424                write_lock(&tbl->lock);
 425                list_for_each_entry_safe(en, nxt, &tbl->bucket[i], list) {
 426                        ip_vs_lblcr_free(en);
 427                        atomic_dec(&tbl->entries);
 428                }
 429                write_unlock(&tbl->lock);
 430        }
 431}
 432
 433
 434static inline void ip_vs_lblcr_full_check(struct ip_vs_lblcr_table *tbl)
 435{
 436        unsigned long now = jiffies;
 437        int i, j;
 438        struct ip_vs_lblcr_entry *en, *nxt;
 439
 440        for (i=0, j=tbl->rover; i<IP_VS_LBLCR_TAB_SIZE; i++) {
 441                j = (j + 1) & IP_VS_LBLCR_TAB_MASK;
 442
 443                write_lock(&tbl->lock);
 444                list_for_each_entry_safe(en, nxt, &tbl->bucket[j], list) {
 445                        if (time_after(en->lastuse+sysctl_ip_vs_lblcr_expiration,
 446                                       now))
 447                                continue;
 448
 449                        ip_vs_lblcr_free(en);
 450                        atomic_dec(&tbl->entries);
 451                }
 452                write_unlock(&tbl->lock);
 453        }
 454        tbl->rover = j;
 455}
 456
 457
 458/*
 459 *      Periodical timer handler for IPVS lblcr table
 460 *      It is used to collect stale entries when the number of entries
 461 *      exceeds the maximum size of the table.
 462 *
 463 *      Fixme: we probably need more complicated algorithm to collect
 464 *             entries that have not been used for a long time even
 465 *             if the number of entries doesn't exceed the maximum size
 466 *             of the table.
 467 *      The full expiration check is for this purpose now.
 468 */
 469static void ip_vs_lblcr_check_expire(unsigned long data)
 470{
 471        struct ip_vs_lblcr_table *tbl;
 472        unsigned long now = jiffies;
 473        int goal;
 474        int i, j;
 475        struct ip_vs_lblcr_entry *en, *nxt;
 476
 477        tbl = (struct ip_vs_lblcr_table *)data;
 478
 479        if ((tbl->counter % COUNT_FOR_FULL_EXPIRATION) == 0) {
 480                /* do full expiration check */
 481                ip_vs_lblcr_full_check(tbl);
 482                tbl->counter = 1;
 483                goto out;
 484        }
 485
 486        if (atomic_read(&tbl->entries) <= tbl->max_size) {
 487                tbl->counter++;
 488                goto out;
 489        }
 490
 491        goal = (atomic_read(&tbl->entries) - tbl->max_size)*4/3;
 492        if (goal > tbl->max_size/2)
 493                goal = tbl->max_size/2;
 494
 495        for (i=0, j=tbl->rover; i<IP_VS_LBLCR_TAB_SIZE; i++) {
 496                j = (j + 1) & IP_VS_LBLCR_TAB_MASK;
 497
 498                write_lock(&tbl->lock);
 499                list_for_each_entry_safe(en, nxt, &tbl->bucket[j], list) {
 500                        if (time_before(now, en->lastuse+ENTRY_TIMEOUT))
 501                                continue;
 502
 503                        ip_vs_lblcr_free(en);
 504                        atomic_dec(&tbl->entries);
 505                        goal--;
 506                }
 507                write_unlock(&tbl->lock);
 508                if (goal <= 0)
 509                        break;
 510        }
 511        tbl->rover = j;
 512
 513  out:
 514        mod_timer(&tbl->periodic_timer, jiffies+CHECK_EXPIRE_INTERVAL);
 515}
 516
 517static int ip_vs_lblcr_init_svc(struct ip_vs_service *svc)
 518{
 519        int i;
 520        struct ip_vs_lblcr_table *tbl;
 521
 522        /*
 523         *    Allocate the ip_vs_lblcr_table for this service
 524         */
 525        tbl = kmalloc(sizeof(struct ip_vs_lblcr_table), GFP_ATOMIC);
 526        if (tbl == NULL) {
 527                IP_VS_ERR("ip_vs_lblcr_init_svc(): no memory\n");
 528                return -ENOMEM;
 529        }
 530        svc->sched_data = tbl;
 531        IP_VS_DBG(6, "LBLCR hash table (memory=%Zdbytes) allocated for "
 532                  "current service\n",
 533                  sizeof(struct ip_vs_lblcr_table));
 534
 535        /*
 536         *    Initialize the hash buckets
 537         */
 538        for (i=0; i<IP_VS_LBLCR_TAB_SIZE; i++) {
 539                INIT_LIST_HEAD(&tbl->bucket[i]);
 540        }
 541        rwlock_init(&tbl->lock);
 542        tbl->max_size = IP_VS_LBLCR_TAB_SIZE*16;
 543        tbl->rover = 0;
 544        tbl->counter = 1;
 545
 546        /*
 547         *    Hook periodic timer for garbage collection
 548         */
 549        setup_timer(&tbl->periodic_timer, ip_vs_lblcr_check_expire,
 550                        (unsigned long)tbl);
 551        tbl->periodic_timer.expires = jiffies+CHECK_EXPIRE_INTERVAL;
 552        add_timer(&tbl->periodic_timer);
 553
 554        return 0;
 555}
 556
 557
 558static int ip_vs_lblcr_done_svc(struct ip_vs_service *svc)
 559{
 560        struct ip_vs_lblcr_table *tbl = svc->sched_data;
 561
 562        /* remove periodic timer */
 563        del_timer_sync(&tbl->periodic_timer);
 564
 565        /* got to clean up table entries here */
 566        ip_vs_lblcr_flush(tbl);
 567
 568        /* release the table itself */
 569        kfree(svc->sched_data);
 570        IP_VS_DBG(6, "LBLCR hash table (memory=%Zdbytes) released\n",
 571                  sizeof(struct ip_vs_lblcr_table));
 572
 573        return 0;
 574}
 575
 576
 577static int ip_vs_lblcr_update_svc(struct ip_vs_service *svc)
 578{
 579        return 0;
 580}
 581
 582
 583static inline struct ip_vs_dest *
 584__ip_vs_wlc_schedule(struct ip_vs_service *svc, struct iphdr *iph)
 585{
 586        struct ip_vs_dest *dest, *least;
 587        int loh, doh;
 588
 589        /*
 590         * We think the overhead of processing active connections is fifty
 591         * times higher than that of inactive connections in average. (This
 592         * fifty times might not be accurate, we will change it later.) We
 593         * use the following formula to estimate the overhead:
 594         *                dest->activeconns*50 + dest->inactconns
 595         * and the load:
 596         *                (dest overhead) / dest->weight
 597         *
 598         * Remember -- no floats in kernel mode!!!
 599         * The comparison of h1*w2 > h2*w1 is equivalent to that of
 600         *                h1/w1 > h2/w2
 601         * if every weight is larger than zero.
 602         *
 603         * The server with weight=0 is quiesced and will not receive any
 604         * new connection.
 605         */
 606        list_for_each_entry(dest, &svc->destinations, n_list) {
 607                if (dest->flags & IP_VS_DEST_F_OVERLOAD)
 608                        continue;
 609
 610                if (atomic_read(&dest->weight) > 0) {
 611                        least = dest;
 612                        loh = atomic_read(&least->activeconns) * 50
 613                                + atomic_read(&least->inactconns);
 614                        goto nextstage;
 615                }
 616        }
 617        return NULL;
 618
 619        /*
 620         *    Find the destination with the least load.
 621         */
 622  nextstage:
 623        list_for_each_entry_continue(dest, &svc->destinations, n_list) {
 624                if (dest->flags & IP_VS_DEST_F_OVERLOAD)
 625                        continue;
 626
 627                doh = atomic_read(&dest->activeconns) * 50
 628                        + atomic_read(&dest->inactconns);
 629                if (loh * atomic_read(&dest->weight) >
 630                    doh * atomic_read(&least->weight)) {
 631                        least = dest;
 632                        loh = doh;
 633                }
 634        }
 635
 636        IP_VS_DBG(6, "LBLCR: server %d.%d.%d.%d:%d "
 637                  "activeconns %d refcnt %d weight %d overhead %d\n",
 638                  NIPQUAD(least->addr), ntohs(least->port),
 639                  atomic_read(&least->activeconns),
 640                  atomic_read(&least->refcnt),
 641                  atomic_read(&least->weight), loh);
 642
 643        return least;
 644}
 645
 646
 647/*
 648 *   If this destination server is overloaded and there is a less loaded
 649 *   server, then return true.
 650 */
 651static inline int
 652is_overloaded(struct ip_vs_dest *dest, struct ip_vs_service *svc)
 653{
 654        if (atomic_read(&dest->activeconns) > atomic_read(&dest->weight)) {
 655                struct ip_vs_dest *d;
 656
 657                list_for_each_entry(d, &svc->destinations, n_list) {
 658                        if (atomic_read(&d->activeconns)*2
 659                            < atomic_read(&d->weight)) {
 660                                return 1;
 661                        }
 662                }
 663        }
 664        return 0;
 665}
 666
 667
 668/*
 669 *    Locality-Based (weighted) Least-Connection scheduling
 670 */
 671static struct ip_vs_dest *
 672ip_vs_lblcr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
 673{
 674        struct ip_vs_dest *dest;
 675        struct ip_vs_lblcr_table *tbl;
 676        struct ip_vs_lblcr_entry *en;
 677        struct iphdr *iph = ip_hdr(skb);
 678
 679        IP_VS_DBG(6, "ip_vs_lblcr_schedule(): Scheduling...\n");
 680
 681        tbl = (struct ip_vs_lblcr_table *)svc->sched_data;
 682        en = ip_vs_lblcr_get(tbl, iph->daddr);
 683        if (en == NULL) {
 684                dest = __ip_vs_wlc_schedule(svc, iph);
 685                if (dest == NULL) {
 686                        IP_VS_DBG(1, "no destination available\n");
 687                        return NULL;
 688                }
 689                en = ip_vs_lblcr_new(iph->daddr);
 690                if (en == NULL) {
 691                        return NULL;
 692                }
 693                ip_vs_dest_set_insert(&en->set, dest);
 694                ip_vs_lblcr_hash(tbl, en);
 695        } else {
 696                dest = ip_vs_dest_set_min(&en->set);
 697                if (!dest || is_overloaded(dest, svc)) {
 698                        dest = __ip_vs_wlc_schedule(svc, iph);
 699                        if (dest == NULL) {
 700                                IP_VS_DBG(1, "no destination available\n");
 701                                return NULL;
 702                        }
 703                        ip_vs_dest_set_insert(&en->set, dest);
 704                }
 705                if (atomic_read(&en->set.size) > 1 &&
 706                    jiffies-en->set.lastmod > sysctl_ip_vs_lblcr_expiration) {
 707                        struct ip_vs_dest *m;
 708                        m = ip_vs_dest_set_max(&en->set);
 709                        if (m)
 710                                ip_vs_dest_set_erase(&en->set, m);
 711                }
 712        }
 713        en->lastuse = jiffies;
 714
 715        IP_VS_DBG(6, "LBLCR: destination IP address %u.%u.%u.%u "
 716                  "--> server %u.%u.%u.%u:%d\n",
 717                  NIPQUAD(en->addr),
 718                  NIPQUAD(dest->addr),
 719                  ntohs(dest->port));
 720
 721        return dest;
 722}
 723
 724
 725/*
 726 *      IPVS LBLCR Scheduler structure
 727 */
 728static struct ip_vs_scheduler ip_vs_lblcr_scheduler =
 729{
 730        .name =                 "lblcr",
 731        .refcnt =               ATOMIC_INIT(0),
 732        .module =               THIS_MODULE,
 733        .init_service =         ip_vs_lblcr_init_svc,
 734        .done_service =         ip_vs_lblcr_done_svc,
 735        .update_service =       ip_vs_lblcr_update_svc,
 736        .schedule =             ip_vs_lblcr_schedule,
 737};
 738
 739
 740static int __init ip_vs_lblcr_init(void)
 741{
 742        int ret;
 743
 744        INIT_LIST_HEAD(&ip_vs_lblcr_scheduler.n_list);
 745        sysctl_header = register_sysctl_paths(net_vs_ctl_path, vs_vars_table);
 746        ret = register_ip_vs_scheduler(&ip_vs_lblcr_scheduler);
 747        if (ret)
 748                unregister_sysctl_table(sysctl_header);
 749        return ret;
 750}
 751
 752
 753static void __exit ip_vs_lblcr_cleanup(void)
 754{
 755        unregister_sysctl_table(sysctl_header);
 756        unregister_ip_vs_scheduler(&ip_vs_lblcr_scheduler);
 757}
 758
 759
 760module_init(ip_vs_lblcr_init);
 761module_exit(ip_vs_lblcr_cleanup);
 762MODULE_LICENSE("GPL");
 763
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.