Message ID | 20191122070321.20915-1-Po.Liu@nxp.com |
---|---|
State | Changes Requested |
Delegated to: | David Miller |
Headers | show |
Series | [net-next] enetc: add support Credit Based Shaper(CBS) for hardware offload | expand |
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..
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.
Hi Jakub, Thanks! Br, Po Liu > -----Original Message----- > From: Jakub Kicinski <jakub.kicinski@netronome.com> > Sent: 2019年11月25日 7:35 > To: Po Liu <po.liu@nxp.com> > Cc: davem@davemloft.net; linux-kernel@vger.kernel.org; > netdev@vger.kernel.org; vinicius.gomes@intel.com; Claudiu Manoil > <claudiu.manoil@nxp.com>; Vladimir Oltean <vladimir.oltean@nxp.com>; > Alexandru Marginean <alexandru.marginean@nxp.com>; Xiaoliang Yang > <xiaoliang.yang_1@nxp.com>; Roy Zang <roy.zang@nxp.com>; Mingkai Hu > <mingkai.hu@nxp.com>; Jerry Huang <jerry.huang@nxp.com>; Leo Li > <leoyang.li@nxp.com> > 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().
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; +}