2019-11-22 07:21:33

by Po Liu

[permalink] [raw]
Subject: [net-next] enetc: add support Credit Based Shaper(CBS) for hardware offload

The ENETC hardware support the Credit Based Shaper(CBS) which part
of the IEEE-802.1Qav. The CBS driver was loaded by the sch_cbs
interface when set in the QOS in the kernel.

Here is an example command to set 20Mbits bandwidth in 1Gbits port
for taffic class 7:

tc qdisc add dev eth0 root handle 1: mqprio \
num_tc 8 map 0 1 2 3 4 5 6 7 hw 1

tc qdisc replace dev eth0 parent 1:8 cbs \
locredit -1470 hicredit 30 \
sendslope -980000 idleslope 20000 offload 1

Signed-off-by: Po Liu <[email protected]>
Reviewed-by: Claudiu Manoil <[email protected]>
Reviewed-by: Vladimir Oltean <[email protected]>
---
drivers/net/ethernet/freescale/enetc/Kconfig | 4 +-
drivers/net/ethernet/freescale/enetc/enetc.c | 2 +
drivers/net/ethernet/freescale/enetc/enetc.h | 2 +
.../net/ethernet/freescale/enetc/enetc_hw.h | 4 +
.../net/ethernet/freescale/enetc/enetc_qos.c | 126 ++++++++++++++++++
5 files changed, 136 insertions(+), 2 deletions(-)

diff --git a/drivers/net/ethernet/freescale/enetc/Kconfig b/drivers/net/ethernet/freescale/enetc/Kconfig
index 491659fe3e35..edad4ca46327 100644
--- a/drivers/net/ethernet/freescale/enetc/Kconfig
+++ b/drivers/net/ethernet/freescale/enetc/Kconfig
@@ -53,10 +53,10 @@ config FSL_ENETC_HW_TIMESTAMPING

config FSL_ENETC_QOS
bool "ENETC hardware Time-sensitive Network support"
- depends on (FSL_ENETC || FSL_ENETC_VF) && NET_SCH_TAPRIO
+ depends on (FSL_ENETC || FSL_ENETC_VF) && (NET_SCH_TAPRIO || NET_SCH_CBS)
help
There are Time-Sensitive Network(TSN) capabilities(802.1Qbv/802.1Qci
/802.1Qbu etc.) supported by ENETC. These TSN capabilities can be set
enable/disable from user space via Qos commands(tc). In the kernel
side, it can be loaded by Qos driver. Currently, it is only support
- taprio(802.1Qbv).
+ taprio(802.1Qbv) and Credit Based Shaper(802.1Qbu).
diff --git a/drivers/net/ethernet/freescale/enetc/enetc.c b/drivers/net/ethernet/freescale/enetc/enetc.c
index 27f6fd1708f0..9db1b96ed9b9 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc.c
@@ -1496,6 +1496,8 @@ int enetc_setup_tc(struct net_device *ndev, enum tc_setup_type type,
return enetc_setup_tc_mqprio(ndev, type_data);
case TC_SETUP_QDISC_TAPRIO:
return enetc_setup_tc_taprio(ndev, type_data);
+ case TC_SETUP_QDISC_CBS:
+ return enetc_setup_tc_cbs(ndev, type_data);
default:
return -EOPNOTSUPP;
}
diff --git a/drivers/net/ethernet/freescale/enetc/enetc.h b/drivers/net/ethernet/freescale/enetc/enetc.h
index 89f23156f330..7ee0da6d0015 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc.h
@@ -255,7 +255,9 @@ int enetc_send_cmd(struct enetc_si *si, struct enetc_cbd *cbd);
#ifdef CONFIG_FSL_ENETC_QOS
int enetc_setup_tc_taprio(struct net_device *ndev, void *type_data);
void enetc_sched_speed_set(struct net_device *ndev);
+int enetc_setup_tc_cbs(struct net_device *ndev, void *type_data);
#else
#define enetc_setup_tc_taprio(ndev, type_data) -EOPNOTSUPP
#define enetc_sched_speed_set(ndev) (void)0
+#define enetc_setup_tc_cbs(ndev, type_data) -EOPNOTSUPP
#endif
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_hw.h b/drivers/net/ethernet/freescale/enetc/enetc_hw.h
index 924ddb6d358a..51f543ef37a8 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_hw.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_hw.h
@@ -185,6 +185,8 @@ enum enetc_bdr_type {TX, RX};
#define ENETC_PSICFGR0_SIVC(bmp) (((bmp) & 0xff) << 24) /* VLAN_TYPE */

#define ENETC_PTCCBSR0(n) (0x1110 + (n) * 8) /* n = 0 to 7*/
+#define ENETC_CBSE BIT(31)
+#define ENETC_CBS_BW_MASK GENMASK(6, 0)
#define ENETC_PTCCBSR1(n) (0x1114 + (n) * 8) /* n = 0 to 7*/
#define ENETC_RSSHASH_KEY_SIZE 40
#define ENETC_PRSSK(n) (0x1410 + (n) * 4) /* n = [0..9] */
@@ -603,6 +605,8 @@ struct enetc_cbd {
u8 status_flags;
};

+#define ENETC_CLK 400000000ULL
+
/* port time gating control register */
#define ENETC_QBV_PTGCR_OFFSET 0x11a00
#define ENETC_QBV_TGE BIT(31)
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_qos.c b/drivers/net/ethernet/freescale/enetc/enetc_qos.c
index 66a3da61ca16..98c3d062459a 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_qos.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_qos.c
@@ -170,3 +170,129 @@ int enetc_setup_tc_taprio(struct net_device *ndev, void *type_data)

return err;
}
+
+static u32 enetc_get_cbs_enable(struct enetc_hw *hw, u8 tc)
+{
+ return enetc_port_rd(hw, ENETC_PTCCBSR0(tc)) & ENETC_CBSE;
+}
+
+static u8 enetc_get_cbs_bw(struct enetc_hw *hw, u8 tc)
+{
+ return enetc_port_rd(hw, ENETC_PTCCBSR0(tc)) & ENETC_CBS_BW_MASK;
+}
+
+int enetc_setup_tc_cbs(struct net_device *ndev, void *type_data)
+{
+ struct enetc_ndev_priv *priv = netdev_priv(ndev);
+ struct tc_cbs_qopt_offload *cbs = type_data;
+ u32 port_transmit_rate = priv->speed;
+ u8 tc_nums = netdev_get_num_tc(ndev);
+ struct enetc_si *si = priv->si;
+ u32 hi_credit_bit, hi_credit_reg;
+ u32 max_interference_size;
+ u32 port_frame_max_size;
+ u32 tc_max_sized_frame;
+ u8 tc = cbs->queue;
+ u8 prio_top, prio_next;
+ int bw_sum = 0;
+ u8 bw;
+
+ prio_top = netdev_get_prio_tc_map(ndev, tc_nums - 1);
+ prio_next = netdev_get_prio_tc_map(ndev, tc_nums - 2);
+
+ /* Support highest prio and second prio tc in cbs mode */
+ if (tc != prio_top && tc != prio_next)
+ return -EOPNOTSUPP;
+
+ if (!cbs->enable) {
+ /* Make sure the other TC that are numerically
+ * lower than this TC have been disabled.
+ */
+ if (tc == prio_top &&
+ enetc_get_cbs_enable(&si->hw, prio_next)) {
+ dev_err(&ndev->dev,
+ "Disable TC%d before disable TC%d\n",
+ prio_next, tc);
+ return -EINVAL;
+ }
+
+ enetc_port_wr(&si->hw, ENETC_PTCCBSR1(tc), 0);
+ enetc_port_wr(&si->hw, ENETC_PTCCBSR0(tc), 0);
+
+ return 0;
+ }
+
+ if (cbs->idleslope - cbs->sendslope != port_transmit_rate * 1000L ||
+ cbs->idleslope < 0 || cbs->sendslope > 0)
+ return -EOPNOTSUPP;
+
+ port_frame_max_size = ndev->mtu + VLAN_ETH_HLEN + ETH_FCS_LEN;
+
+ bw = cbs->idleslope / (port_transmit_rate * 10UL);
+
+ /* Make sure the other TC that are numerically
+ * higher than this TC have been enabled.
+ */
+ if (tc == prio_next) {
+ if (!enetc_get_cbs_enable(&si->hw, prio_top)) {
+ dev_err(&ndev->dev,
+ "Enable TC%d first before enable TC%d\n",
+ prio_top, prio_next);
+ return -EINVAL;
+ }
+ bw_sum += enetc_get_cbs_bw(&si->hw, prio_top);
+ }
+
+ if (bw_sum + bw >= 100) {
+ dev_err(&ndev->dev,
+ "The sum of all CBS Bandwidth can't exceed 100\n");
+ return -EINVAL;
+ }
+
+ tc_max_sized_frame = enetc_port_rd(&si->hw, ENETC_PTCMSDUR(tc));
+
+ /* For top prio TC, the max_interfrence_size is maxSizedFrame.
+ *
+ * For next prio TC, the max_interfrence_size is calculated as below:
+ *
+ * max_interference_size = M0 + Ma + Ra * M0 / (R0 - Ra)
+ *
+ * - RA: idleSlope for AVB Class A
+ * - R0: port transmit rate
+ * - M0: maximum sized frame for the port
+ * - MA: maximum sized frame for AVB Class A
+ */
+
+ if (tc == prio_top) {
+ max_interference_size = port_frame_max_size * 8;
+ } else {
+ u32 m0, ma, r0, ra;
+
+ m0 = port_frame_max_size * 8;
+ ma = enetc_port_rd(&si->hw, ENETC_PTCMSDUR(prio_top)) * 8;
+ ra = enetc_get_cbs_bw(&si->hw, prio_top) *
+ port_transmit_rate * 10000ULL;
+ r0 = port_transmit_rate * 1000000ULL;
+ max_interference_size = m0 + ma + (u64)ra * m0 / (r0 - ra);
+ }
+
+ /* hiCredit bits calculate by:
+ *
+ * maxSizedFrame * (idleSlope/portTxRate)
+ */
+ hi_credit_bit = max_interference_size * bw / 100;
+
+ /* hiCredit bits to hiCredit register need to calculated as:
+ *
+ * (enetClockFrequency / portTransmitRate) * 100
+ */
+ hi_credit_reg = (ENETC_CLK * 100ULL) * hi_credit_bit
+ / (port_transmit_rate * 1000000ULL);
+
+ enetc_port_wr(&si->hw, ENETC_PTCCBSR1(tc), hi_credit_reg);
+
+ /* Set bw register and enable this traffic class */
+ enetc_port_wr(&si->hw, ENETC_PTCCBSR0(tc), bw | ENETC_CBSE);
+
+ return 0;
+}
--
2.17.1


2019-11-24 03:07:53

by Jakub Kicinski

[permalink] [raw]
Subject: Re: [net-next] enetc: add support Credit Based Shaper(CBS) for hardware offload

On Fri, 22 Nov 2019 07:17:18 +0000, Po Liu wrote:
> + if (tc == prio_top) {
> + max_interference_size = port_frame_max_size * 8;
> + } else {
> + u32 m0, ma, r0, ra;
> +
> + m0 = port_frame_max_size * 8;
> + ma = enetc_port_rd(&si->hw, ENETC_PTCMSDUR(prio_top)) * 8;
> + ra = enetc_get_cbs_bw(&si->hw, prio_top) *
> + port_transmit_rate * 10000ULL;
> + r0 = port_transmit_rate * 1000000ULL;
> + max_interference_size = m0 + ma + (u64)ra * m0 / (r0 - ra);
> + }
> +
> + /* hiCredit bits calculate by:
> + *
> + * maxSizedFrame * (idleSlope/portTxRate)
> + */
> + hi_credit_bit = max_interference_size * bw / 100;
> +
> + /* hiCredit bits to hiCredit register need to calculated as:
> + *
> + * (enetClockFrequency / portTransmitRate) * 100
> + */
> + hi_credit_reg = (ENETC_CLK * 100ULL) * hi_credit_bit
> + / (port_transmit_rate * 1000000ULL);

Hi! The patch looks good to me, but I'm concerned about those 64bit
divisions here. Don't these need to be div_u64() & co.? Otherwise
we may see one of the:

ERROR: "__udivdi3" [drivers/net/ethernet/freescale/enetc/fsl-enetc.ko] undefined!

messages from the build bot..

I could be wrong, I haven't actually tested..

2019-11-24 23:37:18

by Jakub Kicinski

[permalink] [raw]
Subject: Re: [net-next] enetc: add support Credit Based Shaper(CBS) for hardware offload

On Sat, 23 Nov 2019 19:02:09 -0800, Jakub Kicinski wrote:
> On Fri, 22 Nov 2019 07:17:18 +0000, Po Liu wrote:
> > + if (tc == prio_top) {
> > + max_interference_size = port_frame_max_size * 8;
> > + } else {
> > + u32 m0, ma, r0, ra;
> > +
> > + m0 = port_frame_max_size * 8;
> > + ma = enetc_port_rd(&si->hw, ENETC_PTCMSDUR(prio_top)) * 8;
> > + ra = enetc_get_cbs_bw(&si->hw, prio_top) *
> > + port_transmit_rate * 10000ULL;
> > + r0 = port_transmit_rate * 1000000ULL;
> > + max_interference_size = m0 + ma + (u64)ra * m0 / (r0 - ra);
> > + }
> > +
> > + /* hiCredit bits calculate by:
> > + *
> > + * maxSizedFrame * (idleSlope/portTxRate)
> > + */
> > + hi_credit_bit = max_interference_size * bw / 100;
> > +
> > + /* hiCredit bits to hiCredit register need to calculated as:
> > + *
> > + * (enetClockFrequency / portTransmitRate) * 100
> > + */
> > + hi_credit_reg = (ENETC_CLK * 100ULL) * hi_credit_bit
> > + / (port_transmit_rate * 1000000ULL);
>
> Hi! The patch looks good to me, but I'm concerned about those 64bit
> divisions here. Don't these need to be div_u64() & co.? Otherwise
> we may see one of the:
>
> ERROR: "__udivdi3" [drivers/net/ethernet/freescale/enetc/fsl-enetc.ko] undefined!
>
> messages from the build bot..
>
> I could be wrong, I haven't actually tested..

Yup:

drivers/net/ethernet/freescale/enetc/enetc_qos.o: In function `enetc_setup_tc_cbs':
enetc_qos.c:(.text+0x5b4): undefined reference to `__udivdi3'
enetc_qos.c:(.text+0x608): undefined reference to `__udivdi3'
/home/jkicinski/devel/linux/Makefile:1077: recipe for target 'vmlinux' failed
make[1]: *** [vmlinux] Error 1
make[1]: Leaving directory '/home/jkicinski/devel/linux/build_tmp2'
Makefile:179: recipe for target 'sub-make' failed
make: *** [sub-make] Error 2

Please fix and repost.

2019-11-25 03:05:19

by Po Liu

[permalink] [raw]
Subject: RE: [EXT] Re: [net-next] enetc: add support Credit Based Shaper(CBS) for hardware offload

Hi Jakub,

Thanks!


Br,
Po Liu

> -----Original Message-----
> From: Jakub Kicinski <[email protected]>
> Sent: 2019??11??25?? 7:35
> To: Po Liu <[email protected]>
> Cc: [email protected]; [email protected];
> [email protected]; [email protected]; Claudiu Manoil
> <[email protected]>; Vladimir Oltean <[email protected]>;
> Alexandru Marginean <[email protected]>; Xiaoliang Yang
> <[email protected]>; Roy Zang <[email protected]>; Mingkai Hu
> <[email protected]>; Jerry Huang <[email protected]>; Leo Li
> <[email protected]>
> Subject: [EXT] Re: [net-next] enetc: add support Credit Based Shaper(CBS) for
> hardware offload
>
> Caution: EXT Email
>
> On Sat, 23 Nov 2019 19:02:09 -0800, Jakub Kicinski wrote:
> > On Fri, 22 Nov 2019 07:17:18 +0000, Po Liu wrote:
> > > + if (tc == prio_top) {
> > > + max_interference_size = port_frame_max_size * 8;
> > > + } else {
> > > + u32 m0, ma, r0, ra;
> > > +
> > > + m0 = port_frame_max_size * 8;
> > > + ma = enetc_port_rd(&si->hw, ENETC_PTCMSDUR(prio_top)) * 8;
> > > + ra = enetc_get_cbs_bw(&si->hw, prio_top) *
> > > + port_transmit_rate * 10000ULL;
> > > + r0 = port_transmit_rate * 1000000ULL;
> > > + max_interference_size = m0 + ma + (u64)ra * m0 / (r0 - ra);
> > > + }
> > > +
> > > + /* hiCredit bits calculate by:
> > > + *
> > > + * maxSizedFrame * (idleSlope/portTxRate)
> > > + */
> > > + hi_credit_bit = max_interference_size * bw / 100;
> > > +
> > > + /* hiCredit bits to hiCredit register need to calculated as:
> > > + *
> > > + * (enetClockFrequency / portTransmitRate) * 100
> > > + */
> > > + hi_credit_reg = (ENETC_CLK * 100ULL) * hi_credit_bit
> > > + / (port_transmit_rate * 1000000ULL);
> >
> > Hi! The patch looks good to me, but I'm concerned about those 64bit
> > divisions here. Don't these need to be div_u64() & co.? Otherwise we
> > may see one of the:
> >
> > ERROR: "__udivdi3" [drivers/net/ethernet/freescale/enetc/fsl-enetc.ko]
> undefined!
> >
> > messages from the build bot..
> >
> > I could be wrong, I haven't actually tested..
>
> Yup:
>
> drivers/net/ethernet/freescale/enetc/enetc_qos.o: In function
> `enetc_setup_tc_cbs':
> enetc_qos.c:(.text+0x5b4): undefined reference to `__udivdi3'
> enetc_qos.c:(.text+0x608): undefined reference to `__udivdi3'
> /home/jkicinski/devel/linux/Makefile:1077: recipe for target 'vmlinux' failed
> make[1]: *** [vmlinux] Error 1
> make[1]: Leaving directory '/home/jkicinski/devel/linux/build_tmp2'
> Makefile:179: recipe for target 'sub-make' failed
> make: *** [sub-make] Error 2
>
> Please fix and repost.

Will update to div_u64().

2019-11-25 05:58:31

by Po Liu

[permalink] [raw]
Subject: [v2,net-next] enetc: add support Credit Based Shaper(CBS) for hardware offload

The ENETC hardware support the Credit Based Shaper(CBS) which part
of the IEEE-802.1Qav. The CBS driver was loaded by the sch_cbs
interface when set in the QOS in the kernel.

Here is an example command to set 20Mbits bandwidth in 1Gbits port
for taffic class 7:

tc qdisc add dev eth0 root handle 1: mqprio \
num_tc 8 map 0 1 2 3 4 5 6 7 hw 1

tc qdisc replace dev eth0 parent 1:8 cbs \
locredit -1470 hicredit 30 \
sendslope -980000 idleslope 20000 offload 1

Signed-off-by: Po Liu <[email protected]>
Reviewed-by: Claudiu Manoil <[email protected]>
Reviewed-by: Vladimir Oltean <[email protected]>
---
changes:
v2:
- replace with div_u64() for division suggested by Jakub Kicinski.

drivers/net/ethernet/freescale/enetc/Kconfig | 4 +-
drivers/net/ethernet/freescale/enetc/enetc.c | 2 +
drivers/net/ethernet/freescale/enetc/enetc.h | 2 +
.../net/ethernet/freescale/enetc/enetc_hw.h | 4 +
.../net/ethernet/freescale/enetc/enetc_qos.c | 128 ++++++++++++++++++
5 files changed, 138 insertions(+), 2 deletions(-)

diff --git a/drivers/net/ethernet/freescale/enetc/Kconfig b/drivers/net/ethernet/freescale/enetc/Kconfig
index 491659fe3e35..edad4ca46327 100644
--- a/drivers/net/ethernet/freescale/enetc/Kconfig
+++ b/drivers/net/ethernet/freescale/enetc/Kconfig
@@ -53,10 +53,10 @@ config FSL_ENETC_HW_TIMESTAMPING

config FSL_ENETC_QOS
bool "ENETC hardware Time-sensitive Network support"
- depends on (FSL_ENETC || FSL_ENETC_VF) && NET_SCH_TAPRIO
+ depends on (FSL_ENETC || FSL_ENETC_VF) && (NET_SCH_TAPRIO || NET_SCH_CBS)
help
There are Time-Sensitive Network(TSN) capabilities(802.1Qbv/802.1Qci
/802.1Qbu etc.) supported by ENETC. These TSN capabilities can be set
enable/disable from user space via Qos commands(tc). In the kernel
side, it can be loaded by Qos driver. Currently, it is only support
- taprio(802.1Qbv).
+ taprio(802.1Qbv) and Credit Based Shaper(802.1Qbu).
diff --git a/drivers/net/ethernet/freescale/enetc/enetc.c b/drivers/net/ethernet/freescale/enetc/enetc.c
index 27f6fd1708f0..9db1b96ed9b9 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc.c
@@ -1496,6 +1496,8 @@ int enetc_setup_tc(struct net_device *ndev, enum tc_setup_type type,
return enetc_setup_tc_mqprio(ndev, type_data);
case TC_SETUP_QDISC_TAPRIO:
return enetc_setup_tc_taprio(ndev, type_data);
+ case TC_SETUP_QDISC_CBS:
+ return enetc_setup_tc_cbs(ndev, type_data);
default:
return -EOPNOTSUPP;
}
diff --git a/drivers/net/ethernet/freescale/enetc/enetc.h b/drivers/net/ethernet/freescale/enetc/enetc.h
index 89f23156f330..7ee0da6d0015 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc.h
@@ -255,7 +255,9 @@ int enetc_send_cmd(struct enetc_si *si, struct enetc_cbd *cbd);
#ifdef CONFIG_FSL_ENETC_QOS
int enetc_setup_tc_taprio(struct net_device *ndev, void *type_data);
void enetc_sched_speed_set(struct net_device *ndev);
+int enetc_setup_tc_cbs(struct net_device *ndev, void *type_data);
#else
#define enetc_setup_tc_taprio(ndev, type_data) -EOPNOTSUPP
#define enetc_sched_speed_set(ndev) (void)0
+#define enetc_setup_tc_cbs(ndev, type_data) -EOPNOTSUPP
#endif
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_hw.h b/drivers/net/ethernet/freescale/enetc/enetc_hw.h
index 924ddb6d358a..51f543ef37a8 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_hw.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_hw.h
@@ -185,6 +185,8 @@ enum enetc_bdr_type {TX, RX};
#define ENETC_PSICFGR0_SIVC(bmp) (((bmp) & 0xff) << 24) /* VLAN_TYPE */

#define ENETC_PTCCBSR0(n) (0x1110 + (n) * 8) /* n = 0 to 7*/
+#define ENETC_CBSE BIT(31)
+#define ENETC_CBS_BW_MASK GENMASK(6, 0)
#define ENETC_PTCCBSR1(n) (0x1114 + (n) * 8) /* n = 0 to 7*/
#define ENETC_RSSHASH_KEY_SIZE 40
#define ENETC_PRSSK(n) (0x1410 + (n) * 4) /* n = [0..9] */
@@ -603,6 +605,8 @@ struct enetc_cbd {
u8 status_flags;
};

+#define ENETC_CLK 400000000ULL
+
/* port time gating control register */
#define ENETC_QBV_PTGCR_OFFSET 0x11a00
#define ENETC_QBV_TGE BIT(31)
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_qos.c b/drivers/net/ethernet/freescale/enetc/enetc_qos.c
index 66a3da61ca16..2e99438cb1bf 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_qos.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_qos.c
@@ -4,6 +4,7 @@
#include "enetc.h"

#include <net/pkt_sched.h>
+#include <linux/math64.h>

static u16 enetc_get_max_gcl_len(struct enetc_hw *hw)
{
@@ -170,3 +171,130 @@ int enetc_setup_tc_taprio(struct net_device *ndev, void *type_data)

return err;
}
+
+static u32 enetc_get_cbs_enable(struct enetc_hw *hw, u8 tc)
+{
+ return enetc_port_rd(hw, ENETC_PTCCBSR0(tc)) & ENETC_CBSE;
+}
+
+static u8 enetc_get_cbs_bw(struct enetc_hw *hw, u8 tc)
+{
+ return enetc_port_rd(hw, ENETC_PTCCBSR0(tc)) & ENETC_CBS_BW_MASK;
+}
+
+int enetc_setup_tc_cbs(struct net_device *ndev, void *type_data)
+{
+ struct enetc_ndev_priv *priv = netdev_priv(ndev);
+ struct tc_cbs_qopt_offload *cbs = type_data;
+ u32 port_transmit_rate = priv->speed;
+ u8 tc_nums = netdev_get_num_tc(ndev);
+ struct enetc_si *si = priv->si;
+ u32 hi_credit_bit, hi_credit_reg;
+ u32 max_interference_size;
+ u32 port_frame_max_size;
+ u32 tc_max_sized_frame;
+ u8 tc = cbs->queue;
+ u8 prio_top, prio_next;
+ int bw_sum = 0;
+ u8 bw;
+
+ prio_top = netdev_get_prio_tc_map(ndev, tc_nums - 1);
+ prio_next = netdev_get_prio_tc_map(ndev, tc_nums - 2);
+
+ /* Support highest prio and second prio tc in cbs mode */
+ if (tc != prio_top && tc != prio_next)
+ return -EOPNOTSUPP;
+
+ if (!cbs->enable) {
+ /* Make sure the other TC that are numerically
+ * lower than this TC have been disabled.
+ */
+ if (tc == prio_top &&
+ enetc_get_cbs_enable(&si->hw, prio_next)) {
+ dev_err(&ndev->dev,
+ "Disable TC%d before disable TC%d\n",
+ prio_next, tc);
+ return -EINVAL;
+ }
+
+ enetc_port_wr(&si->hw, ENETC_PTCCBSR1(tc), 0);
+ enetc_port_wr(&si->hw, ENETC_PTCCBSR0(tc), 0);
+
+ return 0;
+ }
+
+ if (cbs->idleslope - cbs->sendslope != port_transmit_rate * 1000L ||
+ cbs->idleslope < 0 || cbs->sendslope > 0)
+ return -EOPNOTSUPP;
+
+ port_frame_max_size = ndev->mtu + VLAN_ETH_HLEN + ETH_FCS_LEN;
+
+ bw = cbs->idleslope / (port_transmit_rate * 10UL);
+
+ /* Make sure the other TC that are numerically
+ * higher than this TC have been enabled.
+ */
+ if (tc == prio_next) {
+ if (!enetc_get_cbs_enable(&si->hw, prio_top)) {
+ dev_err(&ndev->dev,
+ "Enable TC%d first before enable TC%d\n",
+ prio_top, prio_next);
+ return -EINVAL;
+ }
+ bw_sum += enetc_get_cbs_bw(&si->hw, prio_top);
+ }
+
+ if (bw_sum + bw >= 100) {
+ dev_err(&ndev->dev,
+ "The sum of all CBS Bandwidth can't exceed 100\n");
+ return -EINVAL;
+ }
+
+ tc_max_sized_frame = enetc_port_rd(&si->hw, ENETC_PTCMSDUR(tc));
+
+ /* For top prio TC, the max_interfrence_size is maxSizedFrame.
+ *
+ * For next prio TC, the max_interfrence_size is calculated as below:
+ *
+ * max_interference_size = M0 + Ma + Ra * M0 / (R0 - Ra)
+ *
+ * - RA: idleSlope for AVB Class A
+ * - R0: port transmit rate
+ * - M0: maximum sized frame for the port
+ * - MA: maximum sized frame for AVB Class A
+ */
+
+ if (tc == prio_top) {
+ max_interference_size = port_frame_max_size * 8;
+ } else {
+ u32 m0, ma, r0, ra;
+
+ m0 = port_frame_max_size * 8;
+ ma = enetc_port_rd(&si->hw, ENETC_PTCMSDUR(prio_top)) * 8;
+ ra = enetc_get_cbs_bw(&si->hw, prio_top) *
+ port_transmit_rate * 10000ULL;
+ r0 = port_transmit_rate * 1000000ULL;
+ max_interference_size = m0 + ma +
+ (u32)div_u64((u64)ra * m0, r0 - ra);
+ }
+
+ /* hiCredit bits calculate by:
+ *
+ * maxSizedFrame * (idleSlope/portTxRate)
+ */
+ hi_credit_bit = max_interference_size * bw / 100;
+
+ /* hiCredit bits to hiCredit register need to calculated as:
+ *
+ * (enetClockFrequency / portTransmitRate) * 100
+ */
+ hi_credit_reg = (u32)div_u64((ENETC_CLK * 100ULL) * hi_credit_bit,
+ port_transmit_rate * 1000000ULL);
+
+ enetc_port_wr(&si->hw, ENETC_PTCCBSR1(tc), hi_credit_reg);
+
+ /* Set bw register and enable this traffic class */
+ enetc_port_wr(&si->hw, ENETC_PTCCBSR0(tc), bw | ENETC_CBSE);
+
+ return 0;
+}
--
2.17.1

2019-11-25 18:58:20

by David Miller

[permalink] [raw]
Subject: Re: [v2,net-next] enetc: add support Credit Based Shaper(CBS) for hardware offload

From: Po Liu <[email protected]>
Date: Mon, 25 Nov 2019 05:56:56 +0000

> The ENETC hardware support the Credit Based Shaper(CBS) which part
> of the IEEE-802.1Qav. The CBS driver was loaded by the sch_cbs
> interface when set in the QOS in the kernel.
>
> Here is an example command to set 20Mbits bandwidth in 1Gbits port
> for taffic class 7:
>
> tc qdisc add dev eth0 root handle 1: mqprio \
> num_tc 8 map 0 1 2 3 4 5 6 7 hw 1
>
> tc qdisc replace dev eth0 parent 1:8 cbs \
> locredit -1470 hicredit 30 \
> sendslope -980000 idleslope 20000 offload 1
>
> Signed-off-by: Po Liu <[email protected]>
> Reviewed-by: Claudiu Manoil <[email protected]>
> Reviewed-by: Vladimir Oltean <[email protected]>

Applied, thanks.