| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * net/sched/sch_qfq.c Quick Fair Queueing Plus Scheduler. |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * Copyright (c) 2009 Fabio Checconi, Luigi Rizzo, and Paolo Valente. |
|---|
| 5 | 6 | * 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. |
|---|
| 10 | 7 | */ |
|---|
| 11 | 8 | |
|---|
| 12 | 9 | #include <linux/module.h> |
|---|
| .. | .. |
|---|
| 116 | 113 | |
|---|
| 117 | 114 | #define QFQ_MTU_SHIFT 16 /* to support TSO/GSO */ |
|---|
| 118 | 115 | #define QFQ_MIN_LMAX 512 /* see qfq_slot_insert */ |
|---|
| 116 | +#define QFQ_MAX_LMAX (1UL << QFQ_MTU_SHIFT) |
|---|
| 119 | 117 | |
|---|
| 120 | 118 | #define QFQ_MAX_AGG_CLASSES 8 /* max num classes per aggregate allowed */ |
|---|
| 121 | 119 | |
|---|
| .. | .. |
|---|
| 217 | 215 | return container_of(clc, struct qfq_class, common); |
|---|
| 218 | 216 | } |
|---|
| 219 | 217 | |
|---|
| 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 | +}; |
|---|
| 228 | 222 | |
|---|
| 229 | 223 | 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), |
|---|
| 232 | 226 | }; |
|---|
| 233 | 227 | |
|---|
| 234 | 228 | /* |
|---|
| .. | .. |
|---|
| 387 | 381 | u32 lmax) |
|---|
| 388 | 382 | { |
|---|
| 389 | 383 | 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; |
|---|
| 391 | 385 | |
|---|
| 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); |
|---|
| 392 | 391 | if (new_agg == NULL) { /* create new aggregate */ |
|---|
| 393 | 392 | new_agg = kzalloc(sizeof(*new_agg), GFP_ATOMIC); |
|---|
| 394 | 393 | if (new_agg == NULL) |
|---|
| .. | .. |
|---|
| 419 | 418 | return -EINVAL; |
|---|
| 420 | 419 | } |
|---|
| 421 | 420 | |
|---|
| 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); |
|---|
| 424 | 423 | if (err < 0) |
|---|
| 425 | 424 | return err; |
|---|
| 426 | 425 | |
|---|
| 427 | | - if (tb[TCA_QFQ_WEIGHT]) { |
|---|
| 426 | + if (tb[TCA_QFQ_WEIGHT]) |
|---|
| 428 | 427 | 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 |
|---|
| 434 | 429 | weight = 1; |
|---|
| 435 | 430 | |
|---|
| 436 | 431 | if (tb[TCA_QFQ_LMAX]) { |
|---|
| 437 | 432 | 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"); |
|---|
| 440 | 439 | return -EINVAL; |
|---|
| 441 | 440 | } |
|---|
| 442 | | - } else |
|---|
| 443 | | - lmax = psched_mtu(qdisc_dev(sch)); |
|---|
| 441 | + } |
|---|
| 444 | 442 | |
|---|
| 445 | 443 | inv_w = ONE_FP / weight; |
|---|
| 446 | 444 | weight = ONE_FP / inv_w; |
|---|
| .. | .. |
|---|
| 549 | 547 | |
|---|
| 550 | 548 | sch_tree_lock(sch); |
|---|
| 551 | 549 | |
|---|
| 552 | | - qfq_purge_queue(cl); |
|---|
| 550 | + qdisc_purge_queue(cl->qdisc); |
|---|
| 553 | 551 | qdisc_class_hash_remove(&q->clhash, &cl->common); |
|---|
| 554 | 552 | |
|---|
| 555 | 553 | sch_tree_unlock(sch); |
|---|
| .. | .. |
|---|
| 626 | 624 | tcm->tcm_handle = cl->common.classid; |
|---|
| 627 | 625 | tcm->tcm_info = cl->qdisc->handle; |
|---|
| 628 | 626 | |
|---|
| 629 | | - nest = nla_nest_start(skb, TCA_OPTIONS); |
|---|
| 627 | + nest = nla_nest_start_noflag(skb, TCA_OPTIONS); |
|---|
| 630 | 628 | if (nest == NULL) |
|---|
| 631 | 629 | goto nla_put_failure; |
|---|
| 632 | 630 | if (nla_put_u32(skb, TCA_QFQ_WEIGHT, cl->agg->class_weight) || |
|---|
| .. | .. |
|---|
| 653 | 651 | if (gnet_stats_copy_basic(qdisc_root_sleeping_running(sch), |
|---|
| 654 | 652 | d, NULL, &cl->bstats) < 0 || |
|---|
| 655 | 653 | 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) |
|---|
| 658 | 655 | return -1; |
|---|
| 659 | 656 | |
|---|
| 660 | 657 | return gnet_stats_copy_app(d, &xstats, sizeof(xstats)); |
|---|
| .. | .. |
|---|
| 710 | 707 | case TC_ACT_STOLEN: |
|---|
| 711 | 708 | case TC_ACT_TRAP: |
|---|
| 712 | 709 | *qerr = NET_XMIT_SUCCESS | __NET_XMIT_STOLEN; |
|---|
| 713 | | - /* fall through */ |
|---|
| 710 | + fallthrough; |
|---|
| 714 | 711 | case TC_ACT_SHOT: |
|---|
| 715 | 712 | return NULL; |
|---|
| 716 | 713 | } |
|---|
| .. | .. |
|---|
| 982 | 979 | } |
|---|
| 983 | 980 | |
|---|
| 984 | 981 | /* 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) |
|---|
| 987 | 984 | { |
|---|
| 988 | | - qdisc_dequeue_peeked(cl->qdisc); |
|---|
| 985 | + struct sk_buff *skb = qdisc_dequeue_peeked(cl->qdisc); |
|---|
| 986 | + |
|---|
| 987 | + if (!skb) |
|---|
| 988 | + return NULL; |
|---|
| 989 | 989 | |
|---|
| 990 | 990 | cl->deficit -= (int) len; |
|---|
| 991 | 991 | |
|---|
| .. | .. |
|---|
| 995 | 995 | cl->deficit += agg->lmax; |
|---|
| 996 | 996 | list_move_tail(&cl->alist, &agg->active); |
|---|
| 997 | 997 | } |
|---|
| 998 | + |
|---|
| 999 | + return skb; |
|---|
| 998 | 1000 | } |
|---|
| 999 | 1001 | |
|---|
| 1000 | 1002 | static inline struct sk_buff *qfq_peek_skb(struct qfq_aggregate *agg, |
|---|
| .. | .. |
|---|
| 1140 | 1142 | if (!skb) |
|---|
| 1141 | 1143 | return NULL; |
|---|
| 1142 | 1144 | |
|---|
| 1143 | | - qdisc_qstats_backlog_dec(sch, skb); |
|---|
| 1144 | 1145 | 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); |
|---|
| 1145 | 1155 | qdisc_bstats_update(sch, skb); |
|---|
| 1146 | 1156 | |
|---|
| 1147 | | - agg_dequeue(in_serv_agg, cl, len); |
|---|
| 1148 | 1157 | /* If lmax is lowered, through qfq_change_class, for a class |
|---|
| 1149 | 1158 | * owning pending packets with larger size than the new value |
|---|
| 1150 | 1159 | * of lmax, then the following condition may hold. |
|---|
| .. | .. |
|---|
| 1208 | 1217 | static int qfq_enqueue(struct sk_buff *skb, struct Qdisc *sch, |
|---|
| 1209 | 1218 | struct sk_buff **to_free) |
|---|
| 1210 | 1219 | { |
|---|
| 1220 | + unsigned int len = qdisc_pkt_len(skb), gso_segs; |
|---|
| 1211 | 1221 | struct qfq_sched *q = qdisc_priv(sch); |
|---|
| 1212 | 1222 | struct qfq_class *cl; |
|---|
| 1213 | 1223 | struct qfq_aggregate *agg; |
|---|
| 1214 | 1224 | int err = 0; |
|---|
| 1225 | + bool first; |
|---|
| 1215 | 1226 | |
|---|
| 1216 | 1227 | cl = qfq_classify(skb, sch, &err); |
|---|
| 1217 | 1228 | if (cl == NULL) { |
|---|
| .. | .. |
|---|
| 1222 | 1233 | } |
|---|
| 1223 | 1234 | pr_debug("qfq_enqueue: cl = %x\n", cl->common.classid); |
|---|
| 1224 | 1235 | |
|---|
| 1225 | | - if (unlikely(cl->agg->lmax < qdisc_pkt_len(skb))) { |
|---|
| 1236 | + if (unlikely(cl->agg->lmax < len)) { |
|---|
| 1226 | 1237 | 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); |
|---|
| 1230 | 1240 | if (err) { |
|---|
| 1231 | 1241 | cl->qstats.drops++; |
|---|
| 1232 | 1242 | return qdisc_drop(skb, sch, to_free); |
|---|
| 1233 | 1243 | } |
|---|
| 1234 | 1244 | } |
|---|
| 1235 | 1245 | |
|---|
| 1246 | + gso_segs = skb_is_gso(skb) ? skb_shinfo(skb)->gso_segs : 1; |
|---|
| 1247 | + first = !cl->qdisc->q.qlen; |
|---|
| 1236 | 1248 | err = qdisc_enqueue(skb, cl->qdisc, to_free); |
|---|
| 1237 | 1249 | if (unlikely(err != NET_XMIT_SUCCESS)) { |
|---|
| 1238 | 1250 | pr_debug("qfq_enqueue: enqueue failed %d\n", err); |
|---|
| .. | .. |
|---|
| 1243 | 1255 | return err; |
|---|
| 1244 | 1256 | } |
|---|
| 1245 | 1257 | |
|---|
| 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; |
|---|
| 1248 | 1261 | ++sch->q.qlen; |
|---|
| 1249 | 1262 | |
|---|
| 1250 | 1263 | agg = cl->agg; |
|---|
| 1251 | 1264 | /* if the queue was not empty, then done here */ |
|---|
| 1252 | | - if (cl->qdisc->q.qlen != 1) { |
|---|
| 1265 | + if (!first) { |
|---|
| 1253 | 1266 | if (unlikely(skb == cl->qdisc->ops->peek(cl->qdisc)) && |
|---|
| 1254 | 1267 | list_first_entry(&agg->active, struct qfq_class, alist) |
|---|
| 1255 | | - == cl && cl->deficit < qdisc_pkt_len(skb)) |
|---|
| 1268 | + == cl && cl->deficit < len) |
|---|
| 1256 | 1269 | list_move_tail(&cl->alist, &agg->active); |
|---|
| 1257 | 1270 | |
|---|
| 1258 | 1271 | return err; |
|---|
| .. | .. |
|---|
| 1467 | 1480 | qdisc_reset(cl->qdisc); |
|---|
| 1468 | 1481 | } |
|---|
| 1469 | 1482 | } |
|---|
| 1470 | | - sch->qstats.backlog = 0; |
|---|
| 1471 | | - sch->q.qlen = 0; |
|---|
| 1472 | 1483 | } |
|---|
| 1473 | 1484 | |
|---|
| 1474 | 1485 | static void qfq_destroy_qdisc(struct Qdisc *sch) |
|---|