blob: 5f72f3f916a5a6c030eccc67332d6c86795dca4b [file] [log] [blame]
Thomas Gleixner2874c5f2019-05-27 08:55:01 +02001// SPDX-License-Identifier: GPL-2.0-or-later
Linus Torvalds1da177e2005-04-16 15:20:36 -07002/*
3 * net/sched/sch_tbf.c Token Bucket Filter queue.
4 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07005 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
6 * Dmitry Torokhov <dtor@mail.ru> - allow attaching inner qdiscs -
7 * original idea by Martin Devera
Linus Torvalds1da177e2005-04-16 15:20:36 -07008 */
9
Linus Torvalds1da177e2005-04-16 15:20:36 -070010#include <linux/module.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070011#include <linux/types.h>
12#include <linux/kernel.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070013#include <linux/string.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070014#include <linux/errno.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070015#include <linux/skbuff.h>
Patrick McHardy0ba48052007-07-02 22:49:07 -070016#include <net/netlink.h>
Jiri Pirkob757c932013-02-12 00:12:05 +000017#include <net/sch_generic.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070018#include <net/pkt_sched.h>
19
20
21/* Simple Token Bucket Filter.
22 =======================================
23
24 SOURCE.
25 -------
26
27 None.
28
29 Description.
30 ------------
31
32 A data flow obeys TBF with rate R and depth B, if for any
33 time interval t_i...t_f the number of transmitted bits
34 does not exceed B + R*(t_f-t_i).
35
36 Packetized version of this definition:
37 The sequence of packets of sizes s_i served at moments t_i
38 obeys TBF, if for any i<=k:
39
40 s_i+....+s_k <= B + R*(t_k - t_i)
41
42 Algorithm.
43 ----------
44
45 Let N(t_i) be B/R initially and N(t) grow continuously with time as:
46
47 N(t+delta) = min{B/R, N(t) + delta}
48
49 If the first packet in queue has length S, it may be
50 transmitted only at the time t_* when S/R <= N(t_*),
51 and in this case N(t) jumps:
52
53 N(t_* + 0) = N(t_* - 0) - S/R.
54
55
56
57 Actually, QoS requires two TBF to be applied to a data stream.
58 One of them controls steady state burst size, another
59 one with rate P (peak rate) and depth M (equal to link MTU)
60 limits bursts at a smaller time scale.
61
62 It is easy to see that P>R, and B>M. If P is infinity, this double
63 TBF is equivalent to a single one.
64
65 When TBF works in reshaping mode, latency is estimated as:
66
67 lat = max ((L-B)/R, (L-M)/P)
68
69
70 NOTES.
71 ------
72
73 If TBF throttles, it starts a watchdog timer, which will wake it up
74 when it is ready to transmit.
75 Note that the minimal timer resolution is 1/HZ.
76 If no new packets arrive during this period,
77 or if the device is not awaken by EOI for some previous packet,
78 TBF can stop its activity for 1/HZ.
79
80
81 This means, that with depth B, the maximal rate is
82
83 R_crit = B*HZ
84
85 F.e. for 10Mbit ethernet and HZ=100 the minimal allowed B is ~10Kbytes.
86
87 Note that the peak rate TBF is much more tough: with MTU 1500
88 P_crit = 150Kbytes/sec. So, if you need greater peak
89 rates, use alpha with HZ=1000 :-)
90
91 With classful TBF, limit is just kept for backwards compatibility.
92 It is passed to the default bfifo qdisc - if the inner qdisc is
93 changed the limit is not effective anymore.
94*/
95
Eric Dumazetcc7ec452011-01-19 19:26:56 +000096struct tbf_sched_data {
Linus Torvalds1da177e2005-04-16 15:20:36 -070097/* Parameters */
98 u32 limit; /* Maximal length of backlog: bytes */
Hiroaki SHIMODAa135e592014-03-02 17:30:26 +090099 u32 max_size;
Jiri Pirkob757c932013-02-12 00:12:05 +0000100 s64 buffer; /* Token bucket depth/rate: MUST BE >= MTU/B */
101 s64 mtu;
Jiri Pirkob757c932013-02-12 00:12:05 +0000102 struct psched_ratecfg rate;
103 struct psched_ratecfg peak;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700104
105/* Variables */
Jiri Pirkob757c932013-02-12 00:12:05 +0000106 s64 tokens; /* Current number of B tokens */
107 s64 ptokens; /* Current number of P tokens */
108 s64 t_c; /* Time check-point */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700109 struct Qdisc *qdisc; /* Inner qdisc, default - bfifo queue */
Patrick McHardyf7f593e2007-03-16 01:20:07 -0700110 struct qdisc_watchdog watchdog; /* Watchdog timer */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111};
112
Eric Dumazete43ac792013-05-21 08:16:46 +0000113
Yang Yingliangcc106e42013-12-10 14:59:27 +0800114/* Time to Length, convert time in ns to length in bytes
115 * to determinate how many bytes can be sent in given time.
116 */
117static u64 psched_ns_t2l(const struct psched_ratecfg *r,
118 u64 time_in_ns)
119{
120 /* The formula is :
121 * len = (time_in_ns * r->rate_bytes_ps) / NSEC_PER_SEC
122 */
123 u64 len = time_in_ns * r->rate_bytes_ps;
124
125 do_div(len, NSEC_PER_SEC);
126
Yang Yingliangd55d2822013-12-12 10:57:22 +0800127 if (unlikely(r->linklayer == TC_LINKLAYER_ATM)) {
128 do_div(len, 53);
129 len = len * 48;
130 }
Yang Yingliangcc106e42013-12-10 14:59:27 +0800131
132 if (len > r->overhead)
133 len -= r->overhead;
134 else
135 len = 0;
136
137 return len;
138}
139
Eric Dumazete43ac792013-05-21 08:16:46 +0000140/* GSO packet is too big, segment it so that tbf can transmit
141 * each segment in time
142 */
Eric Dumazet520ac302016-06-21 23:16:49 -0700143static int tbf_segment(struct sk_buff *skb, struct Qdisc *sch,
144 struct sk_buff **to_free)
Eric Dumazete43ac792013-05-21 08:16:46 +0000145{
146 struct tbf_sched_data *q = qdisc_priv(sch);
147 struct sk_buff *segs, *nskb;
148 netdev_features_t features = netif_skb_features(skb);
WANG Cong2ccccf52016-02-25 14:55:01 -0800149 unsigned int len = 0, prev_len = qdisc_pkt_len(skb);
Eric Dumazete43ac792013-05-21 08:16:46 +0000150 int ret, nb;
151
152 segs = skb_gso_segment(skb, features & ~NETIF_F_GSO_MASK);
153
154 if (IS_ERR_OR_NULL(segs))
Eric Dumazet520ac302016-06-21 23:16:49 -0700155 return qdisc_drop(skb, sch, to_free);
Eric Dumazete43ac792013-05-21 08:16:46 +0000156
157 nb = 0;
158 while (segs) {
159 nskb = segs->next;
David S. Millera8305bf2018-07-29 20:42:53 -0700160 skb_mark_not_on_list(segs);
Eric Dumazet4d0820c2013-11-23 12:59:20 -0800161 qdisc_skb_cb(segs)->pkt_len = segs->len;
WANG Cong2ccccf52016-02-25 14:55:01 -0800162 len += segs->len;
Eric Dumazet520ac302016-06-21 23:16:49 -0700163 ret = qdisc_enqueue(segs, q->qdisc, to_free);
Eric Dumazete43ac792013-05-21 08:16:46 +0000164 if (ret != NET_XMIT_SUCCESS) {
165 if (net_xmit_drop_count(ret))
John Fastabend25331d62014-09-28 11:53:29 -0700166 qdisc_qstats_drop(sch);
Eric Dumazete43ac792013-05-21 08:16:46 +0000167 } else {
168 nb++;
169 }
170 segs = nskb;
171 }
172 sch->q.qlen += nb;
173 if (nb > 1)
WANG Cong2ccccf52016-02-25 14:55:01 -0800174 qdisc_tree_reduce_backlog(sch, 1 - nb, prev_len - len);
Eric Dumazete43ac792013-05-21 08:16:46 +0000175 consume_skb(skb);
176 return nb > 0 ? NET_XMIT_SUCCESS : NET_XMIT_DROP;
177}
178
Eric Dumazet520ac302016-06-21 23:16:49 -0700179static int tbf_enqueue(struct sk_buff *skb, struct Qdisc *sch,
180 struct sk_buff **to_free)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181{
182 struct tbf_sched_data *q = qdisc_priv(sch);
Toke Høiland-Jørgensenf6bab192019-01-09 17:09:42 +0100183 unsigned int len = qdisc_pkt_len(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184 int ret;
185
Eric Dumazete43ac792013-05-21 08:16:46 +0000186 if (qdisc_pkt_len(skb) > q->max_size) {
Daniel Axtensee78bbe2018-03-01 17:13:38 +1100187 if (skb_is_gso(skb) &&
188 skb_gso_validate_mac_len(skb, q->max_size))
Eric Dumazet520ac302016-06-21 23:16:49 -0700189 return tbf_segment(skb, sch, to_free);
190 return qdisc_drop(skb, sch, to_free);
Eric Dumazete43ac792013-05-21 08:16:46 +0000191 }
Eric Dumazet520ac302016-06-21 23:16:49 -0700192 ret = qdisc_enqueue(skb, q->qdisc, to_free);
Ben Greear9871e502010-08-10 01:45:40 -0700193 if (ret != NET_XMIT_SUCCESS) {
Jarek Poplawski378a2f02008-08-04 22:31:03 -0700194 if (net_xmit_drop_count(ret))
John Fastabend25331d62014-09-28 11:53:29 -0700195 qdisc_qstats_drop(sch);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700196 return ret;
197 }
198
Toke Høiland-Jørgensenf6bab192019-01-09 17:09:42 +0100199 sch->qstats.backlog += len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200 sch->q.qlen++;
Ben Greear9871e502010-08-10 01:45:40 -0700201 return NET_XMIT_SUCCESS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202}
203
Hiroaki SHIMODAa135e592014-03-02 17:30:26 +0900204static bool tbf_peak_present(const struct tbf_sched_data *q)
205{
206 return q->peak.rate_bytes_ps;
207}
208
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000209static struct sk_buff *tbf_dequeue(struct Qdisc *sch)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700210{
211 struct tbf_sched_data *q = qdisc_priv(sch);
212 struct sk_buff *skb;
213
Jarek Poplawski03c05f02008-10-31 00:46:19 -0700214 skb = q->qdisc->ops->peek(q->qdisc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215
216 if (skb) {
Jiri Pirkob757c932013-02-12 00:12:05 +0000217 s64 now;
218 s64 toks;
219 s64 ptoks = 0;
Jussi Kivilinna0abf77e2008-07-20 00:08:27 -0700220 unsigned int len = qdisc_pkt_len(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700221
Eric Dumazetd2de8752014-08-22 18:32:09 -0700222 now = ktime_get_ns();
Jiri Pirkob757c932013-02-12 00:12:05 +0000223 toks = min_t(s64, now - q->t_c, q->buffer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224
Hiroaki SHIMODAa135e592014-03-02 17:30:26 +0900225 if (tbf_peak_present(q)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226 ptoks = toks + q->ptokens;
Jiri Pirkob757c932013-02-12 00:12:05 +0000227 if (ptoks > q->mtu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228 ptoks = q->mtu;
Jiri Pirkob757c932013-02-12 00:12:05 +0000229 ptoks -= (s64) psched_l2t_ns(&q->peak, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230 }
231 toks += q->tokens;
Jiri Pirkob757c932013-02-12 00:12:05 +0000232 if (toks > q->buffer)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233 toks = q->buffer;
Jiri Pirkob757c932013-02-12 00:12:05 +0000234 toks -= (s64) psched_l2t_ns(&q->rate, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235
236 if ((toks|ptoks) >= 0) {
Jarek Poplawski77be1552008-10-31 00:47:01 -0700237 skb = qdisc_dequeue_peeked(q->qdisc);
Jarek Poplawski03c05f02008-10-31 00:46:19 -0700238 if (unlikely(!skb))
239 return NULL;
240
Linus Torvalds1da177e2005-04-16 15:20:36 -0700241 q->t_c = now;
242 q->tokens = toks;
243 q->ptokens = ptoks;
WANG Cong8d5958f2016-06-01 16:15:19 -0700244 qdisc_qstats_backlog_dec(sch, skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245 sch->q.qlen--;
Eric Dumazet9190b3b2011-01-20 23:31:33 -0800246 qdisc_bstats_update(sch, skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700247 return skb;
248 }
249
Jiri Pirkob757c932013-02-12 00:12:05 +0000250 qdisc_watchdog_schedule_ns(&q->watchdog,
Eric Dumazet45f50be2016-06-10 16:41:39 -0700251 now + max_t(long, -toks, -ptoks));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252
253 /* Maybe we have a shorter packet in the queue,
254 which can be sent now. It sounds cool,
255 but, however, this is wrong in principle.
256 We MUST NOT reorder packets under these circumstances.
257
258 Really, if we split the flow into independent
259 subflows, it would be a very good solution.
260 This is the main idea of all FQ algorithms
261 (cf. CSZ, HPFQ, HFSC)
262 */
263
John Fastabend25331d62014-09-28 11:53:29 -0700264 qdisc_qstats_overlimit(sch);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700265 }
266 return NULL;
267}
268
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000269static void tbf_reset(struct Qdisc *sch)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700270{
271 struct tbf_sched_data *q = qdisc_priv(sch);
272
273 qdisc_reset(q->qdisc);
WANG Cong8d5958f2016-06-01 16:15:19 -0700274 sch->qstats.backlog = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275 sch->q.qlen = 0;
Eric Dumazetd2de8752014-08-22 18:32:09 -0700276 q->t_c = ktime_get_ns();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277 q->tokens = q->buffer;
278 q->ptokens = q->mtu;
Patrick McHardyf7f593e2007-03-16 01:20:07 -0700279 qdisc_watchdog_cancel(&q->watchdog);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280}
281
Patrick McHardy27a34212008-01-23 20:35:39 -0800282static const struct nla_policy tbf_policy[TCA_TBF_MAX + 1] = {
283 [TCA_TBF_PARMS] = { .len = sizeof(struct tc_tbf_qopt) },
284 [TCA_TBF_RTAB] = { .type = NLA_BINARY, .len = TC_RTAB_SIZE },
285 [TCA_TBF_PTAB] = { .type = NLA_BINARY, .len = TC_RTAB_SIZE },
Yang Yinglianga33c4a22013-11-08 10:23:34 +0800286 [TCA_TBF_RATE64] = { .type = NLA_U64 },
287 [TCA_TBF_PRATE64] = { .type = NLA_U64 },
Yang Yingliang2e04ad42013-12-20 09:24:47 +0800288 [TCA_TBF_BURST] = { .type = NLA_U32 },
289 [TCA_TBF_PBURST] = { .type = NLA_U32 },
Patrick McHardy27a34212008-01-23 20:35:39 -0800290};
291
Alexander Aring20307212017-12-20 12:35:14 -0500292static int tbf_change(struct Qdisc *sch, struct nlattr *opt,
293 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700294{
Patrick McHardycee63722008-01-23 20:33:32 -0800295 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296 struct tbf_sched_data *q = qdisc_priv(sch);
Yang Yinglianga33c4a22013-11-08 10:23:34 +0800297 struct nlattr *tb[TCA_TBF_MAX + 1];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298 struct tc_tbf_qopt *qopt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700299 struct Qdisc *child = NULL;
Yang Yingliangcc106e42013-12-10 14:59:27 +0800300 struct psched_ratecfg rate;
301 struct psched_ratecfg peak;
302 u64 max_size;
303 s64 buffer, mtu;
Yang Yinglianga33c4a22013-11-08 10:23:34 +0800304 u64 rate64 = 0, prate64 = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305
Johannes Berg8cb08172019-04-26 14:07:28 +0200306 err = nla_parse_nested_deprecated(tb, TCA_TBF_MAX, opt, tbf_policy,
307 NULL);
Patrick McHardycee63722008-01-23 20:33:32 -0800308 if (err < 0)
309 return err;
310
311 err = -EINVAL;
Patrick McHardy27a34212008-01-23 20:35:39 -0800312 if (tb[TCA_TBF_PARMS] == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313 goto done;
314
Patrick McHardy1e904742008-01-22 22:11:17 -0800315 qopt = nla_data(tb[TCA_TBF_PARMS]);
Yang Yingliangcc106e42013-12-10 14:59:27 +0800316 if (qopt->rate.linklayer == TC_LINKLAYER_UNAWARE)
317 qdisc_put_rtab(qdisc_get_rtab(&qopt->rate,
Alexander Aringe9bc3fa2017-12-20 12:35:18 -0500318 tb[TCA_TBF_RTAB],
319 NULL));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700320
Yang Yingliangcc106e42013-12-10 14:59:27 +0800321 if (qopt->peakrate.linklayer == TC_LINKLAYER_UNAWARE)
322 qdisc_put_rtab(qdisc_get_rtab(&qopt->peakrate,
Alexander Aringe9bc3fa2017-12-20 12:35:18 -0500323 tb[TCA_TBF_PTAB],
324 NULL));
Eric Dumazet4d0820c2013-11-23 12:59:20 -0800325
Yang Yingliangcc106e42013-12-10 14:59:27 +0800326 buffer = min_t(u64, PSCHED_TICKS2NS(qopt->buffer), ~0U);
327 mtu = min_t(u64, PSCHED_TICKS2NS(qopt->mtu), ~0U);
328
329 if (tb[TCA_TBF_RATE64])
330 rate64 = nla_get_u64(tb[TCA_TBF_RATE64]);
331 psched_ratecfg_precompute(&rate, &qopt->rate, rate64);
332
Yang Yingliang2e04ad42013-12-20 09:24:47 +0800333 if (tb[TCA_TBF_BURST]) {
334 max_size = nla_get_u32(tb[TCA_TBF_BURST]);
335 buffer = psched_l2t_ns(&rate, max_size);
336 } else {
337 max_size = min_t(u64, psched_ns_t2l(&rate, buffer), ~0U);
338 }
Yang Yingliangcc106e42013-12-10 14:59:27 +0800339
340 if (qopt->peakrate.rate) {
341 if (tb[TCA_TBF_PRATE64])
342 prate64 = nla_get_u64(tb[TCA_TBF_PRATE64]);
343 psched_ratecfg_precompute(&peak, &qopt->peakrate, prate64);
344 if (peak.rate_bytes_ps <= rate.rate_bytes_ps) {
345 pr_warn_ratelimited("sch_tbf: peakrate %llu is lower than or equals to rate %llu !\n",
Yang Yingliang2e04ad42013-12-20 09:24:47 +0800346 peak.rate_bytes_ps, rate.rate_bytes_ps);
Yang Yingliangcc106e42013-12-10 14:59:27 +0800347 err = -EINVAL;
348 goto done;
349 }
350
Yang Yingliang2e04ad42013-12-20 09:24:47 +0800351 if (tb[TCA_TBF_PBURST]) {
352 u32 pburst = nla_get_u32(tb[TCA_TBF_PBURST]);
353 max_size = min_t(u32, max_size, pburst);
354 mtu = psched_l2t_ns(&peak, pburst);
355 } else {
356 max_size = min_t(u64, max_size, psched_ns_t2l(&peak, mtu));
357 }
Hiroaki SHIMODAa135e592014-03-02 17:30:26 +0900358 } else {
359 memset(&peak, 0, sizeof(peak));
Yang Yingliangcc106e42013-12-10 14:59:27 +0800360 }
361
362 if (max_size < psched_mtu(qdisc_dev(sch)))
363 pr_warn_ratelimited("sch_tbf: burst %llu is lower than device %s mtu (%u) !\n",
364 max_size, qdisc_dev(sch)->name,
365 psched_mtu(qdisc_dev(sch)));
366
367 if (!max_size) {
368 err = -EINVAL;
369 goto done;
370 }
371
Hiroaki SHIMODA724b9e12014-02-26 21:43:42 +0900372 if (q->qdisc != &noop_qdisc) {
373 err = fifo_set_limit(q->qdisc, qopt->limit);
374 if (err)
375 goto done;
376 } else if (qopt->limit > 0) {
Alexander Aringa38a98822017-12-20 12:35:21 -0500377 child = fifo_create_dflt(sch, &bfifo_qdisc_ops, qopt->limit,
378 extack);
Hiroaki SHIMODA724b9e12014-02-26 21:43:42 +0900379 if (IS_ERR(child)) {
380 err = PTR_ERR(child);
381 goto done;
382 }
Paolo Abeni44a63b132018-05-18 14:51:44 +0200383
384 /* child is fifo, no need to check for noop_qdisc */
385 qdisc_hash_add(child, true);
Hiroaki SHIMODA724b9e12014-02-26 21:43:42 +0900386 }
387
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388 sch_tree_lock(sch);
Patrick McHardy5e50da02006-11-29 17:36:20 -0800389 if (child) {
Paolo Abenie5f0e8f2019-03-28 16:53:13 +0100390 qdisc_tree_flush_backlog(q->qdisc);
Vlad Buslov86bd4462018-09-24 19:22:50 +0300391 qdisc_put(q->qdisc);
Patrick McHardyb94c8af2008-11-20 04:11:36 -0800392 q->qdisc = child;
Patrick McHardy5e50da02006-11-29 17:36:20 -0800393 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394 q->limit = qopt->limit;
Yang Yingliang2e04ad42013-12-20 09:24:47 +0800395 if (tb[TCA_TBF_PBURST])
396 q->mtu = mtu;
397 else
398 q->mtu = PSCHED_TICKS2NS(qopt->mtu);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399 q->max_size = max_size;
Yang Yingliang2e04ad42013-12-20 09:24:47 +0800400 if (tb[TCA_TBF_BURST])
401 q->buffer = buffer;
402 else
403 q->buffer = PSCHED_TICKS2NS(qopt->buffer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404 q->tokens = q->buffer;
405 q->ptokens = q->mtu;
Patrick McHardyb94c8af2008-11-20 04:11:36 -0800406
Yang Yingliangcc106e42013-12-10 14:59:27 +0800407 memcpy(&q->rate, &rate, sizeof(struct psched_ratecfg));
Hiroaki SHIMODAa135e592014-03-02 17:30:26 +0900408 memcpy(&q->peak, &peak, sizeof(struct psched_ratecfg));
Patrick McHardyb94c8af2008-11-20 04:11:36 -0800409
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410 sch_tree_unlock(sch);
411 err = 0;
412done:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413 return err;
414}
415
Alexander Aringe63d7dfd2017-12-20 12:35:13 -0500416static int tbf_init(struct Qdisc *sch, struct nlattr *opt,
417 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418{
419 struct tbf_sched_data *q = qdisc_priv(sch);
420
Nikolay Aleksandrovc2d65112017-08-30 12:49:05 +0300421 qdisc_watchdog_init(&q->watchdog, sch);
422 q->qdisc = &noop_qdisc;
423
Alexander Aringac8ef4a2017-12-20 12:35:11 -0500424 if (!opt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425 return -EINVAL;
426
Eric Dumazetd2de8752014-08-22 18:32:09 -0700427 q->t_c = ktime_get_ns();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428
Alexander Aring20307212017-12-20 12:35:14 -0500429 return tbf_change(sch, opt, extack);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700430}
431
432static void tbf_destroy(struct Qdisc *sch)
433{
434 struct tbf_sched_data *q = qdisc_priv(sch);
435
Patrick McHardyf7f593e2007-03-16 01:20:07 -0700436 qdisc_watchdog_cancel(&q->watchdog);
Vlad Buslov86bd4462018-09-24 19:22:50 +0300437 qdisc_put(q->qdisc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700438}
439
440static int tbf_dump(struct Qdisc *sch, struct sk_buff *skb)
441{
442 struct tbf_sched_data *q = qdisc_priv(sch);
Patrick McHardy4b3550ef2008-01-23 20:34:11 -0800443 struct nlattr *nest;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444 struct tc_tbf_qopt opt;
445
Eric Dumazetb0460e42011-12-28 23:27:44 +0000446 sch->qstats.backlog = q->qdisc->qstats.backlog;
Michal Kubecekae0be8d2019-04-26 11:13:06 +0200447 nest = nla_nest_start_noflag(skb, TCA_OPTIONS);
Patrick McHardy4b3550ef2008-01-23 20:34:11 -0800448 if (nest == NULL)
449 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700450
451 opt.limit = q->limit;
Eric Dumazet01cb71d2013-06-02 13:55:05 +0000452 psched_ratecfg_getrate(&opt.rate, &q->rate);
Hiroaki SHIMODAa135e592014-03-02 17:30:26 +0900453 if (tbf_peak_present(q))
Eric Dumazet01cb71d2013-06-02 13:55:05 +0000454 psched_ratecfg_getrate(&opt.peakrate, &q->peak);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455 else
456 memset(&opt.peakrate, 0, sizeof(opt.peakrate));
Jiri Pirkob757c932013-02-12 00:12:05 +0000457 opt.mtu = PSCHED_NS2TICKS(q->mtu);
458 opt.buffer = PSCHED_NS2TICKS(q->buffer);
David S. Miller1b34ec42012-03-29 05:11:39 -0400459 if (nla_put(skb, TCA_TBF_PARMS, sizeof(opt), &opt))
460 goto nla_put_failure;
Yang Yinglianga33c4a22013-11-08 10:23:34 +0800461 if (q->rate.rate_bytes_ps >= (1ULL << 32) &&
Nicolas Dichtel2a51c1e2016-04-25 10:25:15 +0200462 nla_put_u64_64bit(skb, TCA_TBF_RATE64, q->rate.rate_bytes_ps,
463 TCA_TBF_PAD))
Yang Yinglianga33c4a22013-11-08 10:23:34 +0800464 goto nla_put_failure;
Hiroaki SHIMODAa135e592014-03-02 17:30:26 +0900465 if (tbf_peak_present(q) &&
Yang Yinglianga33c4a22013-11-08 10:23:34 +0800466 q->peak.rate_bytes_ps >= (1ULL << 32) &&
Nicolas Dichtel2a51c1e2016-04-25 10:25:15 +0200467 nla_put_u64_64bit(skb, TCA_TBF_PRATE64, q->peak.rate_bytes_ps,
468 TCA_TBF_PAD))
Yang Yinglianga33c4a22013-11-08 10:23:34 +0800469 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470
Yang Yingliangd59b7d82014-03-12 10:20:32 +0800471 return nla_nest_end(skb, nest);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472
Patrick McHardy1e904742008-01-22 22:11:17 -0800473nla_put_failure:
Patrick McHardy4b3550ef2008-01-23 20:34:11 -0800474 nla_nest_cancel(skb, nest);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700475 return -1;
476}
477
478static int tbf_dump_class(struct Qdisc *sch, unsigned long cl,
479 struct sk_buff *skb, struct tcmsg *tcm)
480{
481 struct tbf_sched_data *q = qdisc_priv(sch);
482
Linus Torvalds1da177e2005-04-16 15:20:36 -0700483 tcm->tcm_handle |= TC_H_MIN(1);
484 tcm->tcm_info = q->qdisc->handle;
485
486 return 0;
487}
488
489static int tbf_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new,
Alexander Aring653d6fd2017-12-20 12:35:17 -0500490 struct Qdisc **old, struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491{
492 struct tbf_sched_data *q = qdisc_priv(sch);
493
494 if (new == NULL)
495 new = &noop_qdisc;
496
WANG Cong86a79962016-02-25 14:55:00 -0800497 *old = qdisc_replace(sch, new, &q->qdisc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498 return 0;
499}
500
501static struct Qdisc *tbf_leaf(struct Qdisc *sch, unsigned long arg)
502{
503 struct tbf_sched_data *q = qdisc_priv(sch);
504 return q->qdisc;
505}
506
WANG Cong143976c2017-08-24 16:51:29 -0700507static unsigned long tbf_find(struct Qdisc *sch, u32 classid)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508{
509 return 1;
510}
511
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512static void tbf_walk(struct Qdisc *sch, struct qdisc_walker *walker)
513{
514 if (!walker->stop) {
515 if (walker->count >= walker->skip)
516 if (walker->fn(sch, 1, walker) < 0) {
517 walker->stop = 1;
518 return;
519 }
520 walker->count++;
521 }
522}
523
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000524static const struct Qdisc_class_ops tbf_class_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525 .graft = tbf_graft,
526 .leaf = tbf_leaf,
WANG Cong143976c2017-08-24 16:51:29 -0700527 .find = tbf_find,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528 .walk = tbf_walk,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700529 .dump = tbf_dump_class,
530};
531
Eric Dumazet20fea082007-11-14 01:44:41 -0800532static struct Qdisc_ops tbf_qdisc_ops __read_mostly = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533 .next = NULL,
534 .cl_ops = &tbf_class_ops,
535 .id = "tbf",
536 .priv_size = sizeof(struct tbf_sched_data),
537 .enqueue = tbf_enqueue,
538 .dequeue = tbf_dequeue,
Jarek Poplawski77be1552008-10-31 00:47:01 -0700539 .peek = qdisc_peek_dequeued,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540 .init = tbf_init,
541 .reset = tbf_reset,
542 .destroy = tbf_destroy,
543 .change = tbf_change,
544 .dump = tbf_dump,
545 .owner = THIS_MODULE,
546};
547
548static int __init tbf_module_init(void)
549{
550 return register_qdisc(&tbf_qdisc_ops);
551}
552
553static void __exit tbf_module_exit(void)
554{
555 unregister_qdisc(&tbf_qdisc_ops);
556}
557module_init(tbf_module_init)
558module_exit(tbf_module_exit)
559MODULE_LICENSE("GPL");