.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | 3 | * net/sched/sch_tbf.c Token Bucket Filter queue. |
---|
3 | | - * |
---|
4 | | - * This program is free software; you can redistribute it and/or |
---|
5 | | - * modify it under the terms of the GNU General Public License |
---|
6 | | - * as published by the Free Software Foundation; either version |
---|
7 | | - * 2 of the License, or (at your option) any later version. |
---|
8 | 4 | * |
---|
9 | 5 | * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> |
---|
10 | 6 | * Dmitry Torokhov <dtor@mail.ru> - allow attaching inner qdiscs - |
---|
11 | 7 | * original idea by Martin Devera |
---|
12 | | - * |
---|
13 | 8 | */ |
---|
14 | 9 | |
---|
15 | 10 | #include <linux/module.h> |
---|
.. | .. |
---|
20 | 15 | #include <linux/skbuff.h> |
---|
21 | 16 | #include <net/netlink.h> |
---|
22 | 17 | #include <net/sch_generic.h> |
---|
| 18 | +#include <net/pkt_cls.h> |
---|
23 | 19 | #include <net/pkt_sched.h> |
---|
24 | 20 | |
---|
25 | 21 | |
---|
.. | .. |
---|
142 | 138 | return len; |
---|
143 | 139 | } |
---|
144 | 140 | |
---|
| 141 | +static void tbf_offload_change(struct Qdisc *sch) |
---|
| 142 | +{ |
---|
| 143 | + struct tbf_sched_data *q = qdisc_priv(sch); |
---|
| 144 | + struct net_device *dev = qdisc_dev(sch); |
---|
| 145 | + struct tc_tbf_qopt_offload qopt; |
---|
| 146 | + |
---|
| 147 | + if (!tc_can_offload(dev) || !dev->netdev_ops->ndo_setup_tc) |
---|
| 148 | + return; |
---|
| 149 | + |
---|
| 150 | + qopt.command = TC_TBF_REPLACE; |
---|
| 151 | + qopt.handle = sch->handle; |
---|
| 152 | + qopt.parent = sch->parent; |
---|
| 153 | + qopt.replace_params.rate = q->rate; |
---|
| 154 | + qopt.replace_params.max_size = q->max_size; |
---|
| 155 | + qopt.replace_params.qstats = &sch->qstats; |
---|
| 156 | + |
---|
| 157 | + dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_QDISC_TBF, &qopt); |
---|
| 158 | +} |
---|
| 159 | + |
---|
| 160 | +static void tbf_offload_destroy(struct Qdisc *sch) |
---|
| 161 | +{ |
---|
| 162 | + struct net_device *dev = qdisc_dev(sch); |
---|
| 163 | + struct tc_tbf_qopt_offload qopt; |
---|
| 164 | + |
---|
| 165 | + if (!tc_can_offload(dev) || !dev->netdev_ops->ndo_setup_tc) |
---|
| 166 | + return; |
---|
| 167 | + |
---|
| 168 | + qopt.command = TC_TBF_DESTROY; |
---|
| 169 | + qopt.handle = sch->handle; |
---|
| 170 | + qopt.parent = sch->parent; |
---|
| 171 | + dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_QDISC_TBF, &qopt); |
---|
| 172 | +} |
---|
| 173 | + |
---|
| 174 | +static int tbf_offload_dump(struct Qdisc *sch) |
---|
| 175 | +{ |
---|
| 176 | + struct tc_tbf_qopt_offload qopt; |
---|
| 177 | + |
---|
| 178 | + qopt.command = TC_TBF_STATS; |
---|
| 179 | + qopt.handle = sch->handle; |
---|
| 180 | + qopt.parent = sch->parent; |
---|
| 181 | + qopt.stats.bstats = &sch->bstats; |
---|
| 182 | + qopt.stats.qstats = &sch->qstats; |
---|
| 183 | + |
---|
| 184 | + return qdisc_offload_dump_helper(sch, TC_SETUP_QDISC_TBF, &qopt); |
---|
| 185 | +} |
---|
| 186 | + |
---|
145 | 187 | /* GSO packet is too big, segment it so that tbf can transmit |
---|
146 | 188 | * each segment in time |
---|
147 | 189 | */ |
---|
.. | .. |
---|
160 | 202 | return qdisc_drop(skb, sch, to_free); |
---|
161 | 203 | |
---|
162 | 204 | nb = 0; |
---|
163 | | - while (segs) { |
---|
164 | | - nskb = segs->next; |
---|
165 | | - segs->next = NULL; |
---|
| 205 | + skb_list_walk_safe(segs, segs, nskb) { |
---|
| 206 | + skb_mark_not_on_list(segs); |
---|
166 | 207 | qdisc_skb_cb(segs)->pkt_len = segs->len; |
---|
167 | 208 | len += segs->len; |
---|
168 | 209 | ret = qdisc_enqueue(segs, q->qdisc, to_free); |
---|
.. | .. |
---|
172 | 213 | } else { |
---|
173 | 214 | nb++; |
---|
174 | 215 | } |
---|
175 | | - segs = nskb; |
---|
176 | 216 | } |
---|
177 | 217 | sch->q.qlen += nb; |
---|
178 | 218 | if (nb > 1) |
---|
.. | .. |
---|
185 | 225 | struct sk_buff **to_free) |
---|
186 | 226 | { |
---|
187 | 227 | struct tbf_sched_data *q = qdisc_priv(sch); |
---|
| 228 | + unsigned int len = qdisc_pkt_len(skb); |
---|
188 | 229 | int ret; |
---|
189 | 230 | |
---|
190 | 231 | if (qdisc_pkt_len(skb) > q->max_size) { |
---|
.. | .. |
---|
200 | 241 | return ret; |
---|
201 | 242 | } |
---|
202 | 243 | |
---|
203 | | - qdisc_qstats_backlog_inc(sch, skb); |
---|
| 244 | + sch->qstats.backlog += len; |
---|
204 | 245 | sch->q.qlen++; |
---|
205 | 246 | return NET_XMIT_SUCCESS; |
---|
206 | 247 | } |
---|
.. | .. |
---|
275 | 316 | struct tbf_sched_data *q = qdisc_priv(sch); |
---|
276 | 317 | |
---|
277 | 318 | qdisc_reset(q->qdisc); |
---|
278 | | - sch->qstats.backlog = 0; |
---|
279 | | - sch->q.qlen = 0; |
---|
280 | 319 | q->t_c = ktime_get_ns(); |
---|
281 | 320 | q->tokens = q->buffer; |
---|
282 | 321 | q->ptokens = q->mtu; |
---|
.. | .. |
---|
301 | 340 | struct nlattr *tb[TCA_TBF_MAX + 1]; |
---|
302 | 341 | struct tc_tbf_qopt *qopt; |
---|
303 | 342 | struct Qdisc *child = NULL; |
---|
| 343 | + struct Qdisc *old = NULL; |
---|
304 | 344 | struct psched_ratecfg rate; |
---|
305 | 345 | struct psched_ratecfg peak; |
---|
306 | 346 | u64 max_size; |
---|
307 | 347 | s64 buffer, mtu; |
---|
308 | 348 | u64 rate64 = 0, prate64 = 0; |
---|
309 | 349 | |
---|
310 | | - err = nla_parse_nested(tb, TCA_TBF_MAX, opt, tbf_policy, NULL); |
---|
| 350 | + err = nla_parse_nested_deprecated(tb, TCA_TBF_MAX, opt, tbf_policy, |
---|
| 351 | + NULL); |
---|
311 | 352 | if (err < 0) |
---|
312 | 353 | return err; |
---|
313 | 354 | |
---|
.. | .. |
---|
390 | 431 | |
---|
391 | 432 | sch_tree_lock(sch); |
---|
392 | 433 | if (child) { |
---|
393 | | - qdisc_tree_reduce_backlog(q->qdisc, q->qdisc->q.qlen, |
---|
394 | | - q->qdisc->qstats.backlog); |
---|
395 | | - qdisc_put(q->qdisc); |
---|
| 434 | + qdisc_tree_flush_backlog(q->qdisc); |
---|
| 435 | + old = q->qdisc; |
---|
396 | 436 | q->qdisc = child; |
---|
397 | 437 | } |
---|
398 | 438 | q->limit = qopt->limit; |
---|
.. | .. |
---|
412 | 452 | memcpy(&q->peak, &peak, sizeof(struct psched_ratecfg)); |
---|
413 | 453 | |
---|
414 | 454 | sch_tree_unlock(sch); |
---|
| 455 | + qdisc_put(old); |
---|
415 | 456 | err = 0; |
---|
| 457 | + |
---|
| 458 | + tbf_offload_change(sch); |
---|
416 | 459 | done: |
---|
417 | 460 | return err; |
---|
418 | 461 | } |
---|
.. | .. |
---|
438 | 481 | struct tbf_sched_data *q = qdisc_priv(sch); |
---|
439 | 482 | |
---|
440 | 483 | qdisc_watchdog_cancel(&q->watchdog); |
---|
| 484 | + tbf_offload_destroy(sch); |
---|
441 | 485 | qdisc_put(q->qdisc); |
---|
442 | 486 | } |
---|
443 | 487 | |
---|
.. | .. |
---|
446 | 490 | struct tbf_sched_data *q = qdisc_priv(sch); |
---|
447 | 491 | struct nlattr *nest; |
---|
448 | 492 | struct tc_tbf_qopt opt; |
---|
| 493 | + int err; |
---|
449 | 494 | |
---|
450 | | - sch->qstats.backlog = q->qdisc->qstats.backlog; |
---|
451 | | - nest = nla_nest_start(skb, TCA_OPTIONS); |
---|
| 495 | + err = tbf_offload_dump(sch); |
---|
| 496 | + if (err) |
---|
| 497 | + return err; |
---|
| 498 | + |
---|
| 499 | + nest = nla_nest_start_noflag(skb, TCA_OPTIONS); |
---|
452 | 500 | if (nest == NULL) |
---|
453 | 501 | goto nla_put_failure; |
---|
454 | 502 | |
---|