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: Paolo Valente <paolo.valente@unimore.it> Reviewed-by: Fabio Checconi <fchecconi@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
624b85fb96
commit
2f3b89a1fe
|
@ -1003,6 +1003,12 @@ static inline void charge_actual_service(struct qfq_aggregate *agg)
|
|||
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)
|
||||
{
|
||||
struct qfq_sched *q = qdisc_priv(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->budgetmax;
|
||||
|
||||
if (!list_empty(&in_serv_agg->active))
|
||||
if (!list_empty(&in_serv_agg->active)) {
|
||||
/*
|
||||
* Still active: reschedule for
|
||||
* service. Possible optimization: if no other
|
||||
|
@ -1041,8 +1047,9 @@ static struct sk_buff *qfq_dequeue(struct Qdisc *sch)
|
|||
* handle it, we would need to maintain an
|
||||
* extra num_active_aggs field.
|
||||
*/
|
||||
qfq_activate_agg(q, in_serv_agg, requeue);
|
||||
else if (sch->q.qlen == 0) { /* no aggregate to serve */
|
||||
qfq_update_agg_ts(q, in_serv_agg, requeue);
|
||||
qfq_schedule_agg(q, in_serv_agg);
|
||||
} else if (sch->q.qlen == 0) { /* no aggregate to serve */
|
||||
q->in_serv_agg = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
@ -1226,17 +1233,11 @@ static int qfq_enqueue(struct sk_buff *skb, struct Qdisc *sch)
|
|||
cl->deficit = agg->lmax;
|
||||
list_add_tail(&cl->alist, &agg->active);
|
||||
|
||||
if (list_first_entry(&agg->active, struct qfq_class, alist) != cl)
|
||||
return err; /* aggregate was not empty, nothing else to do */
|
||||
if (list_first_entry(&agg->active, struct qfq_class, alist) != cl ||
|
||||
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);
|
||||
if (q->in_serv_agg == NULL)
|
||||
q->in_serv_agg = agg;
|
||||
else if (agg != q->in_serv_agg)
|
||||
qfq_schedule_agg(q, agg);
|
||||
qfq_activate_agg(q, agg, enqueue);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
@ -1293,8 +1294,15 @@ skip_update:
|
|||
static void qfq_activate_agg(struct qfq_sched *q, struct qfq_aggregate *agg,
|
||||
enum update_reason reason)
|
||||
{
|
||||
agg->initial_budget = agg->budget = agg->budgetmax; /* recharge budg. */
|
||||
|
||||
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,
|
||||
|
|
Loading…
Reference in a new issue