blob: a41d7d4434eedddd7580a3a545ae144ed7ebc5b8 [file] [log] [blame]
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -07001// SPDX-License-Identifier: GPL-2.0
2
3/* net/sched/sch_taprio.c Time Aware Priority Scheduler
4 *
5 * Authors: Vinicius Costa Gomes <vinicius.gomes@intel.com>
6 *
7 */
8
9#include <linux/types.h>
10#include <linux/slab.h>
11#include <linux/kernel.h>
12#include <linux/string.h>
13#include <linux/list.h>
14#include <linux/errno.h>
15#include <linux/skbuff.h>
Jakub Kicinski23bddf62019-04-17 13:51:57 -070016#include <linux/math64.h>
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -070017#include <linux/module.h>
18#include <linux/spinlock.h>
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -070019#include <linux/rcupdate.h>
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -070020#include <net/netlink.h>
21#include <net/pkt_sched.h>
22#include <net/pkt_cls.h>
23#include <net/sch_generic.h>
24
Leandro Dorileo7b9eba72019-04-08 10:12:17 -070025static LIST_HEAD(taprio_list);
26static DEFINE_SPINLOCK(taprio_list_lock);
27
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -070028#define TAPRIO_ALL_GATES_OPEN -1
29
30struct sched_entry {
31 struct list_head list;
32
33 /* The instant that this entry "closes" and the next one
34 * should open, the qdisc will make some effort so that no
35 * packet leaves after this time.
36 */
37 ktime_t close_time;
38 atomic_t budget;
39 int index;
40 u32 gate_mask;
41 u32 interval;
42 u8 command;
43};
44
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -070045struct sched_gate_list {
46 struct rcu_head rcu;
47 struct list_head entries;
48 size_t num_entries;
Vinicius Costa Gomes6ca6a662019-04-29 15:48:32 -070049 ktime_t cycle_close_time;
50 s64 cycle_time;
Vinicius Costa Gomesc25031e2019-04-29 15:48:33 -070051 s64 cycle_time_extension;
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -070052 s64 base_time;
53};
54
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -070055struct taprio_sched {
56 struct Qdisc **qdiscs;
57 struct Qdisc *root;
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -070058 int clockid;
Leandro Dorileo7b9eba72019-04-08 10:12:17 -070059 atomic64_t picos_per_byte; /* Using picoseconds because for 10Gbps+
60 * speeds it's sub-nanoseconds per byte
61 */
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -070062
63 /* Protects the update side of the RCU protected current_entry */
64 spinlock_t current_entry_lock;
65 struct sched_entry __rcu *current_entry;
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -070066 struct sched_gate_list __rcu *oper_sched;
67 struct sched_gate_list __rcu *admin_sched;
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -070068 ktime_t (*get_time)(void);
69 struct hrtimer advance_timer;
Leandro Dorileo7b9eba72019-04-08 10:12:17 -070070 struct list_head taprio_list;
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -070071};
72
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -070073static ktime_t sched_base_time(const struct sched_gate_list *sched)
74{
75 if (!sched)
76 return KTIME_MAX;
77
78 return ns_to_ktime(sched->base_time);
79}
80
81static void taprio_free_sched_cb(struct rcu_head *head)
82{
83 struct sched_gate_list *sched = container_of(head, struct sched_gate_list, rcu);
84 struct sched_entry *entry, *n;
85
86 if (!sched)
87 return;
88
89 list_for_each_entry_safe(entry, n, &sched->entries, list) {
90 list_del(&entry->list);
91 kfree(entry);
92 }
93
94 kfree(sched);
95}
96
97static void switch_schedules(struct taprio_sched *q,
98 struct sched_gate_list **admin,
99 struct sched_gate_list **oper)
100{
101 rcu_assign_pointer(q->oper_sched, *admin);
102 rcu_assign_pointer(q->admin_sched, NULL);
103
104 if (*oper)
105 call_rcu(&(*oper)->rcu, taprio_free_sched_cb);
106
107 *oper = *admin;
108 *admin = NULL;
109}
110
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700111static int taprio_enqueue(struct sk_buff *skb, struct Qdisc *sch,
112 struct sk_buff **to_free)
113{
114 struct taprio_sched *q = qdisc_priv(sch);
115 struct Qdisc *child;
116 int queue;
117
118 queue = skb_get_queue_mapping(skb);
119
120 child = q->qdiscs[queue];
121 if (unlikely(!child))
122 return qdisc_drop(skb, sch, to_free);
123
124 qdisc_qstats_backlog_inc(sch, skb);
125 sch->q.qlen++;
126
127 return qdisc_enqueue(skb, child, to_free);
128}
129
130static struct sk_buff *taprio_peek(struct Qdisc *sch)
131{
132 struct taprio_sched *q = qdisc_priv(sch);
133 struct net_device *dev = qdisc_dev(sch);
134 struct sched_entry *entry;
135 struct sk_buff *skb;
136 u32 gate_mask;
137 int i;
138
139 rcu_read_lock();
140 entry = rcu_dereference(q->current_entry);
Andre Guedes2684d1b2019-04-23 12:44:23 -0700141 gate_mask = entry ? entry->gate_mask : TAPRIO_ALL_GATES_OPEN;
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700142 rcu_read_unlock();
143
144 if (!gate_mask)
145 return NULL;
146
147 for (i = 0; i < dev->num_tx_queues; i++) {
148 struct Qdisc *child = q->qdiscs[i];
149 int prio;
150 u8 tc;
151
152 if (unlikely(!child))
153 continue;
154
155 skb = child->ops->peek(child);
156 if (!skb)
157 continue;
158
159 prio = skb->priority;
160 tc = netdev_get_prio_tc_map(dev, prio);
161
162 if (!(gate_mask & BIT(tc)))
Andre Guedes2684d1b2019-04-23 12:44:23 -0700163 continue;
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700164
165 return skb;
166 }
167
168 return NULL;
169}
170
171static inline int length_to_duration(struct taprio_sched *q, int len)
172{
Jakub Kicinski23bddf62019-04-17 13:51:57 -0700173 return div_u64(len * atomic64_read(&q->picos_per_byte), 1000);
174}
175
176static void taprio_set_budget(struct taprio_sched *q, struct sched_entry *entry)
177{
178 atomic_set(&entry->budget,
179 div64_u64((u64)entry->interval * 1000,
180 atomic64_read(&q->picos_per_byte)));
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700181}
182
183static struct sk_buff *taprio_dequeue(struct Qdisc *sch)
184{
185 struct taprio_sched *q = qdisc_priv(sch);
186 struct net_device *dev = qdisc_dev(sch);
Vinicius Costa Gomes8c79f0e2019-04-29 15:48:30 -0700187 struct sk_buff *skb = NULL;
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700188 struct sched_entry *entry;
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700189 u32 gate_mask;
190 int i;
191
Leandro Dorileo7b9eba72019-04-08 10:12:17 -0700192 if (atomic64_read(&q->picos_per_byte) == -1) {
193 WARN_ONCE(1, "taprio: dequeue() called with unknown picos per byte.");
194 return NULL;
195 }
196
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700197 rcu_read_lock();
198 entry = rcu_dereference(q->current_entry);
199 /* if there's no entry, it means that the schedule didn't
200 * start yet, so force all gates to be open, this is in
201 * accordance to IEEE 802.1Qbv-2015 Section 8.6.9.4.5
202 * "AdminGateSates"
203 */
204 gate_mask = entry ? entry->gate_mask : TAPRIO_ALL_GATES_OPEN;
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700205
206 if (!gate_mask)
Vinicius Costa Gomes8c79f0e2019-04-29 15:48:30 -0700207 goto done;
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700208
209 for (i = 0; i < dev->num_tx_queues; i++) {
210 struct Qdisc *child = q->qdiscs[i];
211 ktime_t guard;
212 int prio;
213 int len;
214 u8 tc;
215
216 if (unlikely(!child))
217 continue;
218
219 skb = child->ops->peek(child);
220 if (!skb)
221 continue;
222
223 prio = skb->priority;
224 tc = netdev_get_prio_tc_map(dev, prio);
225
226 if (!(gate_mask & BIT(tc)))
227 continue;
228
229 len = qdisc_pkt_len(skb);
230 guard = ktime_add_ns(q->get_time(),
231 length_to_duration(q, len));
232
233 /* In the case that there's no gate entry, there's no
234 * guard band ...
235 */
236 if (gate_mask != TAPRIO_ALL_GATES_OPEN &&
237 ktime_after(guard, entry->close_time))
Andre Guedes6e734c82019-04-23 12:44:24 -0700238 continue;
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700239
240 /* ... and no budget. */
241 if (gate_mask != TAPRIO_ALL_GATES_OPEN &&
242 atomic_sub_return(len, &entry->budget) < 0)
Andre Guedes6e734c82019-04-23 12:44:24 -0700243 continue;
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700244
245 skb = child->ops->dequeue(child);
246 if (unlikely(!skb))
Vinicius Costa Gomes8c79f0e2019-04-29 15:48:30 -0700247 goto done;
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700248
249 qdisc_bstats_update(sch, skb);
250 qdisc_qstats_backlog_dec(sch, skb);
251 sch->q.qlen--;
252
Vinicius Costa Gomes8c79f0e2019-04-29 15:48:30 -0700253 goto done;
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700254 }
255
Vinicius Costa Gomes8c79f0e2019-04-29 15:48:30 -0700256done:
257 rcu_read_unlock();
258
259 return skb;
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700260}
261
Vinicius Costa Gomes6ca6a662019-04-29 15:48:32 -0700262static bool should_restart_cycle(const struct sched_gate_list *oper,
263 const struct sched_entry *entry)
264{
265 if (list_is_last(&entry->list, &oper->entries))
266 return true;
267
268 if (ktime_compare(entry->close_time, oper->cycle_close_time) == 0)
269 return true;
270
271 return false;
272}
273
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -0700274static bool should_change_schedules(const struct sched_gate_list *admin,
275 const struct sched_gate_list *oper,
276 ktime_t close_time)
277{
Vinicius Costa Gomesc25031e2019-04-29 15:48:33 -0700278 ktime_t next_base_time, extension_time;
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -0700279
280 if (!admin)
281 return false;
282
283 next_base_time = sched_base_time(admin);
284
285 /* This is the simple case, the close_time would fall after
286 * the next schedule base_time.
287 */
288 if (ktime_compare(next_base_time, close_time) <= 0)
289 return true;
290
Vinicius Costa Gomesc25031e2019-04-29 15:48:33 -0700291 /* This is the cycle_time_extension case, if the close_time
292 * plus the amount that can be extended would fall after the
293 * next schedule base_time, we can extend the current schedule
294 * for that amount.
295 */
296 extension_time = ktime_add_ns(close_time, oper->cycle_time_extension);
297
298 /* FIXME: the IEEE 802.1Q-2018 Specification isn't clear about
299 * how precisely the extension should be made. So after
300 * conformance testing, this logic may change.
301 */
302 if (ktime_compare(next_base_time, extension_time) <= 0)
303 return true;
304
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -0700305 return false;
306}
307
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700308static enum hrtimer_restart advance_sched(struct hrtimer *timer)
309{
310 struct taprio_sched *q = container_of(timer, struct taprio_sched,
311 advance_timer);
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -0700312 struct sched_gate_list *oper, *admin;
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700313 struct sched_entry *entry, *next;
314 struct Qdisc *sch = q->root;
315 ktime_t close_time;
316
317 spin_lock(&q->current_entry_lock);
318 entry = rcu_dereference_protected(q->current_entry,
319 lockdep_is_held(&q->current_entry_lock));
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -0700320 oper = rcu_dereference_protected(q->oper_sched,
321 lockdep_is_held(&q->current_entry_lock));
322 admin = rcu_dereference_protected(q->admin_sched,
323 lockdep_is_held(&q->current_entry_lock));
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700324
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -0700325 if (!oper)
326 switch_schedules(q, &admin, &oper);
327
328 /* This can happen in two cases: 1. this is the very first run
329 * of this function (i.e. we weren't running any schedule
330 * previously); 2. The previous schedule just ended. The first
331 * entry of all schedules are pre-calculated during the
332 * schedule initialization.
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700333 */
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -0700334 if (unlikely(!entry || entry->close_time == oper->base_time)) {
335 next = list_first_entry(&oper->entries, struct sched_entry,
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700336 list);
337 close_time = next->close_time;
338 goto first_run;
339 }
340
Vinicius Costa Gomes6ca6a662019-04-29 15:48:32 -0700341 if (should_restart_cycle(oper, entry)) {
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -0700342 next = list_first_entry(&oper->entries, struct sched_entry,
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700343 list);
Vinicius Costa Gomes6ca6a662019-04-29 15:48:32 -0700344 oper->cycle_close_time = ktime_add_ns(oper->cycle_close_time,
345 oper->cycle_time);
346 } else {
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700347 next = list_next_entry(entry, list);
Vinicius Costa Gomes6ca6a662019-04-29 15:48:32 -0700348 }
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700349
350 close_time = ktime_add_ns(entry->close_time, next->interval);
Vinicius Costa Gomes6ca6a662019-04-29 15:48:32 -0700351 close_time = min_t(ktime_t, close_time, oper->cycle_close_time);
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700352
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -0700353 if (should_change_schedules(admin, oper, close_time)) {
354 /* Set things so the next time this runs, the new
355 * schedule runs.
356 */
357 close_time = sched_base_time(admin);
358 switch_schedules(q, &admin, &oper);
359 }
360
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700361 next->close_time = close_time;
Jakub Kicinski23bddf62019-04-17 13:51:57 -0700362 taprio_set_budget(q, next);
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700363
364first_run:
365 rcu_assign_pointer(q->current_entry, next);
366 spin_unlock(&q->current_entry_lock);
367
368 hrtimer_set_expires(&q->advance_timer, close_time);
369
370 rcu_read_lock();
371 __netif_schedule(sch);
372 rcu_read_unlock();
373
374 return HRTIMER_RESTART;
375}
376
377static const struct nla_policy entry_policy[TCA_TAPRIO_SCHED_ENTRY_MAX + 1] = {
378 [TCA_TAPRIO_SCHED_ENTRY_INDEX] = { .type = NLA_U32 },
379 [TCA_TAPRIO_SCHED_ENTRY_CMD] = { .type = NLA_U8 },
380 [TCA_TAPRIO_SCHED_ENTRY_GATE_MASK] = { .type = NLA_U32 },
381 [TCA_TAPRIO_SCHED_ENTRY_INTERVAL] = { .type = NLA_U32 },
382};
383
384static const struct nla_policy entry_list_policy[TCA_TAPRIO_SCHED_MAX + 1] = {
385 [TCA_TAPRIO_SCHED_ENTRY] = { .type = NLA_NESTED },
386};
387
388static const struct nla_policy taprio_policy[TCA_TAPRIO_ATTR_MAX + 1] = {
389 [TCA_TAPRIO_ATTR_PRIOMAP] = {
390 .len = sizeof(struct tc_mqprio_qopt)
391 },
Vinicius Costa Gomesc25031e2019-04-29 15:48:33 -0700392 [TCA_TAPRIO_ATTR_SCHED_ENTRY_LIST] = { .type = NLA_NESTED },
393 [TCA_TAPRIO_ATTR_SCHED_BASE_TIME] = { .type = NLA_S64 },
394 [TCA_TAPRIO_ATTR_SCHED_SINGLE_ENTRY] = { .type = NLA_NESTED },
395 [TCA_TAPRIO_ATTR_SCHED_CLOCKID] = { .type = NLA_S32 },
396 [TCA_TAPRIO_ATTR_SCHED_CYCLE_TIME] = { .type = NLA_S64 },
397 [TCA_TAPRIO_ATTR_SCHED_CYCLE_TIME_EXTENSION] = { .type = NLA_S64 },
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700398};
399
400static int fill_sched_entry(struct nlattr **tb, struct sched_entry *entry,
401 struct netlink_ext_ack *extack)
402{
403 u32 interval = 0;
404
405 if (tb[TCA_TAPRIO_SCHED_ENTRY_CMD])
406 entry->command = nla_get_u8(
407 tb[TCA_TAPRIO_SCHED_ENTRY_CMD]);
408
409 if (tb[TCA_TAPRIO_SCHED_ENTRY_GATE_MASK])
410 entry->gate_mask = nla_get_u32(
411 tb[TCA_TAPRIO_SCHED_ENTRY_GATE_MASK]);
412
413 if (tb[TCA_TAPRIO_SCHED_ENTRY_INTERVAL])
414 interval = nla_get_u32(
415 tb[TCA_TAPRIO_SCHED_ENTRY_INTERVAL]);
416
417 if (interval == 0) {
418 NL_SET_ERR_MSG(extack, "Invalid interval for schedule entry");
419 return -EINVAL;
420 }
421
422 entry->interval = interval;
423
424 return 0;
425}
426
427static int parse_sched_entry(struct nlattr *n, struct sched_entry *entry,
428 int index, struct netlink_ext_ack *extack)
429{
430 struct nlattr *tb[TCA_TAPRIO_SCHED_ENTRY_MAX + 1] = { };
431 int err;
432
Johannes Berg8cb08172019-04-26 14:07:28 +0200433 err = nla_parse_nested_deprecated(tb, TCA_TAPRIO_SCHED_ENTRY_MAX, n,
434 entry_policy, NULL);
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700435 if (err < 0) {
436 NL_SET_ERR_MSG(extack, "Could not parse nested entry");
437 return -EINVAL;
438 }
439
440 entry->index = index;
441
442 return fill_sched_entry(tb, entry, extack);
443}
444
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700445static int parse_sched_list(struct nlattr *list,
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -0700446 struct sched_gate_list *sched,
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700447 struct netlink_ext_ack *extack)
448{
449 struct nlattr *n;
450 int err, rem;
451 int i = 0;
452
453 if (!list)
454 return -EINVAL;
455
456 nla_for_each_nested(n, list, rem) {
457 struct sched_entry *entry;
458
459 if (nla_type(n) != TCA_TAPRIO_SCHED_ENTRY) {
460 NL_SET_ERR_MSG(extack, "Attribute is not of type 'entry'");
461 continue;
462 }
463
464 entry = kzalloc(sizeof(*entry), GFP_KERNEL);
465 if (!entry) {
466 NL_SET_ERR_MSG(extack, "Not enough memory for entry");
467 return -ENOMEM;
468 }
469
470 err = parse_sched_entry(n, entry, i, extack);
471 if (err < 0) {
472 kfree(entry);
473 return err;
474 }
475
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -0700476 list_add_tail(&entry->list, &sched->entries);
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700477 i++;
478 }
479
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -0700480 sched->num_entries = i;
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700481
482 return i;
483}
484
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -0700485static int parse_taprio_schedule(struct nlattr **tb,
486 struct sched_gate_list *new,
487 struct netlink_ext_ack *extack)
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700488{
489 int err = 0;
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700490
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -0700491 if (tb[TCA_TAPRIO_ATTR_SCHED_SINGLE_ENTRY]) {
492 NL_SET_ERR_MSG(extack, "Adding a single entry is not supported");
493 return -ENOTSUPP;
494 }
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700495
496 if (tb[TCA_TAPRIO_ATTR_SCHED_BASE_TIME])
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -0700497 new->base_time = nla_get_s64(tb[TCA_TAPRIO_ATTR_SCHED_BASE_TIME]);
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700498
Vinicius Costa Gomesc25031e2019-04-29 15:48:33 -0700499 if (tb[TCA_TAPRIO_ATTR_SCHED_CYCLE_TIME_EXTENSION])
500 new->cycle_time_extension = nla_get_s64(tb[TCA_TAPRIO_ATTR_SCHED_CYCLE_TIME_EXTENSION]);
501
Vinicius Costa Gomes6ca6a662019-04-29 15:48:32 -0700502 if (tb[TCA_TAPRIO_ATTR_SCHED_CYCLE_TIME])
503 new->cycle_time = nla_get_s64(tb[TCA_TAPRIO_ATTR_SCHED_CYCLE_TIME]);
504
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700505 if (tb[TCA_TAPRIO_ATTR_SCHED_ENTRY_LIST])
506 err = parse_sched_list(
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -0700507 tb[TCA_TAPRIO_ATTR_SCHED_ENTRY_LIST], new, extack);
508 if (err < 0)
509 return err;
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700510
Vedang Patel037be032019-06-25 15:07:15 -0700511 if (!new->cycle_time) {
512 struct sched_entry *entry;
513 ktime_t cycle = 0;
514
515 list_for_each_entry(entry, &new->entries, list)
516 cycle = ktime_add_ns(cycle, entry->interval);
517 new->cycle_time = cycle;
518 }
519
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -0700520 return 0;
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700521}
522
523static int taprio_parse_mqprio_opt(struct net_device *dev,
524 struct tc_mqprio_qopt *qopt,
525 struct netlink_ext_ack *extack)
526{
527 int i, j;
528
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -0700529 if (!qopt && !dev->num_tc) {
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700530 NL_SET_ERR_MSG(extack, "'mqprio' configuration is necessary");
531 return -EINVAL;
532 }
533
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -0700534 /* If num_tc is already set, it means that the user already
535 * configured the mqprio part
536 */
537 if (dev->num_tc)
538 return 0;
539
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700540 /* Verify num_tc is not out of max range */
541 if (qopt->num_tc > TC_MAX_QUEUE) {
542 NL_SET_ERR_MSG(extack, "Number of traffic classes is outside valid range");
543 return -EINVAL;
544 }
545
546 /* taprio imposes that traffic classes map 1:n to tx queues */
547 if (qopt->num_tc > dev->num_tx_queues) {
548 NL_SET_ERR_MSG(extack, "Number of traffic classes is greater than number of HW queues");
549 return -EINVAL;
550 }
551
552 /* Verify priority mapping uses valid tcs */
553 for (i = 0; i < TC_BITMASK + 1; i++) {
554 if (qopt->prio_tc_map[i] >= qopt->num_tc) {
555 NL_SET_ERR_MSG(extack, "Invalid traffic class in priority to traffic class mapping");
556 return -EINVAL;
557 }
558 }
559
560 for (i = 0; i < qopt->num_tc; i++) {
561 unsigned int last = qopt->offset[i] + qopt->count[i];
562
563 /* Verify the queue count is in tx range being equal to the
564 * real_num_tx_queues indicates the last queue is in use.
565 */
566 if (qopt->offset[i] >= dev->num_tx_queues ||
567 !qopt->count[i] ||
568 last > dev->real_num_tx_queues) {
569 NL_SET_ERR_MSG(extack, "Invalid queue in traffic class to queue mapping");
570 return -EINVAL;
571 }
572
573 /* Verify that the offset and counts do not overlap */
574 for (j = i + 1; j < qopt->num_tc; j++) {
575 if (last > qopt->offset[j]) {
576 NL_SET_ERR_MSG(extack, "Detected overlap in the traffic class to queue mapping");
577 return -EINVAL;
578 }
579 }
580 }
581
582 return 0;
583}
584
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -0700585static int taprio_get_start_time(struct Qdisc *sch,
586 struct sched_gate_list *sched,
587 ktime_t *start)
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700588{
589 struct taprio_sched *q = qdisc_priv(sch);
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700590 ktime_t now, base, cycle;
591 s64 n;
592
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -0700593 base = sched_base_time(sched);
Andre Guedes85990992019-04-23 12:44:21 -0700594 now = q->get_time();
595
596 if (ktime_after(base, now)) {
597 *start = base;
598 return 0;
599 }
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700600
Vedang Patel037be032019-06-25 15:07:15 -0700601 cycle = sched->cycle_time;
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700602
Andre Guedes85990992019-04-23 12:44:21 -0700603 /* The qdisc is expected to have at least one sched_entry. Moreover,
604 * any entry must have 'interval' > 0. Thus if the cycle time is zero,
605 * something went really wrong. In that case, we should warn about this
606 * inconsistent state and return error.
607 */
608 if (WARN_ON(!cycle))
609 return -EFAULT;
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700610
611 /* Schedule the start time for the beginning of the next
612 * cycle.
613 */
614 n = div64_s64(ktime_sub_ns(now, base), cycle);
Andre Guedes85990992019-04-23 12:44:21 -0700615 *start = ktime_add_ns(base, (n + 1) * cycle);
616 return 0;
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700617}
618
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -0700619static void setup_first_close_time(struct taprio_sched *q,
620 struct sched_gate_list *sched, ktime_t base)
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700621{
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700622 struct sched_entry *first;
Vinicius Costa Gomes6ca6a662019-04-29 15:48:32 -0700623 ktime_t cycle;
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700624
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -0700625 first = list_first_entry(&sched->entries,
626 struct sched_entry, list);
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700627
Vedang Patel037be032019-06-25 15:07:15 -0700628 cycle = sched->cycle_time;
Vinicius Costa Gomes6ca6a662019-04-29 15:48:32 -0700629
630 /* FIXME: find a better place to do this */
631 sched->cycle_close_time = ktime_add_ns(base, cycle);
632
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -0700633 first->close_time = ktime_add_ns(base, first->interval);
Jakub Kicinski23bddf62019-04-17 13:51:57 -0700634 taprio_set_budget(q, first);
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700635 rcu_assign_pointer(q->current_entry, NULL);
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -0700636}
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700637
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -0700638static void taprio_start_sched(struct Qdisc *sch,
639 ktime_t start, struct sched_gate_list *new)
640{
641 struct taprio_sched *q = qdisc_priv(sch);
642 ktime_t expires;
643
644 expires = hrtimer_get_expires(&q->advance_timer);
645 if (expires == 0)
646 expires = KTIME_MAX;
647
648 /* If the new schedule starts before the next expiration, we
649 * reprogram it to the earliest one, so we change the admin
650 * schedule to the operational one at the right time.
651 */
652 start = min_t(ktime_t, start, expires);
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700653
654 hrtimer_start(&q->advance_timer, start, HRTIMER_MODE_ABS);
655}
656
Leandro Dorileo7b9eba72019-04-08 10:12:17 -0700657static void taprio_set_picos_per_byte(struct net_device *dev,
658 struct taprio_sched *q)
659{
660 struct ethtool_link_ksettings ecmd;
661 int picos_per_byte = -1;
662
663 if (!__ethtool_get_link_ksettings(dev, &ecmd) &&
664 ecmd.base.speed != SPEED_UNKNOWN)
665 picos_per_byte = div64_s64(NSEC_PER_SEC * 1000LL * 8,
666 ecmd.base.speed * 1000 * 1000);
667
668 atomic64_set(&q->picos_per_byte, picos_per_byte);
669 netdev_dbg(dev, "taprio: set %s's picos_per_byte to: %lld, linkspeed: %d\n",
670 dev->name, (long long)atomic64_read(&q->picos_per_byte),
671 ecmd.base.speed);
672}
673
674static int taprio_dev_notifier(struct notifier_block *nb, unsigned long event,
675 void *ptr)
676{
677 struct net_device *dev = netdev_notifier_info_to_dev(ptr);
678 struct net_device *qdev;
679 struct taprio_sched *q;
680 bool found = false;
681
682 ASSERT_RTNL();
683
684 if (event != NETDEV_UP && event != NETDEV_CHANGE)
685 return NOTIFY_DONE;
686
687 spin_lock(&taprio_list_lock);
688 list_for_each_entry(q, &taprio_list, taprio_list) {
689 qdev = qdisc_dev(q->root);
690 if (qdev == dev) {
691 found = true;
692 break;
693 }
694 }
695 spin_unlock(&taprio_list_lock);
696
697 if (found)
698 taprio_set_picos_per_byte(dev, q);
699
700 return NOTIFY_DONE;
701}
702
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700703static int taprio_change(struct Qdisc *sch, struct nlattr *opt,
704 struct netlink_ext_ack *extack)
705{
706 struct nlattr *tb[TCA_TAPRIO_ATTR_MAX + 1] = { };
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -0700707 struct sched_gate_list *oper, *admin, *new_admin;
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700708 struct taprio_sched *q = qdisc_priv(sch);
709 struct net_device *dev = qdisc_dev(sch);
710 struct tc_mqprio_qopt *mqprio = NULL;
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -0700711 int i, err, clockid;
712 unsigned long flags;
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700713 ktime_t start;
714
Johannes Berg8cb08172019-04-26 14:07:28 +0200715 err = nla_parse_nested_deprecated(tb, TCA_TAPRIO_ATTR_MAX, opt,
716 taprio_policy, extack);
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700717 if (err < 0)
718 return err;
719
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700720 if (tb[TCA_TAPRIO_ATTR_PRIOMAP])
721 mqprio = nla_data(tb[TCA_TAPRIO_ATTR_PRIOMAP]);
722
723 err = taprio_parse_mqprio_opt(dev, mqprio, extack);
724 if (err < 0)
725 return err;
726
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -0700727 new_admin = kzalloc(sizeof(*new_admin), GFP_KERNEL);
728 if (!new_admin) {
729 NL_SET_ERR_MSG(extack, "Not enough memory for a new schedule");
730 return -ENOMEM;
731 }
732 INIT_LIST_HEAD(&new_admin->entries);
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700733
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -0700734 rcu_read_lock();
735 oper = rcu_dereference(q->oper_sched);
736 admin = rcu_dereference(q->admin_sched);
737 rcu_read_unlock();
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700738
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -0700739 if (mqprio && (oper || admin)) {
740 NL_SET_ERR_MSG(extack, "Changing the traffic mapping of a running schedule is not supported");
741 err = -ENOTSUPP;
742 goto free_sched;
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700743 }
744
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -0700745 err = parse_taprio_schedule(tb, new_admin, extack);
746 if (err < 0)
747 goto free_sched;
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700748
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -0700749 if (new_admin->num_entries == 0) {
750 NL_SET_ERR_MSG(extack, "There should be at least one entry in the schedule");
751 err = -EINVAL;
752 goto free_sched;
753 }
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700754
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -0700755 if (tb[TCA_TAPRIO_ATTR_SCHED_CLOCKID]) {
756 clockid = nla_get_s32(tb[TCA_TAPRIO_ATTR_SCHED_CLOCKID]);
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700757
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -0700758 /* We only support static clockids and we don't allow
759 * for it to be modified after the first init.
760 */
761 if (clockid < 0 ||
762 (q->clockid != -1 && q->clockid != clockid)) {
763 NL_SET_ERR_MSG(extack, "Changing the 'clockid' of a running schedule is not supported");
764 err = -ENOTSUPP;
765 goto free_sched;
766 }
767
768 q->clockid = clockid;
769 }
770
771 if (q->clockid == -1 && !tb[TCA_TAPRIO_ATTR_SCHED_CLOCKID]) {
772 NL_SET_ERR_MSG(extack, "Specifying a 'clockid' is mandatory");
773 err = -EINVAL;
774 goto free_sched;
775 }
776
777 taprio_set_picos_per_byte(dev, q);
778
779 /* Protects against enqueue()/dequeue() */
780 spin_lock_bh(qdisc_lock(sch));
781
782 if (!hrtimer_active(&q->advance_timer)) {
783 hrtimer_init(&q->advance_timer, q->clockid, HRTIMER_MODE_ABS);
784 q->advance_timer.function = advance_sched;
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700785 }
786
787 if (mqprio) {
788 netdev_set_num_tc(dev, mqprio->num_tc);
789 for (i = 0; i < mqprio->num_tc; i++)
790 netdev_set_tc_queue(dev, i,
791 mqprio->count[i],
792 mqprio->offset[i]);
793
794 /* Always use supplied priority mappings */
795 for (i = 0; i < TC_BITMASK + 1; i++)
796 netdev_set_prio_tc_map(dev, i,
797 mqprio->prio_tc_map[i]);
798 }
799
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -0700800 switch (q->clockid) {
801 case CLOCK_REALTIME:
802 q->get_time = ktime_get_real;
803 break;
804 case CLOCK_MONOTONIC:
805 q->get_time = ktime_get;
806 break;
807 case CLOCK_BOOTTIME:
808 q->get_time = ktime_get_boottime;
809 break;
810 case CLOCK_TAI:
811 q->get_time = ktime_get_clocktai;
812 break;
813 default:
814 NL_SET_ERR_MSG(extack, "Invalid 'clockid'");
815 err = -EINVAL;
816 goto unlock;
Andre Guedes85990992019-04-23 12:44:21 -0700817 }
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700818
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -0700819 err = taprio_get_start_time(sch, new_admin, &start);
820 if (err < 0) {
821 NL_SET_ERR_MSG(extack, "Internal error: failed get start time");
822 goto unlock;
823 }
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700824
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -0700825 setup_first_close_time(q, new_admin, start);
826
827 /* Protects against advance_sched() */
828 spin_lock_irqsave(&q->current_entry_lock, flags);
829
830 taprio_start_sched(sch, start, new_admin);
831
832 rcu_assign_pointer(q->admin_sched, new_admin);
833 if (admin)
834 call_rcu(&admin->rcu, taprio_free_sched_cb);
835 new_admin = NULL;
836
837 spin_unlock_irqrestore(&q->current_entry_lock, flags);
838
839 err = 0;
840
841unlock:
842 spin_unlock_bh(qdisc_lock(sch));
843
844free_sched:
845 kfree(new_admin);
846
847 return err;
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700848}
849
850static void taprio_destroy(struct Qdisc *sch)
851{
852 struct taprio_sched *q = qdisc_priv(sch);
853 struct net_device *dev = qdisc_dev(sch);
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700854 unsigned int i;
855
Leandro Dorileo7b9eba72019-04-08 10:12:17 -0700856 spin_lock(&taprio_list_lock);
857 list_del(&q->taprio_list);
858 spin_unlock(&taprio_list_lock);
859
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700860 hrtimer_cancel(&q->advance_timer);
861
862 if (q->qdiscs) {
863 for (i = 0; i < dev->num_tx_queues && q->qdiscs[i]; i++)
864 qdisc_put(q->qdiscs[i]);
865
866 kfree(q->qdiscs);
867 }
868 q->qdiscs = NULL;
869
870 netdev_set_num_tc(dev, 0);
871
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -0700872 if (q->oper_sched)
873 call_rcu(&q->oper_sched->rcu, taprio_free_sched_cb);
874
875 if (q->admin_sched)
876 call_rcu(&q->admin_sched->rcu, taprio_free_sched_cb);
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700877}
878
879static int taprio_init(struct Qdisc *sch, struct nlattr *opt,
880 struct netlink_ext_ack *extack)
881{
882 struct taprio_sched *q = qdisc_priv(sch);
883 struct net_device *dev = qdisc_dev(sch);
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -0700884 int i;
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700885
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700886 spin_lock_init(&q->current_entry_lock);
887
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700888 hrtimer_init(&q->advance_timer, CLOCK_TAI, HRTIMER_MODE_ABS);
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -0700889 q->advance_timer.function = advance_sched;
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700890
891 q->root = sch;
892
893 /* We only support static clockids. Use an invalid value as default
894 * and get the valid one on taprio_change().
895 */
896 q->clockid = -1;
897
898 if (sch->parent != TC_H_ROOT)
899 return -EOPNOTSUPP;
900
901 if (!netif_is_multiqueue(dev))
902 return -EOPNOTSUPP;
903
904 /* pre-allocate qdisc, attachment can't fail */
905 q->qdiscs = kcalloc(dev->num_tx_queues,
906 sizeof(q->qdiscs[0]),
907 GFP_KERNEL);
908
909 if (!q->qdiscs)
910 return -ENOMEM;
911
912 if (!opt)
913 return -EINVAL;
914
Leandro Dorileo7b9eba72019-04-08 10:12:17 -0700915 spin_lock(&taprio_list_lock);
916 list_add(&q->taprio_list, &taprio_list);
917 spin_unlock(&taprio_list_lock);
918
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -0700919 for (i = 0; i < dev->num_tx_queues; i++) {
920 struct netdev_queue *dev_queue;
921 struct Qdisc *qdisc;
922
923 dev_queue = netdev_get_tx_queue(dev, i);
924 qdisc = qdisc_create_dflt(dev_queue,
925 &pfifo_qdisc_ops,
926 TC_H_MAKE(TC_H_MAJ(sch->handle),
927 TC_H_MIN(i + 1)),
928 extack);
929 if (!qdisc)
930 return -ENOMEM;
931
932 if (i < dev->real_num_tx_queues)
933 qdisc_hash_add(qdisc, false);
934
935 q->qdiscs[i] = qdisc;
936 }
937
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700938 return taprio_change(sch, opt, extack);
939}
940
941static struct netdev_queue *taprio_queue_get(struct Qdisc *sch,
942 unsigned long cl)
943{
944 struct net_device *dev = qdisc_dev(sch);
945 unsigned long ntx = cl - 1;
946
947 if (ntx >= dev->num_tx_queues)
948 return NULL;
949
950 return netdev_get_tx_queue(dev, ntx);
951}
952
953static int taprio_graft(struct Qdisc *sch, unsigned long cl,
954 struct Qdisc *new, struct Qdisc **old,
955 struct netlink_ext_ack *extack)
956{
957 struct taprio_sched *q = qdisc_priv(sch);
958 struct net_device *dev = qdisc_dev(sch);
959 struct netdev_queue *dev_queue = taprio_queue_get(sch, cl);
960
961 if (!dev_queue)
962 return -EINVAL;
963
964 if (dev->flags & IFF_UP)
965 dev_deactivate(dev);
966
967 *old = q->qdiscs[cl - 1];
968 q->qdiscs[cl - 1] = new;
969
970 if (new)
971 new->flags |= TCQ_F_ONETXQUEUE | TCQ_F_NOPARENT;
972
973 if (dev->flags & IFF_UP)
974 dev_activate(dev);
975
976 return 0;
977}
978
979static int dump_entry(struct sk_buff *msg,
980 const struct sched_entry *entry)
981{
982 struct nlattr *item;
983
Michal Kubecekae0be8d2019-04-26 11:13:06 +0200984 item = nla_nest_start_noflag(msg, TCA_TAPRIO_SCHED_ENTRY);
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -0700985 if (!item)
986 return -ENOSPC;
987
988 if (nla_put_u32(msg, TCA_TAPRIO_SCHED_ENTRY_INDEX, entry->index))
989 goto nla_put_failure;
990
991 if (nla_put_u8(msg, TCA_TAPRIO_SCHED_ENTRY_CMD, entry->command))
992 goto nla_put_failure;
993
994 if (nla_put_u32(msg, TCA_TAPRIO_SCHED_ENTRY_GATE_MASK,
995 entry->gate_mask))
996 goto nla_put_failure;
997
998 if (nla_put_u32(msg, TCA_TAPRIO_SCHED_ENTRY_INTERVAL,
999 entry->interval))
1000 goto nla_put_failure;
1001
1002 return nla_nest_end(msg, item);
1003
1004nla_put_failure:
1005 nla_nest_cancel(msg, item);
1006 return -1;
1007}
1008
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -07001009static int dump_schedule(struct sk_buff *msg,
1010 const struct sched_gate_list *root)
1011{
1012 struct nlattr *entry_list;
1013 struct sched_entry *entry;
1014
1015 if (nla_put_s64(msg, TCA_TAPRIO_ATTR_SCHED_BASE_TIME,
1016 root->base_time, TCA_TAPRIO_PAD))
1017 return -1;
1018
Vinicius Costa Gomes6ca6a662019-04-29 15:48:32 -07001019 if (nla_put_s64(msg, TCA_TAPRIO_ATTR_SCHED_CYCLE_TIME,
1020 root->cycle_time, TCA_TAPRIO_PAD))
1021 return -1;
1022
Vinicius Costa Gomesc25031e2019-04-29 15:48:33 -07001023 if (nla_put_s64(msg, TCA_TAPRIO_ATTR_SCHED_CYCLE_TIME_EXTENSION,
1024 root->cycle_time_extension, TCA_TAPRIO_PAD))
1025 return -1;
1026
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -07001027 entry_list = nla_nest_start_noflag(msg,
1028 TCA_TAPRIO_ATTR_SCHED_ENTRY_LIST);
1029 if (!entry_list)
1030 goto error_nest;
1031
1032 list_for_each_entry(entry, &root->entries, list) {
1033 if (dump_entry(msg, entry) < 0)
1034 goto error_nest;
1035 }
1036
1037 nla_nest_end(msg, entry_list);
1038 return 0;
1039
1040error_nest:
1041 nla_nest_cancel(msg, entry_list);
1042 return -1;
1043}
1044
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -07001045static int taprio_dump(struct Qdisc *sch, struct sk_buff *skb)
1046{
1047 struct taprio_sched *q = qdisc_priv(sch);
1048 struct net_device *dev = qdisc_dev(sch);
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -07001049 struct sched_gate_list *oper, *admin;
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -07001050 struct tc_mqprio_qopt opt = { 0 };
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -07001051 struct nlattr *nest, *sched_nest;
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -07001052 unsigned int i;
1053
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -07001054 rcu_read_lock();
1055 oper = rcu_dereference(q->oper_sched);
1056 admin = rcu_dereference(q->admin_sched);
1057
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -07001058 opt.num_tc = netdev_get_num_tc(dev);
1059 memcpy(opt.prio_tc_map, dev->prio_tc_map, sizeof(opt.prio_tc_map));
1060
1061 for (i = 0; i < netdev_get_num_tc(dev); i++) {
1062 opt.count[i] = dev->tc_to_txq[i].count;
1063 opt.offset[i] = dev->tc_to_txq[i].offset;
1064 }
1065
Michal Kubecekae0be8d2019-04-26 11:13:06 +02001066 nest = nla_nest_start_noflag(skb, TCA_OPTIONS);
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -07001067 if (!nest)
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -07001068 goto start_error;
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -07001069
1070 if (nla_put(skb, TCA_TAPRIO_ATTR_PRIOMAP, sizeof(opt), &opt))
1071 goto options_error;
1072
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -07001073 if (nla_put_s32(skb, TCA_TAPRIO_ATTR_SCHED_CLOCKID, q->clockid))
1074 goto options_error;
1075
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -07001076 if (oper && dump_schedule(skb, oper))
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -07001077 goto options_error;
1078
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -07001079 if (!admin)
1080 goto done;
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -07001081
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -07001082 sched_nest = nla_nest_start_noflag(skb, TCA_TAPRIO_ATTR_ADMIN_SCHED);
Colin Ian Kinge4acf422019-05-05 22:50:19 +01001083 if (!sched_nest)
1084 goto options_error;
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -07001085
1086 if (dump_schedule(skb, admin))
1087 goto admin_error;
1088
1089 nla_nest_end(skb, sched_nest);
1090
1091done:
1092 rcu_read_unlock();
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -07001093
1094 return nla_nest_end(skb, nest);
1095
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -07001096admin_error:
1097 nla_nest_cancel(skb, sched_nest);
1098
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -07001099options_error:
1100 nla_nest_cancel(skb, nest);
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -07001101
1102start_error:
1103 rcu_read_unlock();
1104 return -ENOSPC;
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -07001105}
1106
1107static struct Qdisc *taprio_leaf(struct Qdisc *sch, unsigned long cl)
1108{
1109 struct netdev_queue *dev_queue = taprio_queue_get(sch, cl);
1110
1111 if (!dev_queue)
1112 return NULL;
1113
1114 return dev_queue->qdisc_sleeping;
1115}
1116
1117static unsigned long taprio_find(struct Qdisc *sch, u32 classid)
1118{
1119 unsigned int ntx = TC_H_MIN(classid);
1120
1121 if (!taprio_queue_get(sch, ntx))
1122 return 0;
1123 return ntx;
1124}
1125
1126static int taprio_dump_class(struct Qdisc *sch, unsigned long cl,
1127 struct sk_buff *skb, struct tcmsg *tcm)
1128{
1129 struct netdev_queue *dev_queue = taprio_queue_get(sch, cl);
1130
1131 tcm->tcm_parent = TC_H_ROOT;
1132 tcm->tcm_handle |= TC_H_MIN(cl);
1133 tcm->tcm_info = dev_queue->qdisc_sleeping->handle;
1134
1135 return 0;
1136}
1137
1138static int taprio_dump_class_stats(struct Qdisc *sch, unsigned long cl,
1139 struct gnet_dump *d)
1140 __releases(d->lock)
1141 __acquires(d->lock)
1142{
1143 struct netdev_queue *dev_queue = taprio_queue_get(sch, cl);
1144
1145 sch = dev_queue->qdisc_sleeping;
1146 if (gnet_stats_copy_basic(&sch->running, d, NULL, &sch->bstats) < 0 ||
Paolo Abeni5dd431b2019-03-28 16:53:12 +01001147 qdisc_qstats_copy(d, sch) < 0)
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -07001148 return -1;
1149 return 0;
1150}
1151
1152static void taprio_walk(struct Qdisc *sch, struct qdisc_walker *arg)
1153{
1154 struct net_device *dev = qdisc_dev(sch);
1155 unsigned long ntx;
1156
1157 if (arg->stop)
1158 return;
1159
1160 arg->count = arg->skip;
1161 for (ntx = arg->skip; ntx < dev->num_tx_queues; ntx++) {
1162 if (arg->fn(sch, ntx + 1, arg) < 0) {
1163 arg->stop = 1;
1164 break;
1165 }
1166 arg->count++;
1167 }
1168}
1169
1170static struct netdev_queue *taprio_select_queue(struct Qdisc *sch,
1171 struct tcmsg *tcm)
1172{
1173 return taprio_queue_get(sch, TC_H_MIN(tcm->tcm_parent));
1174}
1175
1176static const struct Qdisc_class_ops taprio_class_ops = {
1177 .graft = taprio_graft,
1178 .leaf = taprio_leaf,
1179 .find = taprio_find,
1180 .walk = taprio_walk,
1181 .dump = taprio_dump_class,
1182 .dump_stats = taprio_dump_class_stats,
1183 .select_queue = taprio_select_queue,
1184};
1185
1186static struct Qdisc_ops taprio_qdisc_ops __read_mostly = {
1187 .cl_ops = &taprio_class_ops,
1188 .id = "taprio",
1189 .priv_size = sizeof(struct taprio_sched),
1190 .init = taprio_init,
Vinicius Costa Gomesa3d43c02019-04-29 15:48:31 -07001191 .change = taprio_change,
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -07001192 .destroy = taprio_destroy,
1193 .peek = taprio_peek,
1194 .dequeue = taprio_dequeue,
1195 .enqueue = taprio_enqueue,
1196 .dump = taprio_dump,
1197 .owner = THIS_MODULE,
1198};
1199
Leandro Dorileo7b9eba72019-04-08 10:12:17 -07001200static struct notifier_block taprio_device_notifier = {
1201 .notifier_call = taprio_dev_notifier,
1202};
1203
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -07001204static int __init taprio_module_init(void)
1205{
Leandro Dorileo7b9eba72019-04-08 10:12:17 -07001206 int err = register_netdevice_notifier(&taprio_device_notifier);
1207
1208 if (err)
1209 return err;
1210
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -07001211 return register_qdisc(&taprio_qdisc_ops);
1212}
1213
1214static void __exit taprio_module_exit(void)
1215{
1216 unregister_qdisc(&taprio_qdisc_ops);
Leandro Dorileo7b9eba72019-04-08 10:12:17 -07001217 unregister_netdevice_notifier(&taprio_device_notifier);
Vinicius Costa Gomes5a781cc2018-09-28 17:59:43 -07001218}
1219
1220module_init(taprio_module_init);
1221module_exit(taprio_module_exit);
1222MODULE_LICENSE("GPL");