Commit 87e3de1e authored by David S. Miller's avatar David S. Miller

Merge branch '1GbE' of git://git.kernel.org/pub/scm/linux/kernel/git/jkirsher/next-queue

Jeff Kirsher says:

====================
1GbE Intel Wired LAN Driver Updates 2017-10-27

This patchset is a proposal of how the Traffic Control subsystem can
be used to offload the configuration of the Credit Based Shaper
(defined in the IEEE 802.1Q-2014 Section 8.6.8.2) into supported
network devices.

As part of this work, we've assessed previous public discussions
related to TSN enabling: patches from Henrik Austad (Cisco), the
presentation from Eric Mann at Linux Plumbers 2012, patches from
Gangfeng Huang (National Instruments) and the current state of the
OpenAVNU project (https://github.com/AVnu/OpenAvnu/).

Overview
========

Time-sensitive Networking (TSN) is a set of standards that aim to
address resources availability for providing bandwidth reservation and
bounded latency on Ethernet based LANs. The proposal described here
aims to cover mainly what is needed to enable the following standards:
802.1Qat and 802.1Qav.

The initial target of this work is the Intel i210 NIC, but other
controllers' datasheet were also taken into account, like the Renesas
RZ/A1H RZ/A1M group and the Synopsis DesignWare Ethernet QoS
controller.

Proposal
========

Feature-wise, what is covered here is the configuration interfaces for
HW implementations of the Credit-Based shaper (CBS, 802.1Qav). CBS is
a per-queue shaper. Given that this feature is related to traffic
shaping, and that the traffic control subsystem already provides a
queueing discipline that offloads config into the device driver (i.e.
mqprio), designing a new qdisc for the specific purpose of offloading
the config for the CBS shaper seemed like a good fit.

For steering traffic into the correct queues, we use the socket option
SO_PRIORITY and then a mechanism to map priority to traffic classes /
Tx queues. The qdisc mqprio is currently used in our tests.

As for the CBS config interface, this patchset is proposing a new
qdisc called 'cbs'. Its 'tc' cmd line is:

$ tc qdisc add dev IFACE parent ID cbs locredit N hicredit M sendslope S \
     idleslope I

   Note that the parameters for this qdisc are the ones defined by the
   802.1Q-2014 spec, so no hardware specific functionality is exposed here.

Per-stream shaping, as defined by IEEE 802.1Q-2014 Section 34.6.1, is
not yet covered by this proposal.

v2: Merged patch 6 of the original series into patch 4 based on feedback
    from David Miller.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 05ce8bd4 05f9d3e1
......@@ -353,7 +353,18 @@
#define E1000_RXPBS_CFG_TS_EN 0x80000000
#define I210_RXPBSIZE_DEFAULT 0x000000A2 /* RXPBSIZE default */
#define I210_RXPBSIZE_MASK 0x0000003F
#define I210_RXPBSIZE_PB_32KB 0x00000020
#define I210_TXPBSIZE_DEFAULT 0x04000014 /* TXPBSIZE default */
#define I210_TXPBSIZE_MASK 0xC0FFFFFF
#define I210_TXPBSIZE_PB0_8KB (8 << 0)
#define I210_TXPBSIZE_PB1_8KB (8 << 6)
#define I210_TXPBSIZE_PB2_4KB (4 << 12)
#define I210_TXPBSIZE_PB3_4KB (4 << 18)
#define I210_DTXMXPKTSZ_DEFAULT 0x00000098
#define I210_SR_QUEUES_NUM 2
/* SerDes Control */
#define E1000_SCTL_DISABLE_SERDES_LOOPBACK 0x0400
......@@ -1051,4 +1062,16 @@
#define E1000_VLAPQF_P_VALID(_n) (0x1 << (3 + (_n) * 4))
#define E1000_VLAPQF_QUEUE_MASK 0x03
/* TX Qav Control fields */
#define E1000_TQAVCTRL_XMIT_MODE BIT(0)
#define E1000_TQAVCTRL_DATAFETCHARB BIT(4)
#define E1000_TQAVCTRL_DATATRANARB BIT(8)
/* TX Qav Credit Control fields */
#define E1000_TQAVCC_IDLESLOPE_MASK 0xFFFF
#define E1000_TQAVCC_QUEUEMODE BIT(31)
/* Transmit Descriptor Control fields */
#define E1000_TXDCTL_PRIORITY BIT(27)
#endif
......@@ -421,6 +421,14 @@ do { \
#define E1000_I210_FLA 0x1201C
#define E1000_I210_DTXMXPKTSZ 0x355C
#define E1000_I210_TXDCTL(_n) (0x0E028 + ((_n) * 0x40))
#define E1000_I210_TQAVCTRL 0x3570
#define E1000_I210_TQAVCC(_n) (0x3004 + ((_n) * 0x40))
#define E1000_I210_TQAVHC(_n) (0x300C + ((_n) * 0x40))
#define E1000_INVM_DATA_REG(_n) (0x12120 + 4*(_n))
#define E1000_INVM_SIZE 64 /* Number of INVM Data Registers */
......
......@@ -281,6 +281,11 @@ struct igb_ring {
u16 count; /* number of desc. in the ring */
u8 queue_index; /* logical index of the ring*/
u8 reg_idx; /* physical index of the ring */
bool cbs_enable; /* indicates if CBS is enabled */
s32 idleslope; /* idleSlope in kbps */
s32 sendslope; /* sendSlope in kbps */
s32 hicredit; /* hiCredit in bytes */
s32 locredit; /* loCredit in bytes */
/* everything past this point are written often */
u16 next_to_clean;
......@@ -621,6 +626,7 @@ struct igb_adapter {
#define IGB_FLAG_EEE BIT(14)
#define IGB_FLAG_VLAN_PROMISC BIT(15)
#define IGB_FLAG_RX_LEGACY BIT(16)
#define IGB_FLAG_FQTSS BIT(17)
/* Media Auto Sense */
#define IGB_MAS_ENABLE_0 0X0001
......
This diff is collapsed.
......@@ -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
......
......@@ -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
......@@ -904,4 +904,23 @@ struct tc_pie_xstats {
__u32 maxq; /* maximum queue size */
__u32 ecn_mark; /* packets marked with ecn*/
};
/* CBS */
struct tc_cbs_qopt {
__u8 offload;
__u8 _pad[3];
__s32 hicredit;
__s32 locredit;
__s32 idleslope;
__s32 sendslope;
};
enum {
TCA_CBS_UNSPEC,
TCA_CBS_PARMS,
__TCA_CBS_MAX,
};
#define TCA_CBS_MAX (__TCA_CBS_MAX - 1)
#endif
......@@ -172,6 +172,17 @@ config NET_SCH_TBF
To compile this code as a module, choose M here: the
module will be called sch_tbf.
config NET_SCH_CBS
tristate "Credit Based Shaper (CBS)"
---help---
Say Y here if you want to use the Credit Based Shaper (CBS) packet
scheduling algorithm.
See the top of <file:net/sched/sch_cbs.c> for more details.
To compile this code as a module, choose M here: the
module will be called sch_cbs.
config NET_SCH_GRED
tristate "Generic Random Early Detection (GRED)"
---help---
......
......@@ -52,6 +52,7 @@ obj-$(CONFIG_NET_SCH_FQ_CODEL) += sch_fq_codel.o
obj-$(CONFIG_NET_SCH_FQ) += sch_fq.o
obj-$(CONFIG_NET_SCH_HHF) += sch_hhf.o
obj-$(CONFIG_NET_SCH_PIE) += sch_pie.o
obj-$(CONFIG_NET_SCH_CBS) += sch_cbs.o
obj-$(CONFIG_NET_CLS_U32) += cls_u32.o
obj-$(CONFIG_NET_CLS_ROUTE4) += cls_route.o
......
/*
* net/sched/sch_cbs.c Credit Based Shaper
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
* Authors: Vinicius Costa Gomes <vinicius.gomes@intel.com>
*
*/
/* Credit Based Shaper (CBS)
* =========================
*
* This is a simple rate-limiting shaper aimed at TSN applications on
* systems with known traffic workloads.
*
* Its algorithm is defined by the IEEE 802.1Q-2014 Specification,
* Section 8.6.8.2, and explained in more detail in the Annex L of the
* same specification.
*
* There are four tunables to be considered:
*
* 'idleslope': Idleslope is the rate of credits that is
* accumulated (in kilobits per second) when there is at least
* one packet waiting for transmission. Packets are transmitted
* when the current value of credits is equal or greater than
* zero. When there is no packet to be transmitted the amount of
* credits is set to zero. This is the main tunable of the CBS
* algorithm.
*
* 'sendslope':
* Sendslope is the rate of credits that is depleted (it should be a
* negative number of kilobits per second) when a transmission is
* ocurring. It can be calculated as follows, (IEEE 802.1Q-2014 Section
* 8.6.8.2 item g):
*
* sendslope = idleslope - port_transmit_rate
*
* 'hicredit': Hicredit defines the maximum amount of credits (in
* bytes) that can be accumulated. Hicredit depends on the
* characteristics of interfering traffic,
* 'max_interference_size' is the maximum size of any burst of
* traffic that can delay the transmission of a frame that is
* available for transmission for this traffic class, (IEEE
* 802.1Q-2014 Annex L, Equation L-3):
*
* hicredit = max_interference_size * (idleslope / port_transmit_rate)
*
* 'locredit': Locredit is the minimum amount of credits that can
* be reached. It is a function of the traffic flowing through
* this qdisc (IEEE 802.1Q-2014 Annex L, Equation L-2):
*
* locredit = max_frame_size * (sendslope / port_transmit_rate)
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/skbuff.h>
#include <net/netlink.h>
#include <net/sch_generic.h>
#include <net/pkt_sched.h>
#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 */
s32 locredit; /* in bytes */
s32 hicredit; /* in bytes */
s64 sendslope; /* in bytes/s */
s64 idleslope; /* in bytes/s */
struct qdisc_watchdog watchdog;
int (*enqueue)(struct sk_buff *skb, struct Qdisc *sch);
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);
if (sch->q.qlen == 0 && q->credits > 0) {
/* We need to stop accumulating credits when there's
* no enqueued packets and q->credits is positive.
*/
q->credits = 0;
q->last = ktime_get_ns();
}
return qdisc_enqueue_tail(skb, sch);
}
static int cbs_enqueue(struct sk_buff *skb, struct Qdisc *sch,
struct sk_buff **to_free)
{
struct cbs_sched_data *q = qdisc_priv(sch);
return q->enqueue(skb, sch);
}
/* timediff is in ns, slope is in bytes/s */
static s64 timediff_to_credits(s64 timediff, s64 slope)
{
return div64_s64(timediff * slope, NSEC_PER_SEC);
}
static s64 delay_from_credits(s64 credits, s64 slope)
{
if (unlikely(slope == 0))
return S64_MAX;
return div64_s64(-credits * NSEC_PER_SEC, slope);
}
static s64 credits_from_len(unsigned int len, s64 slope, s64 port_rate)
{
if (unlikely(port_rate == 0))
return S64_MAX;
return div64_s64(len * slope, port_rate);
}
static struct sk_buff *cbs_dequeue_soft(struct Qdisc *sch)
{
struct cbs_sched_data *q = qdisc_priv(sch);
s64 now = ktime_get_ns();
struct sk_buff *skb;
s64 credits;
int len;
if (q->credits < 0) {
credits = timediff_to_credits(now - q->last, q->idleslope);
credits = q->credits + credits;
q->credits = min_t(s64, credits, q->hicredit);
if (q->credits < 0) {
s64 delay;
delay = delay_from_credits(q->credits, q->idleslope);
qdisc_watchdog_schedule_ns(&q->watchdog, now + delay);
q->last = now;
return NULL;
}
}
skb = qdisc_dequeue_head(sch);
if (!skb)
return NULL;
len = qdisc_pkt_len(skb);
/* As sendslope is a negative number, this will decrease the
* amount of q->credits.
*/
credits = credits_from_len(len, q->sendslope, q->port_rate);
credits += q->credits;
q->credits = max_t(s64, credits, q->locredit);
q->last = now;
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);
return q->dequeue(sch);
}
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 tc_cbs_qopt *qopt;
int err;
err = nla_parse_nested(tb, TCA_CBS_MAX, opt, cbs_policy, NULL);
if (err < 0)
return err;
if (!tb[TCA_CBS_PARMS])
return -EINVAL;
qopt = nla_data(tb[TCA_CBS_PARMS]);
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;
q->port_rate = link_speed * 1000 * BYTES_PER_KBIT;
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);
}
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)
{
struct cbs_sched_data *q = qdisc_priv(sch);
struct tc_cbs_qopt opt = { };
struct nlattr *nest;
nest = nla_nest_start(skb, TCA_OPTIONS);
if (!nest)
goto nla_put_failure;
opt.hicredit = q->hicredit;
opt.locredit = q->locredit;
opt.sendslope = div64_s64(q->sendslope, BYTES_PER_KBIT);
opt.idleslope = div64_s64(q->idleslope, BYTES_PER_KBIT);
opt.offload = q->offload;
if (nla_put(skb, TCA_CBS_PARMS, sizeof(opt), &opt))
goto nla_put_failure;
return nla_nest_end(skb, nest);
nla_put_failure:
nla_nest_cancel(skb, nest);
return -1;
}
static struct Qdisc_ops cbs_qdisc_ops __read_mostly = {
.id = "cbs",
.priv_size = sizeof(struct cbs_sched_data),
.enqueue = cbs_enqueue,
.dequeue = cbs_dequeue,
.peek = qdisc_peek_dequeued,
.init = cbs_init,
.reset = qdisc_reset_queue,
.destroy = cbs_destroy,
.change = cbs_change,
.dump = cbs_dump,
.owner = THIS_MODULE,
};
static int __init cbs_module_init(void)
{
return register_qdisc(&cbs_qdisc_ops);
}
static void __exit cbs_module_exit(void)
{
unregister_qdisc(&cbs_qdisc_ops);
}
module_init(cbs_module_init)
module_exit(cbs_module_exit)
MODULE_LICENSE("GPL");
......@@ -603,8 +603,14 @@ struct Qdisc *qdisc_alloc(struct netdev_queue *dev_queue,
struct Qdisc *sch;
unsigned int size = QDISC_ALIGN(sizeof(*sch)) + ops->priv_size;
int err = -ENOBUFS;
struct net_device *dev = dev_queue->dev;
struct net_device *dev;
if (!dev_queue) {
err = -EINVAL;
goto errout;
}
dev = dev_queue->dev;
p = kzalloc_node(size, GFP_KERNEL,
netdev_queue_numa_node_read(dev_queue));
......
......@@ -130,15 +130,7 @@ static struct netdev_queue *mq_queue_get(struct Qdisc *sch, unsigned long cl)
static struct netdev_queue *mq_select_queue(struct Qdisc *sch,
struct tcmsg *tcm)
{
unsigned int ntx = TC_H_MIN(tcm->tcm_parent);
struct netdev_queue *dev_queue = mq_queue_get(sch, ntx);
if (!dev_queue) {
struct net_device *dev = qdisc_dev(sch);
return netdev_get_tx_queue(dev, 0);
}
return dev_queue;
return mq_queue_get(sch, TC_H_MIN(tcm->tcm_parent));
}
static int mq_graft(struct Qdisc *sch, unsigned long cl, struct Qdisc *new,
......
......@@ -575,6 +575,12 @@ static void mqprio_walk(struct Qdisc *sch, struct qdisc_walker *arg)
}
}
static struct netdev_queue *mqprio_select_queue(struct Qdisc *sch,
struct tcmsg *tcm)
{
return mqprio_queue_get(sch, TC_H_MIN(tcm->tcm_parent));
}
static const struct Qdisc_class_ops mqprio_class_ops = {
.graft = mqprio_graft,
.leaf = mqprio_leaf,
......@@ -582,6 +588,7 @@ static const struct Qdisc_class_ops mqprio_class_ops = {
.walk = mqprio_walk,
.dump = mqprio_dump_class,
.dump_stats = mqprio_dump_class_stats,
.select_queue = mqprio_select_queue,
};
static struct Qdisc_ops mqprio_qdisc_ops __read_mostly = {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment