1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * IPVS: Round-Robin Scheduling module 4 * 5 * Authors: Wensong Zhang <wensong@linuxvirtualserver.org> 6 * Peter Kese <peter.kese@ijs.si> 7 * 8 * Fixes/Changes: 9 * Wensong Zhang : changed the ip_vs_rr_schedule to return dest 10 * Julian Anastasov : fixed the NULL pointer access bug in debugging 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_rr_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 static int ip_vs_rr_init_svc(struct ip_vs_service *svc) 27 { 28 svc->sched_data = &svc->destinations; 29 return 0; 30 } 31 32 33 static int ip_vs_rr_del_dest(struct ip_vs_service *svc, struct ip_vs_dest *dest) 34 { 35 struct list_head *p; 36 37 spin_lock_bh(&svc->sched_lock); 38 p = (struct list_head *) svc->sched_data; 39 /* dest is already unlinked, so p->prev is not valid but 40 * p->next is valid, use it to reach previous entry. 41 */ 42 if (p == &dest->n_list) 43 svc->sched_data = p->next->prev; 44 spin_unlock_bh(&svc->sched_lock); 45 return 0; 46 } 47 48 49 /* 50 * Round-Robin Scheduling 51 */ 52 static struct ip_vs_dest * 53 ip_vs_rr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb, 54 struct ip_vs_iphdr *iph) 55 { 56 struct list_head *p; 57 struct ip_vs_dest *dest, *last; 58 int pass = 0; 59 60 IP_VS_DBG(6, "%s(): Scheduling...\n", __func__); 61 62 spin_lock_bh(&svc->sched_lock); 63 p = (struct list_head *) svc->sched_data; 64 last = dest = list_entry(p, struct ip_vs_dest, n_list); 65 66 do { 67 list_for_each_entry_continue_rcu(dest, 68 &svc->destinations, 69 n_list) { 70 if (!(dest->flags & IP_VS_DEST_F_OVERLOAD) && 71 atomic_read(&dest->weight) > 0) 72 /* HIT */ 73 goto out; 74 if (dest == last) 75 goto stop; 76 } 77 pass++; 78 /* Previous dest could be unlinked, do not loop forever. 79 * If we stay at head there is no need for 2nd pass. 80 */ 81 } while (pass < 2 && p != &svc->destinations); 82 83 stop: 84 spin_unlock_bh(&svc->sched_lock); 85 ip_vs_scheduler_err(svc, "no destination available"); 86 return NULL; 87 88 out: 89 svc->sched_data = &dest->n_list; 90 spin_unlock_bh(&svc->sched_lock); 91 IP_VS_DBG_BUF(6, "RR: server %s:%u " 92 "activeconns %d refcnt %d weight %d\n", 93 IP_VS_DBG_ADDR(dest->af, &dest->addr), ntohs(dest->port), 94 atomic_read(&dest->activeconns), 95 refcount_read(&dest->refcnt), atomic_read(&dest->weight)); 96 97 return dest; 98 } 99 100 101 static struct ip_vs_scheduler ip_vs_rr_scheduler = { 102 .name = "rr", /* name */ 103 .refcnt = ATOMIC_INIT(0), 104 .module = THIS_MODULE, 105 .n_list = LIST_HEAD_INIT(ip_vs_rr_scheduler.n_list), 106 .init_service = ip_vs_rr_init_svc, 107 .add_dest = NULL, 108 .del_dest = ip_vs_rr_del_dest, 109 .schedule = ip_vs_rr_schedule, 110 }; 111 112 static int __init ip_vs_rr_init(void) 113 { 114 return register_ip_vs_scheduler(&ip_vs_rr_scheduler); 115 } 116 117 static void __exit ip_vs_rr_cleanup(void) 118 { 119 unregister_ip_vs_scheduler(&ip_vs_rr_scheduler); 120 synchronize_rcu(); 121 } 122 123 module_init(ip_vs_rr_init); 124 module_exit(ip_vs_rr_cleanup); 125 MODULE_LICENSE("GPL"); 126