|
@@ -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;
|
|
|
}
|
|
|
|
|
@@ -48,35 +57,40 @@ static int ip_vs_rr_update_svc(struct ip_vs_service *svc)
|
|
|
static struct ip_vs_dest *
|
|
|
ip_vs_rr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
|
|
|
{
|
|
|
- struct list_head *p, *q;
|
|
|
- struct ip_vs_dest *dest;
|
|
|
+ struct list_head *p;
|
|
|
+ struct ip_vs_dest *dest, *last;
|
|
|
+ int pass = 0;
|
|
|
|
|
|
IP_VS_DBG(6, "%s(): Scheduling...\n", __func__);
|
|
|
|
|
|
write_lock(&svc->sched_lock);
|
|
|
- p = (struct list_head *)svc->sched_data;
|
|
|
- p = p->next;
|
|
|
- q = p;
|
|
|
+ p = (struct list_head *) svc->sched_data;
|
|
|
+ last = dest = list_entry(p, struct ip_vs_dest, n_list);
|
|
|
+
|
|
|
do {
|
|
|
- /* skip list head */
|
|
|
- if (q == &svc->destinations) {
|
|
|
- q = q->next;
|
|
|
- continue;
|
|
|
+ list_for_each_entry_continue_rcu(dest,
|
|
|
+ &svc->destinations,
|
|
|
+ n_list) {
|
|
|
+ if (!(dest->flags & IP_VS_DEST_F_OVERLOAD) &&
|
|
|
+ atomic_read(&dest->weight) > 0)
|
|
|
+ /* HIT */
|
|
|
+ goto out;
|
|
|
+ if (dest == last)
|
|
|
+ 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);
|
|
|
|
|
|
- dest = list_entry(q, struct ip_vs_dest, n_list);
|
|
|
- if (!(dest->flags & IP_VS_DEST_F_OVERLOAD) &&
|
|
|
- atomic_read(&dest->weight) > 0)
|
|
|
- /* HIT */
|
|
|
- goto out;
|
|
|
- q = q->next;
|
|
|
- } while (q != p);
|
|
|
+stop:
|
|
|
write_unlock(&svc->sched_lock);
|
|
|
ip_vs_scheduler_err(svc, "no destination available");
|
|
|
return NULL;
|
|
|
|
|
|
out:
|
|
|
- svc->sched_data = q;
|
|
|
+ svc->sched_data = &dest->n_list;
|
|
|
write_unlock(&svc->sched_lock);
|
|
|
IP_VS_DBG_BUF(6, "RR: server %s:%u "
|
|
|
"activeconns %d refcnt %d weight %d\n",
|
|
@@ -94,7 +108,8 @@ static struct ip_vs_scheduler ip_vs_rr_scheduler = {
|
|
|
.module = THIS_MODULE,
|
|
|
.n_list = LIST_HEAD_INIT(ip_vs_rr_scheduler.n_list),
|
|
|
.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,
|
|
|
};
|
|
|
|