1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * IPVS:        Weighted Least-Connection Scheduling module
4 *
5 * Authors:     Wensong Zhang <wensong@linuxvirtualserver.org>
6 *              Peter Kese <peter.kese@ijs.si>
7 *
8 * Changes:
9 *     Wensong Zhang            :     changed the ip_vs_wlc_schedule to return dest
10 *     Wensong Zhang            :     changed to use the inactconns in scheduling
11 *     Wensong Zhang            :     changed some comestics things for debugging
12 *     Wensong Zhang            :     changed for the d-linked destination list
13 *     Wensong Zhang            :     added the ip_vs_wlc_update_svc
14 *     Wensong Zhang            :     added any dest with weight=0 is quiesced
15 */
16
17#define KMSG_COMPONENT "IPVS"
18#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
19
20#include <linux/module.h>
21#include <linux/kernel.h>
22
23#include <net/ip_vs.h>
24
25/*
26 *	Weighted Least Connection scheduling
27 */
28static struct ip_vs_dest *
29ip_vs_wlc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
30		   struct ip_vs_iphdr *iph)
31{
32	struct ip_vs_dest *dest, *least;
33	int loh, doh;
34
35	IP_VS_DBG(6, "ip_vs_wlc_schedule(): Scheduling...\n");
36
37	/*
38	 * We calculate the load of each dest server as follows:
39	 *		  (dest overhead) / dest->weight
40	 *
41	 * Remember -- no floats in kernel mode!!!
42	 * The comparison of h1*w2 > h2*w1 is equivalent to that of
43	 *		  h1/w1 > h2/w2
44	 * if every weight is larger than zero.
45	 *
46	 * The server with weight=0 is quiesced and will not receive any
47	 * new connections.
48	 */
49
50	list_for_each_entry_rcu(dest, &svc->destinations, n_list) {
51		if (!(dest->flags & IP_VS_DEST_F_OVERLOAD) &&
52		    atomic_read(&dest->weight) > 0) {
53			least = dest;
54			loh = ip_vs_dest_conn_overhead(least);
55			goto nextstage;
56		}
57	}
58	ip_vs_scheduler_err(svc, "no destination available");
59	return NULL;
60
61	/*
62	 *    Find the destination with the least load.
63	 */
64  nextstage:
65	list_for_each_entry_continue_rcu(dest, &svc->destinations, n_list) {
66		if (dest->flags & IP_VS_DEST_F_OVERLOAD)
67			continue;
68		doh = ip_vs_dest_conn_overhead(dest);
69		if ((__s64)loh * atomic_read(&dest->weight) >
70		    (__s64)doh * atomic_read(&least->weight)) {
71			least = dest;
72			loh = doh;
73		}
74	}
75
76	IP_VS_DBG_BUF(6, "WLC: server %s:%u "
77		      "activeconns %d refcnt %d weight %d overhead %d\n",
78		      IP_VS_DBG_ADDR(least->af, &least->addr),
79		      ntohs(least->port),
80		      atomic_read(&least->activeconns),
81		      refcount_read(&least->refcnt),
82		      atomic_read(&least->weight), loh);
83
84	return least;
85}
86
87
88static struct ip_vs_scheduler ip_vs_wlc_scheduler =
89{
90	.name =			"wlc",
91	.refcnt =		ATOMIC_INIT(0),
92	.module =		THIS_MODULE,
93	.n_list =		LIST_HEAD_INIT(ip_vs_wlc_scheduler.n_list),
94	.schedule =		ip_vs_wlc_schedule,
95};
96
97
98static int __init ip_vs_wlc_init(void)
99{
100	return register_ip_vs_scheduler(&ip_vs_wlc_scheduler);
101}
102
103static void __exit ip_vs_wlc_cleanup(void)
104{
105	unregister_ip_vs_scheduler(&ip_vs_wlc_scheduler);
106	synchronize_rcu();
107}
108
109module_init(ip_vs_wlc_init);
110module_exit(ip_vs_wlc_cleanup);
111MODULE_LICENSE("GPL");
112MODULE_DESCRIPTION("ipvs weighted least connection scheduler");
113