diff options
Diffstat (limited to 'net/sched/sch_qfq.c')
-rw-r--r-- | net/sched/sch_qfq.c | 66 |
1 files changed, 45 insertions, 21 deletions
diff --git a/net/sched/sch_qfq.c b/net/sched/sch_qfq.c index e9a77f621c3d..d51852bba01c 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; |
@@ -817,7 +821,7 @@ static void qfq_make_eligible(struct qfq_sched *q) | |||
817 | unsigned long old_vslot = q->oldV >> q->min_slot_shift; | 821 | unsigned long old_vslot = q->oldV >> q->min_slot_shift; |
818 | 822 | ||
819 | if (vslot != old_vslot) { | 823 | if (vslot != old_vslot) { |
820 | unsigned long mask = (1UL << fls(vslot ^ old_vslot)) - 1; | 824 | unsigned long mask = (1ULL << fls(vslot ^ old_vslot)) - 1; |
821 | qfq_move_groups(q, mask, IR, ER); | 825 | qfq_move_groups(q, mask, IR, ER); |
822 | qfq_move_groups(q, mask, IB, EB); | 826 | qfq_move_groups(q, mask, IB, EB); |
823 | } | 827 | } |
@@ -988,12 +992,23 @@ 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. */ |
989 | static inline void charge_actual_service(struct qfq_aggregate *agg) | 993 | static 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 | } |
996 | 1005 | ||
1006 | static inline void qfq_update_agg_ts(struct qfq_sched *q, | ||
1007 | struct qfq_aggregate *agg, | ||
1008 | enum update_reason reason); | ||
1009 | |||
1010 | static void qfq_schedule_agg(struct qfq_sched *q, struct qfq_aggregate *agg); | ||
1011 | |||
997 | static struct sk_buff *qfq_dequeue(struct Qdisc *sch) | 1012 | static struct sk_buff *qfq_dequeue(struct Qdisc *sch) |
998 | { | 1013 | { |
999 | struct qfq_sched *q = qdisc_priv(sch); | 1014 | struct qfq_sched *q = qdisc_priv(sch); |
@@ -1021,7 +1036,7 @@ static struct sk_buff *qfq_dequeue(struct Qdisc *sch) | |||
1021 | in_serv_agg->initial_budget = in_serv_agg->budget = | 1036 | in_serv_agg->initial_budget = in_serv_agg->budget = |
1022 | in_serv_agg->budgetmax; | 1037 | in_serv_agg->budgetmax; |
1023 | 1038 | ||
1024 | if (!list_empty(&in_serv_agg->active)) | 1039 | if (!list_empty(&in_serv_agg->active)) { |
1025 | /* | 1040 | /* |
1026 | * Still active: reschedule for | 1041 | * Still active: reschedule for |
1027 | * service. Possible optimization: if no other | 1042 | * service. Possible optimization: if no other |
@@ -1032,8 +1047,9 @@ static struct sk_buff *qfq_dequeue(struct Qdisc *sch) | |||
1032 | * handle it, we would need to maintain an | 1047 | * handle it, we would need to maintain an |
1033 | * extra num_active_aggs field. | 1048 | * extra num_active_aggs field. |
1034 | */ | 1049 | */ |
1035 | qfq_activate_agg(q, in_serv_agg, requeue); | 1050 | qfq_update_agg_ts(q, in_serv_agg, requeue); |
1036 | else if (sch->q.qlen == 0) { /* no aggregate to serve */ | 1051 | qfq_schedule_agg(q, in_serv_agg); |
1052 | } else if (sch->q.qlen == 0) { /* no aggregate to serve */ | ||
1037 | q->in_serv_agg = NULL; | 1053 | q->in_serv_agg = NULL; |
1038 | return NULL; | 1054 | return NULL; |
1039 | } | 1055 | } |
@@ -1052,7 +1068,15 @@ static struct sk_buff *qfq_dequeue(struct Qdisc *sch) | |||
1052 | qdisc_bstats_update(sch, skb); | 1068 | qdisc_bstats_update(sch, skb); |
1053 | 1069 | ||
1054 | agg_dequeue(in_serv_agg, cl, len); | 1070 | agg_dequeue(in_serv_agg, cl, len); |
1055 | in_serv_agg->budget -= len; | 1071 | /* If lmax is lowered, through qfq_change_class, for a class |
1072 | * owning pending packets with larger size than the new value | ||
1073 | * of lmax, then the following condition may hold. | ||
1074 | */ | ||
1075 | if (unlikely(in_serv_agg->budget < len)) | ||
1076 | in_serv_agg->budget = 0; | ||
1077 | else | ||
1078 | in_serv_agg->budget -= len; | ||
1079 | |||
1056 | q->V += (u64)len * IWSUM; | 1080 | q->V += (u64)len * IWSUM; |
1057 | pr_debug("qfq dequeue: len %u F %lld now %lld\n", | 1081 | pr_debug("qfq dequeue: len %u F %lld now %lld\n", |
1058 | len, (unsigned long long) in_serv_agg->F, | 1082 | len, (unsigned long long) in_serv_agg->F, |
@@ -1217,17 +1241,11 @@ static int qfq_enqueue(struct sk_buff *skb, struct Qdisc *sch) | |||
1217 | cl->deficit = agg->lmax; | 1241 | cl->deficit = agg->lmax; |
1218 | list_add_tail(&cl->alist, &agg->active); | 1242 | list_add_tail(&cl->alist, &agg->active); |
1219 | 1243 | ||
1220 | if (list_first_entry(&agg->active, struct qfq_class, alist) != cl) | 1244 | if (list_first_entry(&agg->active, struct qfq_class, alist) != cl || |
1221 | return err; /* aggregate was not empty, nothing else to do */ | 1245 | q->in_serv_agg == agg) |
1246 | return err; /* non-empty or in service, nothing else to do */ | ||
1222 | 1247 | ||
1223 | /* recharge budget */ | 1248 | qfq_activate_agg(q, agg, enqueue); |
1224 | agg->initial_budget = agg->budget = agg->budgetmax; | ||
1225 | |||
1226 | qfq_update_agg_ts(q, agg, enqueue); | ||
1227 | if (q->in_serv_agg == NULL) | ||
1228 | q->in_serv_agg = agg; | ||
1229 | else if (agg != q->in_serv_agg) | ||
1230 | qfq_schedule_agg(q, agg); | ||
1231 | 1249 | ||
1232 | return err; | 1250 | return err; |
1233 | } | 1251 | } |
@@ -1261,7 +1279,8 @@ static void qfq_schedule_agg(struct qfq_sched *q, struct qfq_aggregate *agg) | |||
1261 | /* group was surely ineligible, remove */ | 1279 | /* group was surely ineligible, remove */ |
1262 | __clear_bit(grp->index, &q->bitmaps[IR]); | 1280 | __clear_bit(grp->index, &q->bitmaps[IR]); |
1263 | __clear_bit(grp->index, &q->bitmaps[IB]); | 1281 | __clear_bit(grp->index, &q->bitmaps[IB]); |
1264 | } else if (!q->bitmaps[ER] && qfq_gt(roundedS, q->V)) | 1282 | } else if (!q->bitmaps[ER] && qfq_gt(roundedS, q->V) && |
1283 | q->in_serv_agg == NULL) | ||
1265 | q->V = roundedS; | 1284 | q->V = roundedS; |
1266 | 1285 | ||
1267 | grp->S = roundedS; | 1286 | grp->S = roundedS; |
@@ -1284,8 +1303,15 @@ skip_update: | |||
1284 | static void qfq_activate_agg(struct qfq_sched *q, struct qfq_aggregate *agg, | 1303 | static void qfq_activate_agg(struct qfq_sched *q, struct qfq_aggregate *agg, |
1285 | enum update_reason reason) | 1304 | enum update_reason reason) |
1286 | { | 1305 | { |
1306 | agg->initial_budget = agg->budget = agg->budgetmax; /* recharge budg. */ | ||
1307 | |||
1287 | qfq_update_agg_ts(q, agg, reason); | 1308 | qfq_update_agg_ts(q, agg, reason); |
1288 | qfq_schedule_agg(q, agg); | 1309 | if (q->in_serv_agg == NULL) { /* no aggr. in service or scheduled */ |
1310 | q->in_serv_agg = agg; /* start serving this aggregate */ | ||
1311 | /* update V: to be in service, agg must be eligible */ | ||
1312 | q->oldV = q->V = agg->S; | ||
1313 | } else if (agg != q->in_serv_agg) | ||
1314 | qfq_schedule_agg(q, agg); | ||
1289 | } | 1315 | } |
1290 | 1316 | ||
1291 | static void qfq_slot_remove(struct qfq_sched *q, struct qfq_group *grp, | 1317 | static void qfq_slot_remove(struct qfq_sched *q, struct qfq_group *grp, |
@@ -1357,8 +1383,6 @@ static void qfq_deactivate_agg(struct qfq_sched *q, struct qfq_aggregate *agg) | |||
1357 | __set_bit(grp->index, &q->bitmaps[s]); | 1383 | __set_bit(grp->index, &q->bitmaps[s]); |
1358 | } | 1384 | } |
1359 | } | 1385 | } |
1360 | |||
1361 | qfq_update_eligible(q); | ||
1362 | } | 1386 | } |
1363 | 1387 | ||
1364 | static void qfq_qlen_notify(struct Qdisc *sch, unsigned long arg) | 1388 | static void qfq_qlen_notify(struct Qdisc *sch, unsigned long arg) |