hc
2024-10-22 8ac6c7a54ed1b98d142dce24b11c6de6a1e239a5
kernel/net/sched/sch_qfq.c
....@@ -1,12 +1,9 @@
1
+// SPDX-License-Identifier: GPL-2.0-only
12 /*
23 * net/sched/sch_qfq.c Quick Fair Queueing Plus Scheduler.
34 *
45 * Copyright (c) 2009 Fabio Checconi, Luigi Rizzo, and Paolo Valente.
56 * Copyright (c) 2012 Paolo Valente.
6
- *
7
- * This program is free software; you can redistribute it and/or
8
- * modify it under the terms of the GNU General Public License
9
- * version 2 as published by the Free Software Foundation.
107 */
118
129 #include <linux/module.h>
....@@ -116,6 +113,7 @@
116113
117114 #define QFQ_MTU_SHIFT 16 /* to support TSO/GSO */
118115 #define QFQ_MIN_LMAX 512 /* see qfq_slot_insert */
116
+#define QFQ_MAX_LMAX (1UL << QFQ_MTU_SHIFT)
119117
120118 #define QFQ_MAX_AGG_CLASSES 8 /* max num classes per aggregate allowed */
121119
....@@ -217,18 +215,14 @@
217215 return container_of(clc, struct qfq_class, common);
218216 }
219217
220
-static void qfq_purge_queue(struct qfq_class *cl)
221
-{
222
- unsigned int len = cl->qdisc->q.qlen;
223
- unsigned int backlog = cl->qdisc->qstats.backlog;
224
-
225
- qdisc_reset(cl->qdisc);
226
- qdisc_tree_reduce_backlog(cl->qdisc, len, backlog);
227
-}
218
+static struct netlink_range_validation lmax_range = {
219
+ .min = QFQ_MIN_LMAX,
220
+ .max = QFQ_MAX_LMAX,
221
+};
228222
229223 static const struct nla_policy qfq_policy[TCA_QFQ_MAX + 1] = {
230
- [TCA_QFQ_WEIGHT] = { .type = NLA_U32 },
231
- [TCA_QFQ_LMAX] = { .type = NLA_U32 },
224
+ [TCA_QFQ_WEIGHT] = NLA_POLICY_RANGE(NLA_U32, 1, QFQ_MAX_WEIGHT),
225
+ [TCA_QFQ_LMAX] = NLA_POLICY_FULL_RANGE(NLA_U32, &lmax_range),
232226 };
233227
234228 /*
....@@ -387,8 +381,13 @@
387381 u32 lmax)
388382 {
389383 struct qfq_sched *q = qdisc_priv(sch);
390
- struct qfq_aggregate *new_agg = qfq_find_agg(q, lmax, weight);
384
+ struct qfq_aggregate *new_agg;
391385
386
+ /* 'lmax' can range from [QFQ_MIN_LMAX, pktlen + stab overhead] */
387
+ if (lmax > QFQ_MAX_LMAX)
388
+ return -EINVAL;
389
+
390
+ new_agg = qfq_find_agg(q, lmax, weight);
392391 if (new_agg == NULL) { /* create new aggregate */
393392 new_agg = kzalloc(sizeof(*new_agg), GFP_ATOMIC);
394393 if (new_agg == NULL)
....@@ -419,28 +418,27 @@
419418 return -EINVAL;
420419 }
421420
422
- err = nla_parse_nested(tb, TCA_QFQ_MAX, tca[TCA_OPTIONS], qfq_policy,
423
- NULL);
421
+ err = nla_parse_nested_deprecated(tb, TCA_QFQ_MAX, tca[TCA_OPTIONS],
422
+ qfq_policy, extack);
424423 if (err < 0)
425424 return err;
426425
427
- if (tb[TCA_QFQ_WEIGHT]) {
426
+ if (tb[TCA_QFQ_WEIGHT])
428427 weight = nla_get_u32(tb[TCA_QFQ_WEIGHT]);
429
- if (!weight || weight > (1UL << QFQ_MAX_WSHIFT)) {
430
- pr_notice("qfq: invalid weight %u\n", weight);
431
- return -EINVAL;
432
- }
433
- } else
428
+ else
434429 weight = 1;
435430
436431 if (tb[TCA_QFQ_LMAX]) {
437432 lmax = nla_get_u32(tb[TCA_QFQ_LMAX]);
438
- if (lmax < QFQ_MIN_LMAX || lmax > (1UL << QFQ_MTU_SHIFT)) {
439
- pr_notice("qfq: invalid max length %u\n", lmax);
433
+ } else {
434
+ /* MTU size is user controlled */
435
+ lmax = psched_mtu(qdisc_dev(sch));
436
+ if (lmax < QFQ_MIN_LMAX || lmax > QFQ_MAX_LMAX) {
437
+ NL_SET_ERR_MSG_MOD(extack,
438
+ "MTU size out of bounds for qfq");
440439 return -EINVAL;
441440 }
442
- } else
443
- lmax = psched_mtu(qdisc_dev(sch));
441
+ }
444442
445443 inv_w = ONE_FP / weight;
446444 weight = ONE_FP / inv_w;
....@@ -549,7 +547,7 @@
549547
550548 sch_tree_lock(sch);
551549
552
- qfq_purge_queue(cl);
550
+ qdisc_purge_queue(cl->qdisc);
553551 qdisc_class_hash_remove(&q->clhash, &cl->common);
554552
555553 sch_tree_unlock(sch);
....@@ -626,7 +624,7 @@
626624 tcm->tcm_handle = cl->common.classid;
627625 tcm->tcm_info = cl->qdisc->handle;
628626
629
- nest = nla_nest_start(skb, TCA_OPTIONS);
627
+ nest = nla_nest_start_noflag(skb, TCA_OPTIONS);
630628 if (nest == NULL)
631629 goto nla_put_failure;
632630 if (nla_put_u32(skb, TCA_QFQ_WEIGHT, cl->agg->class_weight) ||
....@@ -653,8 +651,7 @@
653651 if (gnet_stats_copy_basic(qdisc_root_sleeping_running(sch),
654652 d, NULL, &cl->bstats) < 0 ||
655653 gnet_stats_copy_rate_est(d, &cl->rate_est) < 0 ||
656
- gnet_stats_copy_queue(d, NULL,
657
- &cl->qdisc->qstats, cl->qdisc->q.qlen) < 0)
654
+ qdisc_qstats_copy(d, cl->qdisc) < 0)
658655 return -1;
659656
660657 return gnet_stats_copy_app(d, &xstats, sizeof(xstats));
....@@ -710,7 +707,7 @@
710707 case TC_ACT_STOLEN:
711708 case TC_ACT_TRAP:
712709 *qerr = NET_XMIT_SUCCESS | __NET_XMIT_STOLEN;
713
- /* fall through */
710
+ fallthrough;
714711 case TC_ACT_SHOT:
715712 return NULL;
716713 }
....@@ -982,10 +979,13 @@
982979 }
983980
984981 /* Dequeue head packet of the head class in the DRR queue of the aggregate. */
985
-static void agg_dequeue(struct qfq_aggregate *agg,
986
- struct qfq_class *cl, unsigned int len)
982
+static struct sk_buff *agg_dequeue(struct qfq_aggregate *agg,
983
+ struct qfq_class *cl, unsigned int len)
987984 {
988
- qdisc_dequeue_peeked(cl->qdisc);
985
+ struct sk_buff *skb = qdisc_dequeue_peeked(cl->qdisc);
986
+
987
+ if (!skb)
988
+ return NULL;
989989
990990 cl->deficit -= (int) len;
991991
....@@ -995,6 +995,8 @@
995995 cl->deficit += agg->lmax;
996996 list_move_tail(&cl->alist, &agg->active);
997997 }
998
+
999
+ return skb;
9981000 }
9991001
10001002 static inline struct sk_buff *qfq_peek_skb(struct qfq_aggregate *agg,
....@@ -1140,11 +1142,18 @@
11401142 if (!skb)
11411143 return NULL;
11421144
1143
- qdisc_qstats_backlog_dec(sch, skb);
11441145 sch->q.qlen--;
1146
+
1147
+ skb = agg_dequeue(in_serv_agg, cl, len);
1148
+
1149
+ if (!skb) {
1150
+ sch->q.qlen++;
1151
+ return NULL;
1152
+ }
1153
+
1154
+ qdisc_qstats_backlog_dec(sch, skb);
11451155 qdisc_bstats_update(sch, skb);
11461156
1147
- agg_dequeue(in_serv_agg, cl, len);
11481157 /* If lmax is lowered, through qfq_change_class, for a class
11491158 * owning pending packets with larger size than the new value
11501159 * of lmax, then the following condition may hold.
....@@ -1208,10 +1217,12 @@
12081217 static int qfq_enqueue(struct sk_buff *skb, struct Qdisc *sch,
12091218 struct sk_buff **to_free)
12101219 {
1220
+ unsigned int len = qdisc_pkt_len(skb), gso_segs;
12111221 struct qfq_sched *q = qdisc_priv(sch);
12121222 struct qfq_class *cl;
12131223 struct qfq_aggregate *agg;
12141224 int err = 0;
1225
+ bool first;
12151226
12161227 cl = qfq_classify(skb, sch, &err);
12171228 if (cl == NULL) {
....@@ -1222,17 +1233,18 @@
12221233 }
12231234 pr_debug("qfq_enqueue: cl = %x\n", cl->common.classid);
12241235
1225
- if (unlikely(cl->agg->lmax < qdisc_pkt_len(skb))) {
1236
+ if (unlikely(cl->agg->lmax < len)) {
12261237 pr_debug("qfq: increasing maxpkt from %u to %u for class %u",
1227
- cl->agg->lmax, qdisc_pkt_len(skb), cl->common.classid);
1228
- err = qfq_change_agg(sch, cl, cl->agg->class_weight,
1229
- qdisc_pkt_len(skb));
1238
+ cl->agg->lmax, len, cl->common.classid);
1239
+ err = qfq_change_agg(sch, cl, cl->agg->class_weight, len);
12301240 if (err) {
12311241 cl->qstats.drops++;
12321242 return qdisc_drop(skb, sch, to_free);
12331243 }
12341244 }
12351245
1246
+ gso_segs = skb_is_gso(skb) ? skb_shinfo(skb)->gso_segs : 1;
1247
+ first = !cl->qdisc->q.qlen;
12361248 err = qdisc_enqueue(skb, cl->qdisc, to_free);
12371249 if (unlikely(err != NET_XMIT_SUCCESS)) {
12381250 pr_debug("qfq_enqueue: enqueue failed %d\n", err);
....@@ -1243,16 +1255,17 @@
12431255 return err;
12441256 }
12451257
1246
- bstats_update(&cl->bstats, skb);
1247
- qdisc_qstats_backlog_inc(sch, skb);
1258
+ cl->bstats.bytes += len;
1259
+ cl->bstats.packets += gso_segs;
1260
+ sch->qstats.backlog += len;
12481261 ++sch->q.qlen;
12491262
12501263 agg = cl->agg;
12511264 /* if the queue was not empty, then done here */
1252
- if (cl->qdisc->q.qlen != 1) {
1265
+ if (!first) {
12531266 if (unlikely(skb == cl->qdisc->ops->peek(cl->qdisc)) &&
12541267 list_first_entry(&agg->active, struct qfq_class, alist)
1255
- == cl && cl->deficit < qdisc_pkt_len(skb))
1268
+ == cl && cl->deficit < len)
12561269 list_move_tail(&cl->alist, &agg->active);
12571270
12581271 return err;
....@@ -1467,8 +1480,6 @@
14671480 qdisc_reset(cl->qdisc);
14681481 }
14691482 }
1470
- sch->qstats.backlog = 0;
1471
- sch->q.qlen = 0;
14721483 }
14731484
14741485 static void qfq_destroy_qdisc(struct Qdisc *sch)