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 */ 28 static struct ip_vs_dest * 29 ip_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 88 static 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 98 static int __init ip_vs_wlc_init(void) 99 { 100 return register_ip_vs_scheduler(&ip_vs_wlc_scheduler); 101 } 102 103 static void __exit ip_vs_wlc_cleanup(void) 104 { 105 unregister_ip_vs_scheduler(&ip_vs_wlc_scheduler); 106 synchronize_rcu(); 107 } 108 109 module_init(ip_vs_wlc_init); 110 module_exit(ip_vs_wlc_cleanup); 111 MODULE_LICENSE("GPL"); 112