1 /* 2 * IPVS: Round-Robin Scheduling module 3 * 4 * Authors: Wensong Zhang <wensong@linuxvirtualserver.org> 5 * Peter Kese <peter.kese@ijs.si> 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License 9 * as published by the Free Software Foundation; either version 10 * 2 of the License, or (at your option) any later version. 11 * 12 * Fixes/Changes: 13 * Wensong Zhang : changed the ip_vs_rr_schedule to return dest 14 * Julian Anastasov : fixed the NULL pointer access bug in debugging 15 * Wensong Zhang : changed some comestics things for debugging 16 * Wensong Zhang : changed for the d-linked destination list 17 * Wensong Zhang : added the ip_vs_rr_update_svc 18 * Wensong Zhang : added any dest with weight=0 is quiesced 19 * 20 */ 21 22 #define KMSG_COMPONENT "IPVS" 23 #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 24 25 #include <linux/module.h> 26 #include <linux/kernel.h> 27 28 #include <net/ip_vs.h> 29 30 31 static int ip_vs_rr_init_svc(struct ip_vs_service *svc) 32 { 33 svc->sched_data = &svc->destinations; 34 return 0; 35 } 36 37 38 static int ip_vs_rr_del_dest(struct ip_vs_service *svc, struct ip_vs_dest *dest) 39 { 40 struct list_head *p; 41 42 spin_lock_bh(&svc->sched_lock); 43 p = (struct list_head *) svc->sched_data; 44 /* dest is already unlinked, so p->prev is not valid but 45 * p->next is valid, use it to reach previous entry. 46 */ 47 if (p == &dest->n_list) 48 svc->sched_data = p->next->prev; 49 spin_unlock_bh(&svc->sched_lock); 50 return 0; 51 } 52 53 54 /* 55 * Round-Robin Scheduling 56 */ 57 static struct ip_vs_dest * 58 ip_vs_rr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb, 59 struct ip_vs_iphdr *iph) 60 { 61 struct list_head *p; 62 struct ip_vs_dest *dest, *last; 63 int pass = 0; 64 65 IP_VS_DBG(6, "%s(): Scheduling...\n", __func__); 66 67 spin_lock_bh(&svc->sched_lock); 68 p = (struct list_head *) svc->sched_data; 69 last = dest = list_entry(p, struct ip_vs_dest, n_list); 70 71 do { 72 list_for_each_entry_continue_rcu(dest, 73 &svc->destinations, 74 n_list) { 75 if (!(dest->flags & IP_VS_DEST_F_OVERLOAD) && 76 atomic_read(&dest->weight) > 0) 77 /* HIT */ 78 goto out; 79 if (dest == last) 80 goto stop; 81 } 82 pass++; 83 /* Previous dest could be unlinked, do not loop forever. 84 * If we stay at head there is no need for 2nd pass. 85 */ 86 } while (pass < 2 && p != &svc->destinations); 87 88 stop: 89 spin_unlock_bh(&svc->sched_lock); 90 ip_vs_scheduler_err(svc, "no destination available"); 91 return NULL; 92 93 out: 94 svc->sched_data = &dest->n_list; 95 spin_unlock_bh(&svc->sched_lock); 96 IP_VS_DBG_BUF(6, "RR: server %s:%u " 97 "activeconns %d refcnt %d weight %d\n", 98 IP_VS_DBG_ADDR(dest->af, &dest->addr), ntohs(dest->port), 99 atomic_read(&dest->activeconns), 100 atomic_read(&dest->refcnt), atomic_read(&dest->weight)); 101 102 return dest; 103 } 104 105 106 static struct ip_vs_scheduler ip_vs_rr_scheduler = { 107 .name = "rr", /* name */ 108 .refcnt = ATOMIC_INIT(0), 109 .module = THIS_MODULE, 110 .n_list = LIST_HEAD_INIT(ip_vs_rr_scheduler.n_list), 111 .init_service = ip_vs_rr_init_svc, 112 .add_dest = NULL, 113 .del_dest = ip_vs_rr_del_dest, 114 .schedule = ip_vs_rr_schedule, 115 }; 116 117 static int __init ip_vs_rr_init(void) 118 { 119 return register_ip_vs_scheduler(&ip_vs_rr_scheduler); 120 } 121 122 static void __exit ip_vs_rr_cleanup(void) 123 { 124 unregister_ip_vs_scheduler(&ip_vs_rr_scheduler); 125 synchronize_rcu(); 126 } 127 128 module_init(ip_vs_rr_init); 129 module_exit(ip_vs_rr_cleanup); 130 MODULE_LICENSE("GPL"); 131