Skip to content

Commit 7b9eba7

Browse files
Leandro Dorileodavem330
authored andcommitted
net/sched: taprio: fix picos_per_byte miscalculation
The Time Aware Priority Scheduler is heavily dependent to link speed, it relies on it to calculate transmission bytes per cycle, we can't properly calculate the so called budget if the device has failed to report the link speed. In that case we can't dequeue packets assuming a wrong budget. This patch makes sure we fail to dequeue case: 1) __ethtool_get_link_ksettings() reports error or 2) the ethernet driver failed to set the ksettings' speed value (setting link speed to SPEED_UNKNOWN). Additionally we re calculate the budget whenever the link speed is changed. Fixes: 5a781cc ("tc: Add support for configuring the taprio scheduler") Signed-off-by: Leandro Dorileo <[email protected]> Reviewed-by: Vedang Patel <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 93e2125 commit 7b9eba7

File tree

1 file changed

+81
-16
lines changed

1 file changed

+81
-16
lines changed

net/sched/sch_taprio.c

Lines changed: 81 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@
2020
#include <net/pkt_cls.h>
2121
#include <net/sch_generic.h>
2222

23+
static LIST_HEAD(taprio_list);
24+
static DEFINE_SPINLOCK(taprio_list_lock);
25+
2326
#define TAPRIO_ALL_GATES_OPEN -1
2427

2528
struct sched_entry {
@@ -42,9 +45,9 @@ struct taprio_sched {
4245
struct Qdisc *root;
4346
s64 base_time;
4447
int clockid;
45-
int picos_per_byte; /* Using picoseconds because for 10Gbps+
46-
* speeds it's sub-nanoseconds per byte
47-
*/
48+
atomic64_t picos_per_byte; /* Using picoseconds because for 10Gbps+
49+
* speeds it's sub-nanoseconds per byte
50+
*/
4851
size_t num_entries;
4952

5053
/* Protects the update side of the RCU protected current_entry */
@@ -53,6 +56,7 @@ struct taprio_sched {
5356
struct list_head entries;
5457
ktime_t (*get_time)(void);
5558
struct hrtimer advance_timer;
59+
struct list_head taprio_list;
5660
};
5761

5862
static int taprio_enqueue(struct sk_buff *skb, struct Qdisc *sch,
@@ -117,7 +121,7 @@ static struct sk_buff *taprio_peek(struct Qdisc *sch)
117121

118122
static inline int length_to_duration(struct taprio_sched *q, int len)
119123
{
120-
return (len * q->picos_per_byte) / 1000;
124+
return (len * atomic64_read(&q->picos_per_byte)) / 1000;
121125
}
122126

123127
static struct sk_buff *taprio_dequeue(struct Qdisc *sch)
@@ -129,6 +133,11 @@ static struct sk_buff *taprio_dequeue(struct Qdisc *sch)
129133
u32 gate_mask;
130134
int i;
131135

136+
if (atomic64_read(&q->picos_per_byte) == -1) {
137+
WARN_ONCE(1, "taprio: dequeue() called with unknown picos per byte.");
138+
return NULL;
139+
}
140+
132141
rcu_read_lock();
133142
entry = rcu_dereference(q->current_entry);
134143
/* if there's no entry, it means that the schedule didn't
@@ -233,7 +242,7 @@ static enum hrtimer_restart advance_sched(struct hrtimer *timer)
233242

234243
next->close_time = close_time;
235244
atomic_set(&next->budget,
236-
(next->interval * 1000) / q->picos_per_byte);
245+
(next->interval * 1000) / atomic64_read(&q->picos_per_byte));
237246

238247
first_run:
239248
rcu_assign_pointer(q->current_entry, next);
@@ -567,24 +576,69 @@ static void taprio_start_sched(struct Qdisc *sch, ktime_t start)
567576

568577
first->close_time = ktime_add_ns(start, first->interval);
569578
atomic_set(&first->budget,
570-
(first->interval * 1000) / q->picos_per_byte);
579+
(first->interval * 1000) /
580+
atomic64_read(&q->picos_per_byte));
571581
rcu_assign_pointer(q->current_entry, NULL);
572582

573583
spin_unlock_irqrestore(&q->current_entry_lock, flags);
574584

575585
hrtimer_start(&q->advance_timer, start, HRTIMER_MODE_ABS);
576586
}
577587

588+
static void taprio_set_picos_per_byte(struct net_device *dev,
589+
struct taprio_sched *q)
590+
{
591+
struct ethtool_link_ksettings ecmd;
592+
int picos_per_byte = -1;
593+
594+
if (!__ethtool_get_link_ksettings(dev, &ecmd) &&
595+
ecmd.base.speed != SPEED_UNKNOWN)
596+
picos_per_byte = div64_s64(NSEC_PER_SEC * 1000LL * 8,
597+
ecmd.base.speed * 1000 * 1000);
598+
599+
atomic64_set(&q->picos_per_byte, picos_per_byte);
600+
netdev_dbg(dev, "taprio: set %s's picos_per_byte to: %lld, linkspeed: %d\n",
601+
dev->name, (long long)atomic64_read(&q->picos_per_byte),
602+
ecmd.base.speed);
603+
}
604+
605+
static int taprio_dev_notifier(struct notifier_block *nb, unsigned long event,
606+
void *ptr)
607+
{
608+
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
609+
struct net_device *qdev;
610+
struct taprio_sched *q;
611+
bool found = false;
612+
613+
ASSERT_RTNL();
614+
615+
if (event != NETDEV_UP && event != NETDEV_CHANGE)
616+
return NOTIFY_DONE;
617+
618+
spin_lock(&taprio_list_lock);
619+
list_for_each_entry(q, &taprio_list, taprio_list) {
620+
qdev = qdisc_dev(q->root);
621+
if (qdev == dev) {
622+
found = true;
623+
break;
624+
}
625+
}
626+
spin_unlock(&taprio_list_lock);
627+
628+
if (found)
629+
taprio_set_picos_per_byte(dev, q);
630+
631+
return NOTIFY_DONE;
632+
}
633+
578634
static int taprio_change(struct Qdisc *sch, struct nlattr *opt,
579635
struct netlink_ext_ack *extack)
580636
{
581637
struct nlattr *tb[TCA_TAPRIO_ATTR_MAX + 1] = { };
582638
struct taprio_sched *q = qdisc_priv(sch);
583639
struct net_device *dev = qdisc_dev(sch);
584640
struct tc_mqprio_qopt *mqprio = NULL;
585-
struct ethtool_link_ksettings ecmd;
586641
int i, err, size;
587-
s64 link_speed;
588642
ktime_t start;
589643

590644
err = nla_parse_nested(tb, TCA_TAPRIO_ATTR_MAX, opt,
@@ -657,14 +711,7 @@ static int taprio_change(struct Qdisc *sch, struct nlattr *opt,
657711
mqprio->prio_tc_map[i]);
658712
}
659713

660-
if (!__ethtool_get_link_ksettings(dev, &ecmd))
661-
link_speed = ecmd.base.speed;
662-
else
663-
link_speed = SPEED_1000;
664-
665-
q->picos_per_byte = div64_s64(NSEC_PER_SEC * 1000LL * 8,
666-
link_speed * 1000 * 1000);
667-
714+
taprio_set_picos_per_byte(dev, q);
668715
start = taprio_get_start_time(sch);
669716
if (!start)
670717
return 0;
@@ -681,6 +728,10 @@ static void taprio_destroy(struct Qdisc *sch)
681728
struct sched_entry *entry, *n;
682729
unsigned int i;
683730

731+
spin_lock(&taprio_list_lock);
732+
list_del(&q->taprio_list);
733+
spin_unlock(&taprio_list_lock);
734+
684735
hrtimer_cancel(&q->advance_timer);
685736

686737
if (q->qdiscs) {
@@ -735,6 +786,10 @@ static int taprio_init(struct Qdisc *sch, struct nlattr *opt,
735786
if (!opt)
736787
return -EINVAL;
737788

789+
spin_lock(&taprio_list_lock);
790+
list_add(&q->taprio_list, &taprio_list);
791+
spin_unlock(&taprio_list_lock);
792+
738793
return taprio_change(sch, opt, extack);
739794
}
740795

@@ -947,14 +1002,24 @@ static struct Qdisc_ops taprio_qdisc_ops __read_mostly = {
9471002
.owner = THIS_MODULE,
9481003
};
9491004

1005+
static struct notifier_block taprio_device_notifier = {
1006+
.notifier_call = taprio_dev_notifier,
1007+
};
1008+
9501009
static int __init taprio_module_init(void)
9511010
{
1011+
int err = register_netdevice_notifier(&taprio_device_notifier);
1012+
1013+
if (err)
1014+
return err;
1015+
9521016
return register_qdisc(&taprio_qdisc_ops);
9531017
}
9541018

9551019
static void __exit taprio_module_exit(void)
9561020
{
9571021
unregister_qdisc(&taprio_qdisc_ops);
1022+
unregister_netdevice_notifier(&taprio_device_notifier);
9581023
}
9591024

9601025
module_init(taprio_module_init);

0 commit comments

Comments
 (0)