Skip to content

Commit e0a7683

Browse files
Leandro Dorileodavem330
Leandro Dorileo
authored andcommitted
net/sched: cbs: fix port_rate miscalculation
The Credit Based Shaper heavily depends on link speed to calculate the scheduling credits, we can't properly calculate the credits if the device has failed to report the link speed. In that case we can't dequeue packets assuming a wrong port rate that will result into an inconsistent credit distribution. 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 properly re calculate the port rate whenever the link speed is changed. Fixes: 3d0bd02 ("net/sched: Add support for HW offloading for CBS") Signed-off-by: Leandro Dorileo <[email protected]> Reviewed-by: Vedang Patel <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 7b9eba7 commit e0a7683

File tree

1 file changed

+84
-14
lines changed

1 file changed

+84
-14
lines changed

net/sched/sch_cbs.c

Lines changed: 84 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -61,16 +61,20 @@
6161
#include <linux/string.h>
6262
#include <linux/errno.h>
6363
#include <linux/skbuff.h>
64+
#include <net/netevent.h>
6465
#include <net/netlink.h>
6566
#include <net/sch_generic.h>
6667
#include <net/pkt_sched.h>
6768

69+
static LIST_HEAD(cbs_list);
70+
static DEFINE_SPINLOCK(cbs_list_lock);
71+
6872
#define BYTES_PER_KBIT (1000LL / 8)
6973

7074
struct cbs_sched_data {
7175
bool offload;
7276
int queue;
73-
s64 port_rate; /* in bytes/s */
77+
atomic64_t port_rate; /* in bytes/s */
7478
s64 last; /* timestamp in ns */
7579
s64 credits; /* in bytes */
7680
s32 locredit; /* in bytes */
@@ -82,6 +86,7 @@ struct cbs_sched_data {
8286
struct sk_buff **to_free);
8387
struct sk_buff *(*dequeue)(struct Qdisc *sch);
8488
struct Qdisc *qdisc;
89+
struct list_head cbs_list;
8590
};
8691

8792
static int cbs_child_enqueue(struct sk_buff *skb, struct Qdisc *sch,
@@ -181,6 +186,11 @@ static struct sk_buff *cbs_dequeue_soft(struct Qdisc *sch)
181186
s64 credits;
182187
int len;
183188

189+
if (atomic64_read(&q->port_rate) == -1) {
190+
WARN_ONCE(1, "cbs: dequeue() called with unknown port rate.");
191+
return NULL;
192+
}
193+
184194
if (q->credits < 0) {
185195
credits = timediff_to_credits(now - q->last, q->idleslope);
186196

@@ -207,7 +217,8 @@ static struct sk_buff *cbs_dequeue_soft(struct Qdisc *sch)
207217
/* As sendslope is a negative number, this will decrease the
208218
* amount of q->credits.
209219
*/
210-
credits = credits_from_len(len, q->sendslope, q->port_rate);
220+
credits = credits_from_len(len, q->sendslope,
221+
atomic64_read(&q->port_rate));
211222
credits += q->credits;
212223

213224
q->credits = max_t(s64, credits, q->locredit);
@@ -294,6 +305,50 @@ static int cbs_enable_offload(struct net_device *dev, struct cbs_sched_data *q,
294305
return 0;
295306
}
296307

308+
static void cbs_set_port_rate(struct net_device *dev, struct cbs_sched_data *q)
309+
{
310+
struct ethtool_link_ksettings ecmd;
311+
int port_rate = -1;
312+
313+
if (!__ethtool_get_link_ksettings(dev, &ecmd) &&
314+
ecmd.base.speed != SPEED_UNKNOWN)
315+
port_rate = ecmd.base.speed * 1000 * BYTES_PER_KBIT;
316+
317+
atomic64_set(&q->port_rate, port_rate);
318+
netdev_dbg(dev, "cbs: set %s's port_rate to: %lld, linkspeed: %d\n",
319+
dev->name, (long long)atomic64_read(&q->port_rate),
320+
ecmd.base.speed);
321+
}
322+
323+
static int cbs_dev_notifier(struct notifier_block *nb, unsigned long event,
324+
void *ptr)
325+
{
326+
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
327+
struct cbs_sched_data *q;
328+
struct net_device *qdev;
329+
bool found = false;
330+
331+
ASSERT_RTNL();
332+
333+
if (event != NETDEV_UP && event != NETDEV_CHANGE)
334+
return NOTIFY_DONE;
335+
336+
spin_lock(&cbs_list_lock);
337+
list_for_each_entry(q, &cbs_list, cbs_list) {
338+
qdev = qdisc_dev(q->qdisc);
339+
if (qdev == dev) {
340+
found = true;
341+
break;
342+
}
343+
}
344+
spin_unlock(&cbs_list_lock);
345+
346+
if (found)
347+
cbs_set_port_rate(dev, q);
348+
349+
return NOTIFY_DONE;
350+
}
351+
297352
static int cbs_change(struct Qdisc *sch, struct nlattr *opt,
298353
struct netlink_ext_ack *extack)
299354
{
@@ -315,16 +370,7 @@ static int cbs_change(struct Qdisc *sch, struct nlattr *opt,
315370
qopt = nla_data(tb[TCA_CBS_PARMS]);
316371

317372
if (!qopt->offload) {
318-
struct ethtool_link_ksettings ecmd;
319-
s64 link_speed;
320-
321-
if (!__ethtool_get_link_ksettings(dev, &ecmd))
322-
link_speed = ecmd.base.speed;
323-
else
324-
link_speed = SPEED_1000;
325-
326-
q->port_rate = link_speed * 1000 * BYTES_PER_KBIT;
327-
373+
cbs_set_port_rate(dev, q);
328374
cbs_disable_offload(dev, q);
329375
} else {
330376
err = cbs_enable_offload(dev, q, qopt, extack);
@@ -347,6 +393,7 @@ static int cbs_init(struct Qdisc *sch, struct nlattr *opt,
347393
{
348394
struct cbs_sched_data *q = qdisc_priv(sch);
349395
struct net_device *dev = qdisc_dev(sch);
396+
int err;
350397

351398
if (!opt) {
352399
NL_SET_ERR_MSG(extack, "Missing CBS qdisc options which are mandatory");
@@ -367,16 +414,29 @@ static int cbs_init(struct Qdisc *sch, struct nlattr *opt,
367414

368415
qdisc_watchdog_init(&q->watchdog, sch);
369416

370-
return cbs_change(sch, opt, extack);
417+
err = cbs_change(sch, opt, extack);
418+
if (err)
419+
return err;
420+
421+
if (!q->offload) {
422+
spin_lock(&cbs_list_lock);
423+
list_add(&q->cbs_list, &cbs_list);
424+
spin_unlock(&cbs_list_lock);
425+
}
426+
427+
return 0;
371428
}
372429

373430
static void cbs_destroy(struct Qdisc *sch)
374431
{
375432
struct cbs_sched_data *q = qdisc_priv(sch);
376433
struct net_device *dev = qdisc_dev(sch);
377434

378-
qdisc_watchdog_cancel(&q->watchdog);
435+
spin_lock(&cbs_list_lock);
436+
list_del(&q->cbs_list);
437+
spin_unlock(&cbs_list_lock);
379438

439+
qdisc_watchdog_cancel(&q->watchdog);
380440
cbs_disable_offload(dev, q);
381441

382442
if (q->qdisc)
@@ -487,14 +547,24 @@ static struct Qdisc_ops cbs_qdisc_ops __read_mostly = {
487547
.owner = THIS_MODULE,
488548
};
489549

550+
static struct notifier_block cbs_device_notifier = {
551+
.notifier_call = cbs_dev_notifier,
552+
};
553+
490554
static int __init cbs_module_init(void)
491555
{
556+
int err = register_netdevice_notifier(&cbs_device_notifier);
557+
558+
if (err)
559+
return err;
560+
492561
return register_qdisc(&cbs_qdisc_ops);
493562
}
494563

495564
static void __exit cbs_module_exit(void)
496565
{
497566
unregister_qdisc(&cbs_qdisc_ops);
567+
unregister_netdevice_notifier(&cbs_device_notifier);
498568
}
499569
module_init(cbs_module_init)
500570
module_exit(cbs_module_exit)

0 commit comments

Comments
 (0)