ipvs: fix synchronization on connection close
[linux-2.6.git] / net / netfilter / ipvs / ip_vs_wlc.c
1 /*
2  * IPVS:        Weighted Least-Connection Scheduling module
3  *
4  * Authors:     Wensong Zhang <wensong@linuxvirtualserver.org>
5  *              Peter Kese <peter.kese@ijs.si>
6  *
7  *              This program is free software; you can redistribute it and/or
8  *              modify it under the terms of the GNU General Public License
9  *              as published by the Free Software Foundation; either version
10  *              2 of the License, or (at your option) any later version.
11  *
12  * Changes:
13  *     Wensong Zhang            :     changed the ip_vs_wlc_schedule to return dest
14  *     Wensong Zhang            :     changed to use the inactconns in scheduling
15  *     Wensong Zhang            :     changed some comestics things for debugging
16  *     Wensong Zhang            :     changed for the d-linked destination list
17  *     Wensong Zhang            :     added the ip_vs_wlc_update_svc
18  *     Wensong Zhang            :     added any dest with weight=0 is quiesced
19  *
20  */
21
22 #define KMSG_COMPONENT "IPVS"
23 #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
24
25 #include <linux/module.h>
26 #include <linux/kernel.h>
27
28 #include <net/ip_vs.h>
29
30
31 static inline unsigned int
32 ip_vs_wlc_dest_overhead(struct ip_vs_dest *dest)
33 {
34         /*
35          * We think the overhead of processing active connections is 256
36          * times higher than that of inactive connections in average. (This
37          * 256 times might not be accurate, we will change it later) We
38          * use the following formula to estimate the overhead now:
39          *                dest->activeconns*256 + dest->inactconns
40          */
41         return (atomic_read(&dest->activeconns) << 8) +
42                 atomic_read(&dest->inactconns);
43 }
44
45
46 /*
47  *      Weighted Least Connection scheduling
48  */
49 static struct ip_vs_dest *
50 ip_vs_wlc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
51 {
52         struct ip_vs_dest *dest, *least;
53         unsigned int loh, doh;
54
55         IP_VS_DBG(6, "ip_vs_wlc_schedule(): Scheduling...\n");
56
57         /*
58          * We calculate the load of each dest server as follows:
59          *                (dest overhead) / dest->weight
60          *
61          * Remember -- no floats in kernel mode!!!
62          * The comparison of h1*w2 > h2*w1 is equivalent to that of
63          *                h1/w1 > h2/w2
64          * if every weight is larger than zero.
65          *
66          * The server with weight=0 is quiesced and will not receive any
67          * new connections.
68          */
69
70         list_for_each_entry(dest, &svc->destinations, n_list) {
71                 if (!(dest->flags & IP_VS_DEST_F_OVERLOAD) &&
72                     atomic_read(&dest->weight) > 0) {
73                         least = dest;
74                         loh = ip_vs_wlc_dest_overhead(least);
75                         goto nextstage;
76                 }
77         }
78         IP_VS_ERR_RL("WLC: no destination available\n");
79         return NULL;
80
81         /*
82          *    Find the destination with the least load.
83          */
84   nextstage:
85         list_for_each_entry_continue(dest, &svc->destinations, n_list) {
86                 if (dest->flags & IP_VS_DEST_F_OVERLOAD)
87                         continue;
88                 doh = ip_vs_wlc_dest_overhead(dest);
89                 if (loh * atomic_read(&dest->weight) >
90                     doh * atomic_read(&least->weight)) {
91                         least = dest;
92                         loh = doh;
93                 }
94         }
95
96         IP_VS_DBG_BUF(6, "WLC: server %s:%u "
97                       "activeconns %d refcnt %d weight %d overhead %d\n",
98                       IP_VS_DBG_ADDR(svc->af, &least->addr), ntohs(least->port),
99                       atomic_read(&least->activeconns),
100                       atomic_read(&least->refcnt),
101                       atomic_read(&least->weight), loh);
102
103         return least;
104 }
105
106
107 static struct ip_vs_scheduler ip_vs_wlc_scheduler =
108 {
109         .name =                 "wlc",
110         .refcnt =               ATOMIC_INIT(0),
111         .module =               THIS_MODULE,
112         .n_list =               LIST_HEAD_INIT(ip_vs_wlc_scheduler.n_list),
113         .schedule =             ip_vs_wlc_schedule,
114 };
115
116
117 static int __init ip_vs_wlc_init(void)
118 {
119         return register_ip_vs_scheduler(&ip_vs_wlc_scheduler);
120 }
121
122 static void __exit ip_vs_wlc_cleanup(void)
123 {
124         unregister_ip_vs_scheduler(&ip_vs_wlc_scheduler);
125 }
126
127 module_init(ip_vs_wlc_init);
128 module_exit(ip_vs_wlc_cleanup);
129 MODULE_LICENSE("GPL");