1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * IPVS:        Least-Connection Scheduling module
4  *
5  * Authors:     Wensong Zhang <wensong@linuxvirtualserver.org>
6  *
7  * Changes:
8  *     Wensong Zhang            :     added the ip_vs_lc_update_svc
9  *     Wensong Zhang            :     added any dest with weight=0 is quiesced
10  */
11 
12 #define KMSG_COMPONENT "IPVS"
13 #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
14 
15 #include <linux/module.h>
16 #include <linux/kernel.h>
17 
18 #include <net/ip_vs.h>
19 
20 /*
21  *	Least Connection scheduling
22  */
23 static struct ip_vs_dest *
ip_vs_lc_schedule(struct ip_vs_service * svc,const struct sk_buff * skb,struct ip_vs_iphdr * iph)24 ip_vs_lc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
25 		  struct ip_vs_iphdr *iph)
26 {
27 	struct ip_vs_dest *dest, *least = NULL;
28 	unsigned int loh = 0, doh;
29 
30 	IP_VS_DBG(6, "%s(): Scheduling...\n", __func__);
31 
32 	/*
33 	 * Simply select the server with the least number of
34 	 *        (activeconns<<5) + inactconns
35 	 * Except whose weight is equal to zero.
36 	 * If the weight is equal to zero, it means that the server is
37 	 * quiesced, the existing connections to the server still get
38 	 * served, but no new connection is assigned to the server.
39 	 */
40 
41 	list_for_each_entry_rcu(dest, &svc->destinations, n_list) {
42 		if ((dest->flags & IP_VS_DEST_F_OVERLOAD) ||
43 		    atomic_read(&dest->weight) == 0)
44 			continue;
45 		doh = ip_vs_dest_conn_overhead(dest);
46 		if (!least || doh < loh) {
47 			least = dest;
48 			loh = doh;
49 		}
50 	}
51 
52 	if (!least)
53 		ip_vs_scheduler_err(svc, "no destination available");
54 	else
55 		IP_VS_DBG_BUF(6, "LC: server %s:%u activeconns %d "
56 			      "inactconns %d\n",
57 			      IP_VS_DBG_ADDR(least->af, &least->addr),
58 			      ntohs(least->port),
59 			      atomic_read(&least->activeconns),
60 			      atomic_read(&least->inactconns));
61 
62 	return least;
63 }
64 
65 
66 static struct ip_vs_scheduler ip_vs_lc_scheduler = {
67 	.name =			"lc",
68 	.refcnt =		ATOMIC_INIT(0),
69 	.module =		THIS_MODULE,
70 	.n_list =		LIST_HEAD_INIT(ip_vs_lc_scheduler.n_list),
71 	.schedule =		ip_vs_lc_schedule,
72 };
73 
74 
ip_vs_lc_init(void)75 static int __init ip_vs_lc_init(void)
76 {
77 	return register_ip_vs_scheduler(&ip_vs_lc_scheduler) ;
78 }
79 
ip_vs_lc_cleanup(void)80 static void __exit ip_vs_lc_cleanup(void)
81 {
82 	unregister_ip_vs_scheduler(&ip_vs_lc_scheduler);
83 	synchronize_rcu();
84 }
85 
86 module_init(ip_vs_lc_init);
87 module_exit(ip_vs_lc_cleanup);
88 MODULE_LICENSE("GPL");
89