diff options
Diffstat (limited to 'net/sched/sch_qfq.c')
-rw-r--r-- | net/sched/sch_qfq.c | 95 |
1 files changed, 69 insertions, 26 deletions
diff --git a/net/sched/sch_qfq.c b/net/sched/sch_qfq.c index 9af01f3df18c..e4723d31fdd5 100644 --- a/net/sched/sch_qfq.c +++ b/net/sched/sch_qfq.c | |||
@@ -203,6 +203,34 @@ out: | |||
203 | return index; | 203 | return index; |
204 | } | 204 | } |
205 | 205 | ||
206 | /* Length of the next packet (0 if the queue is empty). */ | ||
207 | static unsigned int qdisc_peek_len(struct Qdisc *sch) | ||
208 | { | ||
209 | struct sk_buff *skb; | ||
210 | |||
211 | skb = sch->ops->peek(sch); | ||
212 | return skb ? qdisc_pkt_len(skb) : 0; | ||
213 | } | ||
214 | |||
215 | static void qfq_deactivate_class(struct qfq_sched *, struct qfq_class *); | ||
216 | static void qfq_activate_class(struct qfq_sched *q, struct qfq_class *cl, | ||
217 | unsigned int len); | ||
218 | |||
219 | static void qfq_update_class_params(struct qfq_sched *q, struct qfq_class *cl, | ||
220 | u32 lmax, u32 inv_w, int delta_w) | ||
221 | { | ||
222 | int i; | ||
223 | |||
224 | /* update qfq-specific data */ | ||
225 | cl->lmax = lmax; | ||
226 | cl->inv_w = inv_w; | ||
227 | i = qfq_calc_index(cl->inv_w, cl->lmax); | ||
228 | |||
229 | cl->grp = &q->groups[i]; | ||
230 | |||
231 | q->wsum += delta_w; | ||
232 | } | ||
233 | |||
206 | static int qfq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, | 234 | static int qfq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, |
207 | struct nlattr **tca, unsigned long *arg) | 235 | struct nlattr **tca, unsigned long *arg) |
208 | { | 236 | { |
@@ -250,6 +278,8 @@ static int qfq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, | |||
250 | lmax = 1UL << QFQ_MTU_SHIFT; | 278 | lmax = 1UL << QFQ_MTU_SHIFT; |
251 | 279 | ||
252 | if (cl != NULL) { | 280 | if (cl != NULL) { |
281 | bool need_reactivation = false; | ||
282 | |||
253 | if (tca[TCA_RATE]) { | 283 | if (tca[TCA_RATE]) { |
254 | err = gen_replace_estimator(&cl->bstats, &cl->rate_est, | 284 | err = gen_replace_estimator(&cl->bstats, &cl->rate_est, |
255 | qdisc_root_sleeping_lock(sch), | 285 | qdisc_root_sleeping_lock(sch), |
@@ -258,12 +288,29 @@ static int qfq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, | |||
258 | return err; | 288 | return err; |
259 | } | 289 | } |
260 | 290 | ||
261 | if (inv_w != cl->inv_w) { | 291 | if (lmax == cl->lmax && inv_w == cl->inv_w) |
262 | sch_tree_lock(sch); | 292 | return 0; /* nothing to update */ |
263 | q->wsum += delta_w; | 293 | |
264 | cl->inv_w = inv_w; | 294 | i = qfq_calc_index(inv_w, lmax); |
265 | sch_tree_unlock(sch); | 295 | sch_tree_lock(sch); |
296 | if (&q->groups[i] != cl->grp && cl->qdisc->q.qlen > 0) { | ||
297 | /* | ||
298 | * shift cl->F back, to not charge the | ||
299 | * class for the not-yet-served head | ||
300 | * packet | ||
301 | */ | ||
302 | cl->F = cl->S; | ||
303 | /* remove class from its slot in the old group */ | ||
304 | qfq_deactivate_class(q, cl); | ||
305 | need_reactivation = true; | ||
266 | } | 306 | } |
307 | |||
308 | qfq_update_class_params(q, cl, lmax, inv_w, delta_w); | ||
309 | |||
310 | if (need_reactivation) /* activate in new group */ | ||
311 | qfq_activate_class(q, cl, qdisc_peek_len(cl->qdisc)); | ||
312 | sch_tree_unlock(sch); | ||
313 | |||
267 | return 0; | 314 | return 0; |
268 | } | 315 | } |
269 | 316 | ||
@@ -273,11 +320,8 @@ static int qfq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, | |||
273 | 320 | ||
274 | cl->refcnt = 1; | 321 | cl->refcnt = 1; |
275 | cl->common.classid = classid; | 322 | cl->common.classid = classid; |
276 | cl->lmax = lmax; | ||
277 | cl->inv_w = inv_w; | ||
278 | i = qfq_calc_index(cl->inv_w, cl->lmax); | ||
279 | 323 | ||
280 | cl->grp = &q->groups[i]; | 324 | qfq_update_class_params(q, cl, lmax, inv_w, delta_w); |
281 | 325 | ||
282 | cl->qdisc = qdisc_create_dflt(sch->dev_queue, | 326 | cl->qdisc = qdisc_create_dflt(sch->dev_queue, |
283 | &pfifo_qdisc_ops, classid); | 327 | &pfifo_qdisc_ops, classid); |
@@ -294,7 +338,6 @@ static int qfq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, | |||
294 | return err; | 338 | return err; |
295 | } | 339 | } |
296 | } | 340 | } |
297 | q->wsum += weight; | ||
298 | 341 | ||
299 | sch_tree_lock(sch); | 342 | sch_tree_lock(sch); |
300 | qdisc_class_hash_insert(&q->clhash, &cl->common); | 343 | qdisc_class_hash_insert(&q->clhash, &cl->common); |
@@ -711,15 +754,6 @@ static void qfq_update_eligible(struct qfq_sched *q, u64 old_V) | |||
711 | } | 754 | } |
712 | } | 755 | } |
713 | 756 | ||
714 | /* What is length of next packet in queue (0 if queue is empty) */ | ||
715 | static unsigned int qdisc_peek_len(struct Qdisc *sch) | ||
716 | { | ||
717 | struct sk_buff *skb; | ||
718 | |||
719 | skb = sch->ops->peek(sch); | ||
720 | return skb ? qdisc_pkt_len(skb) : 0; | ||
721 | } | ||
722 | |||
723 | /* | 757 | /* |
724 | * Updates the class, returns true if also the group needs to be updated. | 758 | * Updates the class, returns true if also the group needs to be updated. |
725 | */ | 759 | */ |
@@ -843,11 +877,8 @@ static void qfq_update_start(struct qfq_sched *q, struct qfq_class *cl) | |||
843 | static int qfq_enqueue(struct sk_buff *skb, struct Qdisc *sch) | 877 | static int qfq_enqueue(struct sk_buff *skb, struct Qdisc *sch) |
844 | { | 878 | { |
845 | struct qfq_sched *q = qdisc_priv(sch); | 879 | struct qfq_sched *q = qdisc_priv(sch); |
846 | struct qfq_group *grp; | ||
847 | struct qfq_class *cl; | 880 | struct qfq_class *cl; |
848 | int err; | 881 | int err; |
849 | u64 roundedS; | ||
850 | int s; | ||
851 | 882 | ||
852 | cl = qfq_classify(skb, sch, &err); | 883 | cl = qfq_classify(skb, sch, &err); |
853 | if (cl == NULL) { | 884 | if (cl == NULL) { |
@@ -876,11 +907,25 @@ static int qfq_enqueue(struct sk_buff *skb, struct Qdisc *sch) | |||
876 | return err; | 907 | return err; |
877 | 908 | ||
878 | /* If reach this point, queue q was idle */ | 909 | /* If reach this point, queue q was idle */ |
879 | grp = cl->grp; | 910 | qfq_activate_class(q, cl, qdisc_pkt_len(skb)); |
911 | |||
912 | return err; | ||
913 | } | ||
914 | |||
915 | /* | ||
916 | * Handle class switch from idle to backlogged. | ||
917 | */ | ||
918 | static void qfq_activate_class(struct qfq_sched *q, struct qfq_class *cl, | ||
919 | unsigned int pkt_len) | ||
920 | { | ||
921 | struct qfq_group *grp = cl->grp; | ||
922 | u64 roundedS; | ||
923 | int s; | ||
924 | |||
880 | qfq_update_start(q, cl); | 925 | qfq_update_start(q, cl); |
881 | 926 | ||
882 | /* compute new finish time and rounded start. */ | 927 | /* compute new finish time and rounded start. */ |
883 | cl->F = cl->S + (u64)qdisc_pkt_len(skb) * cl->inv_w; | 928 | cl->F = cl->S + (u64)pkt_len * cl->inv_w; |
884 | roundedS = qfq_round_down(cl->S, grp->slot_shift); | 929 | roundedS = qfq_round_down(cl->S, grp->slot_shift); |
885 | 930 | ||
886 | /* | 931 | /* |
@@ -917,8 +962,6 @@ static int qfq_enqueue(struct sk_buff *skb, struct Qdisc *sch) | |||
917 | 962 | ||
918 | skip_update: | 963 | skip_update: |
919 | qfq_slot_insert(grp, cl, roundedS); | 964 | qfq_slot_insert(grp, cl, roundedS); |
920 | |||
921 | return err; | ||
922 | } | 965 | } |
923 | 966 | ||
924 | 967 | ||