aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorPaolo Valente <paolo.valente@unimore.it>2013-03-05 03:04:57 -0500
committerDavid S. Miller <davem@davemloft.net>2013-03-06 02:47:05 -0500
commit9b99b7e90bf5cd9a88823c49574ba80d92a98262 (patch)
tree8ec812a0bd4a5848722470d5ec21b784c127ee75 /net
parentf74861ca87264e594e0134e8ac14db06be77fe6f (diff)
pkt_sched: sch_qfq: properly cap timestamps in charge_actual_service
QFQ+ schedules the active aggregates in a group using a bucket list (one list per group). The bucket in which each aggregate is inserted depends on the aggregate's timestamps, and the number of buckets in a group is enough to accomodate the possible (range of) values of the timestamps of all the aggregates in the group. For this property to hold, timestamps must however be computed correctly. One necessary condition for computing timestamps correctly is that the number of bits dequeued for each aggregate, while the aggregate is in service, does not exceed the maximum budget budgetmax assigned to the aggregate. For each aggregate, budgetmax is proportional to the number of classes in the aggregate. If the number of classes of the aggregate is decreased through qfq_change_class(), then budgetmax is decreased automatically as well. Problems may occur if the aggregate is in service when budgetmax is decreased, because the current remaining budget of the aggregate and/or the service already received by the aggregate may happen to be larger than the new value of budgetmax. In this case, when the aggregate is eventually deselected and its timestamps are updated, the aggregate may happen to have received an amount of service larger than budgetmax. This may cause the aggregate to be assigned a higher virtual finish time than the maximum acceptable value for the last bucket in the bucket list of the group. This fix introduces a cap that addresses this issue. 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>
Diffstat (limited to 'net')
-rw-r--r--net/sched/sch_qfq.c13
1 files changed, 11 insertions, 2 deletions
diff --git a/net/sched/sch_qfq.c b/net/sched/sch_qfq.c
index e9a77f621c3d..489edd929932 100644
--- a/net/sched/sch_qfq.c
+++ b/net/sched/sch_qfq.c
@@ -298,6 +298,10 @@ static void qfq_update_agg(struct qfq_sched *q, struct qfq_aggregate *agg,
298 new_num_classes == q->max_agg_classes - 1) /* agg no more full */ 298 new_num_classes == q->max_agg_classes - 1) /* agg no more full */
299 hlist_add_head(&agg->nonfull_next, &q->nonfull_aggs); 299 hlist_add_head(&agg->nonfull_next, &q->nonfull_aggs);
300 300
301 /* The next assignment may let
302 * agg->initial_budget > agg->budgetmax
303 * hold, we will take it into account in charge_actual_service().
304 */
301 agg->budgetmax = new_num_classes * agg->lmax; 305 agg->budgetmax = new_num_classes * agg->lmax;
302 new_agg_weight = agg->class_weight * new_num_classes; 306 new_agg_weight = agg->class_weight * new_num_classes;
303 agg->inv_w = ONE_FP/new_agg_weight; 307 agg->inv_w = ONE_FP/new_agg_weight;
@@ -988,8 +992,13 @@ static inline struct sk_buff *qfq_peek_skb(struct qfq_aggregate *agg,
988/* Update F according to the actual service received by the aggregate. */ 992/* Update F according to the actual service received by the aggregate. */
989static inline void charge_actual_service(struct qfq_aggregate *agg) 993static inline void charge_actual_service(struct qfq_aggregate *agg)
990{ 994{
991 /* compute the service received by the aggregate */ 995 /* Compute the service received by the aggregate, taking into
992 u32 service_received = agg->initial_budget - agg->budget; 996 * account that, after decreasing the number of classes in
997 * agg, it may happen that
998 * agg->initial_budget - agg->budget > agg->bugdetmax
999 */
1000 u32 service_received = min(agg->budgetmax,
1001 agg->initial_budget - agg->budget);
993 1002
994 agg->F = agg->S + (u64)service_received * agg->inv_w; 1003 agg->F = agg->S + (u64)service_received * agg->inv_w;
995} 1004}