Skip to content

Commit

Permalink
net/sched: Add support for HW offloading for CBS
Browse files Browse the repository at this point in the history
This adds support for offloading the CBS algorithm to the controller,
if supported. Drivers wanting to support CBS offload must implement
the .ndo_setup_tc callback and handle the TC_SETUP_CBS (introduced
here) type.

Signed-off-by: Vinicius Costa Gomes <vinicius.gomes@intel.com>
Tested-by: Henrik Austad <henrik@austad.us>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
  • Loading branch information
Vinicius Costa Gomes authored and Jeff Kirsher committed Oct 27, 2017
1 parent 585d763 commit 3d0bd02
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 12 deletions.
1 change: 1 addition & 0 deletions include/linux/netdevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -776,6 +776,7 @@ enum tc_setup_type {
TC_SETUP_CLSMATCHALL,
TC_SETUP_CLSBPF,
TC_SETUP_BLOCK,
TC_SETUP_CBS,
};

/* These structures hold the attributes of xdp state that are being passed
Expand Down
9 changes: 9 additions & 0 deletions include/net/pkt_sched.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,4 +140,13 @@ static inline struct net *qdisc_net(struct Qdisc *q)
return dev_net(q->dev_queue->dev);
}

struct tc_cbs_qopt_offload {
u8 enable;
s32 queue;
s32 hicredit;
s32 locredit;
s32 idleslope;
s32 sendslope;
};

#endif
104 changes: 92 additions & 12 deletions net/sched/sch_cbs.c
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@
#define BYTES_PER_KBIT (1000LL / 8)

struct cbs_sched_data {
bool offload;
int queue;
s64 port_rate; /* in bytes/s */
s64 last; /* timestamp in ns */
s64 credits; /* in bytes */
Expand All @@ -80,6 +82,11 @@ struct cbs_sched_data {
struct sk_buff *(*dequeue)(struct Qdisc *sch);
};

static int cbs_enqueue_offload(struct sk_buff *skb, struct Qdisc *sch)
{
return qdisc_enqueue_tail(skb, sch);
}

static int cbs_enqueue_soft(struct sk_buff *skb, struct Qdisc *sch)
{
struct cbs_sched_data *q = qdisc_priv(sch);
Expand Down Expand Up @@ -169,6 +176,11 @@ static struct sk_buff *cbs_dequeue_soft(struct Qdisc *sch)
return skb;
}

static struct sk_buff *cbs_dequeue_offload(struct Qdisc *sch)
{
return qdisc_dequeue_head(sch);
}

static struct sk_buff *cbs_dequeue(struct Qdisc *sch)
{
struct cbs_sched_data *q = qdisc_priv(sch);
Expand All @@ -180,14 +192,66 @@ static const struct nla_policy cbs_policy[TCA_CBS_MAX + 1] = {
[TCA_CBS_PARMS] = { .len = sizeof(struct tc_cbs_qopt) },
};

static void cbs_disable_offload(struct net_device *dev,
struct cbs_sched_data *q)
{
struct tc_cbs_qopt_offload cbs = { };
const struct net_device_ops *ops;
int err;

if (!q->offload)
return;

q->enqueue = cbs_enqueue_soft;
q->dequeue = cbs_dequeue_soft;

ops = dev->netdev_ops;
if (!ops->ndo_setup_tc)
return;

cbs.queue = q->queue;
cbs.enable = 0;

err = ops->ndo_setup_tc(dev, TC_SETUP_CBS, &cbs);
if (err < 0)
pr_warn("Couldn't disable CBS offload for queue %d\n",
cbs.queue);
}

static int cbs_enable_offload(struct net_device *dev, struct cbs_sched_data *q,
const struct tc_cbs_qopt *opt)
{
const struct net_device_ops *ops = dev->netdev_ops;
struct tc_cbs_qopt_offload cbs = { };
int err;

if (!ops->ndo_setup_tc)
return -EOPNOTSUPP;

cbs.queue = q->queue;

cbs.enable = 1;
cbs.hicredit = opt->hicredit;
cbs.locredit = opt->locredit;
cbs.idleslope = opt->idleslope;
cbs.sendslope = opt->sendslope;

err = ops->ndo_setup_tc(dev, TC_SETUP_CBS, &cbs);
if (err < 0)
return err;

q->enqueue = cbs_enqueue_offload;
q->dequeue = cbs_dequeue_offload;

return 0;
}

static int cbs_change(struct Qdisc *sch, struct nlattr *opt)
{
struct cbs_sched_data *q = qdisc_priv(sch);
struct net_device *dev = qdisc_dev(sch);
struct nlattr *tb[TCA_CBS_MAX + 1];
struct ethtool_link_ksettings ecmd;
struct tc_cbs_qopt *qopt;
s64 link_speed;
int err;

err = nla_parse_nested(tb, TCA_CBS_MAX, opt, cbs_policy, NULL);
Expand All @@ -199,34 +263,47 @@ static int cbs_change(struct Qdisc *sch, struct nlattr *opt)

qopt = nla_data(tb[TCA_CBS_PARMS]);

if (qopt->offload)
return -EOPNOTSUPP;
if (!qopt->offload) {
struct ethtool_link_ksettings ecmd;
s64 link_speed;

if (!__ethtool_get_link_ksettings(dev, &ecmd))
link_speed = ecmd.base.speed;
else
link_speed = SPEED_1000;
if (!__ethtool_get_link_ksettings(dev, &ecmd))
link_speed = ecmd.base.speed;
else
link_speed = SPEED_1000;

q->port_rate = link_speed * 1000 * BYTES_PER_KBIT;
q->port_rate = link_speed * 1000 * BYTES_PER_KBIT;

q->enqueue = cbs_enqueue_soft;
q->dequeue = cbs_dequeue_soft;
cbs_disable_offload(dev, q);
} else {
err = cbs_enable_offload(dev, q, qopt);
if (err < 0)
return err;
}

/* Everything went OK, save the parameters used. */
q->hicredit = qopt->hicredit;
q->locredit = qopt->locredit;
q->idleslope = qopt->idleslope * BYTES_PER_KBIT;
q->sendslope = qopt->sendslope * BYTES_PER_KBIT;
q->offload = qopt->offload;

return 0;
}

static int cbs_init(struct Qdisc *sch, struct nlattr *opt)
{
struct cbs_sched_data *q = qdisc_priv(sch);
struct net_device *dev = qdisc_dev(sch);

if (!opt)
return -EINVAL;

q->queue = sch->dev_queue - netdev_get_tx_queue(dev, 0);

q->enqueue = cbs_enqueue_soft;
q->dequeue = cbs_dequeue_soft;

qdisc_watchdog_init(&q->watchdog, sch);

return cbs_change(sch, opt);
Expand All @@ -235,8 +312,11 @@ static int cbs_init(struct Qdisc *sch, struct nlattr *opt)
static void cbs_destroy(struct Qdisc *sch)
{
struct cbs_sched_data *q = qdisc_priv(sch);
struct net_device *dev = qdisc_dev(sch);

qdisc_watchdog_cancel(&q->watchdog);

cbs_disable_offload(dev, q);
}

static int cbs_dump(struct Qdisc *sch, struct sk_buff *skb)
Expand All @@ -253,7 +333,7 @@ static int cbs_dump(struct Qdisc *sch, struct sk_buff *skb)
opt.locredit = q->locredit;
opt.sendslope = div64_s64(q->sendslope, BYTES_PER_KBIT);
opt.idleslope = div64_s64(q->idleslope, BYTES_PER_KBIT);
opt.offload = 0;
opt.offload = q->offload;

if (nla_put(skb, TCA_CBS_PARMS, sizeof(opt), &opt))
goto nla_put_failure;
Expand Down

0 comments on commit 3d0bd02

Please sign in to comment.