Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+22 −14
Original line number Diff line number Diff line
@@ -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,7 +1294,14 @@ 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,
			     enum update_reason reason)
{
	agg->initial_budget = agg->budget = agg->budgetmax; /* recharge budg. */

	qfq_update_agg_ts(q, agg, reason);
	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);
}