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 * |
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(v: &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, msg: "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 | |
75 | static int __init ip_vs_lc_init(void) |
76 | { |
77 | return register_ip_vs_scheduler(scheduler: &ip_vs_lc_scheduler) ; |
78 | } |
79 | |
80 | static void __exit ip_vs_lc_cleanup(void) |
81 | { |
82 | unregister_ip_vs_scheduler(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 | MODULE_DESCRIPTION("ipvs least connection scheduler" ); |
90 | |