Commit c0d0c0a1 authored by Julian Anastasov's avatar Julian Anastasov Committed by Pablo Neira Ayuso

ipvs: convert rr scheduler to rcu

The schedule method now needs _rcu list-traversal
primitive for svc->destinations. As the previous entry
could be unlinked, limit the list traversals to 2 when
lookup started from previous entry.
Signed-off-by: default avatarJulian Anastasov <ja@ssi.bg>
Signed-off-by: default avatarSimon Horman <horms@verge.net.au>
parent f92ea8f0
...@@ -35,9 +35,18 @@ static int ip_vs_rr_init_svc(struct ip_vs_service *svc) ...@@ -35,9 +35,18 @@ static int ip_vs_rr_init_svc(struct ip_vs_service *svc)
} }
static int ip_vs_rr_update_svc(struct ip_vs_service *svc) static int ip_vs_rr_del_dest(struct ip_vs_service *svc, struct ip_vs_dest *dest)
{ {
svc->sched_data = &svc->destinations; struct list_head *p;
write_lock_bh(&svc->sched_lock);
p = (struct list_head *) svc->sched_data;
/* dest is already unlinked, so p->prev is not valid but
* p->next is valid, use it to reach previous entry.
*/
if (p == &dest->n_list)
svc->sched_data = p->next->prev;
write_unlock_bh(&svc->sched_lock);
return 0; return 0;
} }
...@@ -48,35 +57,40 @@ static int ip_vs_rr_update_svc(struct ip_vs_service *svc) ...@@ -48,35 +57,40 @@ static int ip_vs_rr_update_svc(struct ip_vs_service *svc)
static struct ip_vs_dest * static struct ip_vs_dest *
ip_vs_rr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) ip_vs_rr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
{ {
struct list_head *p, *q; struct list_head *p;
struct ip_vs_dest *dest; struct ip_vs_dest *dest, *last;
int pass = 0;
IP_VS_DBG(6, "%s(): Scheduling...\n", __func__); IP_VS_DBG(6, "%s(): Scheduling...\n", __func__);
write_lock(&svc->sched_lock); write_lock(&svc->sched_lock);
p = (struct list_head *)svc->sched_data; p = (struct list_head *) svc->sched_data;
p = p->next; last = dest = list_entry(p, struct ip_vs_dest, n_list);
q = p;
do {
/* skip list head */
if (q == &svc->destinations) {
q = q->next;
continue;
}
dest = list_entry(q, struct ip_vs_dest, n_list); do {
list_for_each_entry_continue_rcu(dest,
&svc->destinations,
n_list) {
if (!(dest->flags & IP_VS_DEST_F_OVERLOAD) && if (!(dest->flags & IP_VS_DEST_F_OVERLOAD) &&
atomic_read(&dest->weight) > 0) atomic_read(&dest->weight) > 0)
/* HIT */ /* HIT */
goto out; goto out;
q = q->next; if (dest == last)
} while (q != p); goto stop;
}
pass++;
/* Previous dest could be unlinked, do not loop forever.
* If we stay at head there is no need for 2nd pass.
*/
} while (pass < 2 && p != &svc->destinations);
stop:
write_unlock(&svc->sched_lock); write_unlock(&svc->sched_lock);
ip_vs_scheduler_err(svc, "no destination available"); ip_vs_scheduler_err(svc, "no destination available");
return NULL; return NULL;
out: out:
svc->sched_data = q; svc->sched_data = &dest->n_list;
write_unlock(&svc->sched_lock); write_unlock(&svc->sched_lock);
IP_VS_DBG_BUF(6, "RR: server %s:%u " IP_VS_DBG_BUF(6, "RR: server %s:%u "
"activeconns %d refcnt %d weight %d\n", "activeconns %d refcnt %d weight %d\n",
...@@ -94,7 +108,8 @@ static struct ip_vs_scheduler ip_vs_rr_scheduler = { ...@@ -94,7 +108,8 @@ static struct ip_vs_scheduler ip_vs_rr_scheduler = {
.module = THIS_MODULE, .module = THIS_MODULE,
.n_list = LIST_HEAD_INIT(ip_vs_rr_scheduler.n_list), .n_list = LIST_HEAD_INIT(ip_vs_rr_scheduler.n_list),
.init_service = ip_vs_rr_init_svc, .init_service = ip_vs_rr_init_svc,
.update_service = ip_vs_rr_update_svc, .add_dest = NULL,
.del_dest = ip_vs_rr_del_dest,
.schedule = ip_vs_rr_schedule, .schedule = ip_vs_rr_schedule,
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment