Commit 2f3b89a1 authored by Paolo Valente's avatar Paolo Valente Committed by David S. Miller

pkt_sched: sch_qfq: serve activated aggregates immediately if the scheduler is empty

If no aggregate is in service, then the function qfq_dequeue() does
not dequeue any packet. For this reason, to guarantee QFQ+ to be work
conserving, a just-activated aggregate must be set as in service
immediately if it happens to be the only active aggregate.
This is done by the function qfq_enqueue().

Unfortunately, the function qfq_add_to_agg(), used to add a class to
an aggregate, does not perform this important additional operation.
In particular, if: 1) qfq_add_to_agg() is invoked to complete the move
of a class from a source aggregate, becoming, for this move, inactive,
to a destination aggregate, becoming instead active, and 2) the
destination aggregate becomes the only active aggregate, then this
aggregate is not however set as in service. QFQ+ remains then in a
non-work-conserving state until a new invocation of qfq_enqueue()
recovers the situation.

This fix solves the problem by moving the logic for setting an
aggregate as in service directly into the function qfq_activate_agg().
Hence, from whatever point qfq_activate_aggregate() is invoked, QFQ+
remains work conserving.  Since the more-complex logic of this new
version of activate_aggregate() is not necessary, in qfq_dequeue(), to
reschedule an aggregate that finishes its budget, then the aggregate
is now rescheduled by invoking directly the functions needed.
Signed-off-by: default avatarPaolo Valente <paolo.valente@unimore.it>
Reviewed-by: default avatarFabio Checconi <fchecconi@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 624b85fb
...@@ -1003,6 +1003,12 @@ static inline void charge_actual_service(struct qfq_aggregate *agg) ...@@ -1003,6 +1003,12 @@ static inline void charge_actual_service(struct qfq_aggregate *agg)
agg->F = agg->S + (u64)service_received * agg->inv_w; agg->F = agg->S + (u64)service_received * agg->inv_w;
} }
static inline void qfq_update_agg_ts(struct qfq_sched *q,
struct qfq_aggregate *agg,
enum update_reason reason);
static void qfq_schedule_agg(struct qfq_sched *q, struct qfq_aggregate *agg);
static struct sk_buff *qfq_dequeue(struct Qdisc *sch) static struct sk_buff *qfq_dequeue(struct Qdisc *sch)
{ {
struct qfq_sched *q = qdisc_priv(sch); struct qfq_sched *q = qdisc_priv(sch);
...@@ -1030,7 +1036,7 @@ static struct sk_buff *qfq_dequeue(struct Qdisc *sch) ...@@ -1030,7 +1036,7 @@ static struct sk_buff *qfq_dequeue(struct Qdisc *sch)
in_serv_agg->initial_budget = in_serv_agg->budget = in_serv_agg->initial_budget = in_serv_agg->budget =
in_serv_agg->budgetmax; in_serv_agg->budgetmax;
if (!list_empty(&in_serv_agg->active)) if (!list_empty(&in_serv_agg->active)) {
/* /*
* Still active: reschedule for * Still active: reschedule for
* service. Possible optimization: if no other * service. Possible optimization: if no other
...@@ -1041,8 +1047,9 @@ static struct sk_buff *qfq_dequeue(struct Qdisc *sch) ...@@ -1041,8 +1047,9 @@ static struct sk_buff *qfq_dequeue(struct Qdisc *sch)
* handle it, we would need to maintain an * handle it, we would need to maintain an
* extra num_active_aggs field. * extra num_active_aggs field.
*/ */
qfq_activate_agg(q, in_serv_agg, requeue); qfq_update_agg_ts(q, in_serv_agg, requeue);
else if (sch->q.qlen == 0) { /* no aggregate to serve */ qfq_schedule_agg(q, in_serv_agg);
} else if (sch->q.qlen == 0) { /* no aggregate to serve */
q->in_serv_agg = NULL; q->in_serv_agg = NULL;
return NULL; return NULL;
} }
...@@ -1226,17 +1233,11 @@ static int qfq_enqueue(struct sk_buff *skb, struct Qdisc *sch) ...@@ -1226,17 +1233,11 @@ static int qfq_enqueue(struct sk_buff *skb, struct Qdisc *sch)
cl->deficit = agg->lmax; cl->deficit = agg->lmax;
list_add_tail(&cl->alist, &agg->active); list_add_tail(&cl->alist, &agg->active);
if (list_first_entry(&agg->active, struct qfq_class, alist) != cl) if (list_first_entry(&agg->active, struct qfq_class, alist) != cl ||
return err; /* aggregate was not empty, nothing else to do */ q->in_serv_agg == agg)
return err; /* non-empty or in service, nothing else to do */
/* recharge budget */
agg->initial_budget = agg->budget = agg->budgetmax;
qfq_update_agg_ts(q, agg, enqueue); qfq_activate_agg(q, agg, enqueue);
if (q->in_serv_agg == NULL)
q->in_serv_agg = agg;
else if (agg != q->in_serv_agg)
qfq_schedule_agg(q, agg);
return err; return err;
} }
...@@ -1293,8 +1294,15 @@ static void qfq_schedule_agg(struct qfq_sched *q, struct qfq_aggregate *agg) ...@@ -1293,8 +1294,15 @@ static void qfq_schedule_agg(struct qfq_sched *q, struct qfq_aggregate *agg)
static void qfq_activate_agg(struct qfq_sched *q, struct qfq_aggregate *agg, static void qfq_activate_agg(struct qfq_sched *q, struct qfq_aggregate *agg,
enum update_reason reason) enum update_reason reason)
{ {
agg->initial_budget = agg->budget = agg->budgetmax; /* recharge budg. */
qfq_update_agg_ts(q, agg, reason); qfq_update_agg_ts(q, agg, reason);
qfq_schedule_agg(q, agg); if (q->in_serv_agg == NULL) { /* no aggr. in service or scheduled */
q->in_serv_agg = agg; /* start serving this aggregate */
/* update V: to be in service, agg must be eligible */
q->oldV = q->V = agg->S;
} else if (agg != q->in_serv_agg)
qfq_schedule_agg(q, agg);
} }
static void qfq_slot_remove(struct qfq_sched *q, struct qfq_group *grp, static void qfq_slot_remove(struct qfq_sched *q, struct qfq_group *grp,
......
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