diff mbox series

[net,v2,1/1] net: stmmac: Prevent DSA tags from breaking COE on stmmac

Message ID 20240102162718.268271-2-romain.gantois@bootlin.com (mailing list archive)
State New, archived
Headers show
Series Prevent DSA tags from breaking COE | expand

Commit Message

Romain Gantois Jan. 2, 2024, 4:27 p.m. UTC
Some DSA tagging protocols change the EtherType field in the MAC header
e.g.  DSA_TAG_PROTO_(DSA/EDSA/BRCM/MTK/RTL4C_A/SJA1105). On TX these tagged
frames are ignored by the checksum offload engine and IP header checker of
some stmmac cores.

On RX, the stmmac driver wrongly assumes that checksums have been computed
for these tagged packets, and sets CHECKSUM_UNNECESSARY.

Add an additional check in the stmmac tx and rx hotpaths so that COE is
deactivated for packets with ethertypes that will not trigger the COE and
ip header checks.

Fixes: 6b2c6e4a938f ("net: stmmac: propagate feature flags to vlan")
Cc: stable@vger.kernel.org
Reported-by: Richard Tresidder <rtresidd@electromag.com.au>
Closes: https://lore.kernel.org/netdev/e5c6c75f-2dfa-4e50-a1fb-6bf4cdb617c2@electromag.com.au/
Reported-by: Romain Gantois <romain.gantois@bootlin.com>
Closes: https://lore.kernel.org/netdev/c57283ed-6b9b-b0e6-ee12-5655c1c54495@bootlin.com/
Signed-off-by: Romain Gantois <romain.gantois@bootlin.com>
---
 .../net/ethernet/stmicro/stmmac/stmmac_main.c | 21 ++++++++++++++++---
 1 file changed, 18 insertions(+), 3 deletions(-)

Comments

Florian Fainelli Jan. 2, 2024, 8:26 p.m. UTC | #1
On 1/2/24 08:27, Romain Gantois wrote:
> Some DSA tagging protocols change the EtherType field in the MAC header
> e.g.  DSA_TAG_PROTO_(DSA/EDSA/BRCM/MTK/RTL4C_A/SJA1105). On TX these tagged
> frames are ignored by the checksum offload engine and IP header checker of
> some stmmac cores.
> 
> On RX, the stmmac driver wrongly assumes that checksums have been computed
> for these tagged packets, and sets CHECKSUM_UNNECESSARY.
> 
> Add an additional check in the stmmac tx and rx hotpaths so that COE is
> deactivated for packets with ethertypes that will not trigger the COE and
> ip header checks.
> 
> Fixes: 6b2c6e4a938f ("net: stmmac: propagate feature flags to vlan")
> Cc: stable@vger.kernel.org
> Reported-by: Richard Tresidder <rtresidd@electromag.com.au>
> Closes: https://lore.kernel.org/netdev/e5c6c75f-2dfa-4e50-a1fb-6bf4cdb617c2@electromag.com.au/
> Reported-by: Romain Gantois <romain.gantois@bootlin.com>
> Closes: https://lore.kernel.org/netdev/c57283ed-6b9b-b0e6-ee12-5655c1c54495@bootlin.com/

Fairly sure those should be Link: and Closes: should be used for bug 
tracker entries.

> Signed-off-by: Romain Gantois <romain.gantois@bootlin.com>
> ---
>   .../net/ethernet/stmicro/stmmac/stmmac_main.c | 21 ++++++++++++++++---
>   1 file changed, 18 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
> index 37e64283f910..bb2ae6b32b2f 100644
> --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
> +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
> @@ -4371,6 +4371,17 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
>   	return NETDEV_TX_OK;
>   }
>   
> +/* Check if ethertype will trigger IP
> + * header checks/COE in hardware
> + */
> +static inline bool stmmac_has_ip_ethertype(struct sk_buff *skb)
> +{
> +	__be16 proto = eth_header_parse_protocol(skb);
> +
> +	return (proto == htons(ETH_P_IP)) || (proto == htons(ETH_P_IPV6)) ||
> +		(proto == htons(ETH_P_8021Q));

Do you need to include ETH_P_8021AD in that list as well or is not 
stmmac capable of checksuming beyond a single VLAN tag?

Thanks!
Linus Walleij Jan. 2, 2024, 8:27 p.m. UTC | #2
Hi Romain,

overall this patch makes sense.

On Tue, Jan 2, 2024 at 5:27 PM Romain Gantois
<romain.gantois@bootlin.com> wrote:

> Some DSA tagging protocols change the EtherType field in the MAC header
> e.g.  DSA_TAG_PROTO_(DSA/EDSA/BRCM/MTK/RTL4C_A/SJA1105). On TX these tagged
> frames are ignored by the checksum offload engine and IP header checker of
> some stmmac cores.
>
> On RX, the stmmac driver wrongly assumes that checksums have been computed
> for these tagged packets, and sets CHECKSUM_UNNECESSARY.
>
> Add an additional check in the stmmac tx and rx hotpaths so that COE is
> deactivated for packets with ethertypes that will not trigger the COE and
> ip header checks.
>
> Fixes: 6b2c6e4a938f ("net: stmmac: propagate feature flags to vlan")
> Cc: stable@vger.kernel.org
> Reported-by: Richard Tresidder <rtresidd@electromag.com.au>
> Closes: https://lore.kernel.org/netdev/e5c6c75f-2dfa-4e50-a1fb-6bf4cdb617c2@electromag.com.au/
> Reported-by: Romain Gantois <romain.gantois@bootlin.com>
> Closes: https://lore.kernel.org/netdev/c57283ed-6b9b-b0e6-ee12-5655c1c54495@bootlin.com/
> Signed-off-by: Romain Gantois <romain.gantois@bootlin.com>
(...)

> +static inline bool stmmac_has_ip_ethertype(struct sk_buff *skb)
> +{
> +       __be16 proto = eth_header_parse_protocol(skb);

I made a new function for this in my patch
https://lore.kernel.org/netdev/20231222-new-gemini-ethernet-regression-v4-2-a36e71b0f32b@linaro.org/

I was careful to add if (!pskb_may_pull(skb, ETH_HLEN)) because Eric
was very specific about this, I suppose you could get fragment frames that
are smaller than an ethernet header.

I should use eth_header_parse_protocol() instead of reimplementing it
though. :/

Should we add an if (!pskb_may_pull(skb, ETH_HLEN)) to
eth_header_parse_protocol()?

Yours,
Linus Walleij
Romain Gantois Jan. 3, 2024, 8:40 a.m. UTC | #3
Hi Florian,

On Tue, 2 Jan 2024, Florian Fainelli wrote:

...
> > Fixes: 6b2c6e4a938f ("net: stmmac: propagate feature flags to vlan")
> > Cc: stable@vger.kernel.org
> > Reported-by: Richard Tresidder <rtresidd@electromag.com.au>
> > Closes:
> > https://lore.kernel.org/netdev/e5c6c75f-2dfa-4e50-a1fb-6bf4cdb617c2@electromag.com.au/
> > Reported-by: Romain Gantois <romain.gantois@bootlin.com>
> > Closes:
> > https://lore.kernel.org/netdev/c57283ed-6b9b-b0e6-ee12-5655c1c54495@bootlin.com/
> 
> Fairly sure those should be Link: and Closes: should be used for bug tracker
> entries.

ACK

> > +	return (proto == htons(ETH_P_IP)) || (proto == htons(ETH_P_IPV6)) ||
> > +		(proto == htons(ETH_P_8021Q));
> 
> Do you need to include ETH_P_8021AD in that list as well or is not stmmac
> capable of checksuming beyond a single VLAN tag?

The datasheet for my Ethernet controller doesn't mention 802.1ad tag 
handling and I ran some loopback tests that showed that this controller doesn't 
recognize 802.1ad frames as vlan frames. I also haven't seen anything in the 
stmmac driver that suggests that 802.1ad offloading is supported. Maybe the 
stmmac maintainers could weigh in on this?

Best Regards,
Romain Gantois Jan. 3, 2024, 9:11 a.m. UTC | #4
Hi Linus,

On Tue, 2 Jan 2024, Linus Walleij wrote:
...
> > +static inline bool stmmac_has_ip_ethertype(struct sk_buff *skb)
> > +{
> > +       __be16 proto = eth_header_parse_protocol(skb);
> 
> I made a new function for this in my patch
> https://lore.kernel.org/netdev/20231222-new-gemini-ethernet-regression-v4-2-a36e71b0f32b@linaro.org/
> 
> I was careful to add if (!pskb_may_pull(skb, ETH_HLEN)) because Eric
> was very specific about this, I suppose you could get fragment frames that
> are smaller than an ethernet header.

Okay nice, then I'll rewrite this series to use the new function once your 
changes make it in.

> Should we add an if (!pskb_may_pull(skb, ETH_HLEN)) to
> eth_header_parse_protocol()?
That does sound logical to me but I couldn't tell you what the impact on current 
callers would be. The net maintainers will probably have a better idea of this.

Best Regards,
Linus Walleij Jan. 3, 2024, 4:42 p.m. UTC | #5
On Wed, Jan 3, 2024 at 10:11 AM Romain Gantois
<romain.gantois@bootlin.com> wrote:
> On Tue, 2 Jan 2024, Linus Walleij wrote:
> ...
> > > +static inline bool stmmac_has_ip_ethertype(struct sk_buff *skb)
> > > +{
> > > +       __be16 proto = eth_header_parse_protocol(skb);
> >
> > I made a new function for this in my patch
> > https://lore.kernel.org/netdev/20231222-new-gemini-ethernet-regression-v4-2-a36e71b0f32b@linaro.org/
> >
> > I was careful to add if (!pskb_may_pull(skb, ETH_HLEN)) because Eric
> > was very specific about this, I suppose you could get fragment frames that
> > are smaller than an ethernet header.
>
> Okay nice, then I'll rewrite this series to use the new function once your
> changes make it in.

I just rewrote my patch to use eth_header_parse_protocol() instead.
I should not invent a new version of something that already exist.

> > Should we add an if (!pskb_may_pull(skb, ETH_HLEN)) to
> > eth_header_parse_protocol()?
>
> That does sound logical to me but I couldn't tell you what the impact on current
> callers would be. The net maintainers will probably have a better idea of this.

I can propose a separate patch for this with RFC.

Yours,
Linus Walleij
Vladimir Oltean Jan. 3, 2024, 8:10 p.m. UTC | #6
On Tue, Jan 02, 2024 at 05:27:15PM +0100, Romain Gantois wrote:
> +/* Check if ethertype will trigger IP
> + * header checks/COE in hardware
> + */
> +static inline bool stmmac_has_ip_ethertype(struct sk_buff *skb)
> +{
> +	__be16 proto = eth_header_parse_protocol(skb);
> +
> +	return (proto == htons(ETH_P_IP)) || (proto == htons(ETH_P_IPV6)) ||
> +		(proto == htons(ETH_P_8021Q));

proto == htons(ETH_P_8021Q) means that the skb has an IP EtherType?
What if an IP header does not follow after the VLAN header?

> +}
Linus Walleij Jan. 3, 2024, 10:25 p.m. UTC | #7
On Wed, Jan 3, 2024 at 9:10 PM Vladimir Oltean <olteanv@gmail.com> wrote:
> On Tue, Jan 02, 2024 at 05:27:15PM +0100, Romain Gantois wrote:
> > +/* Check if ethertype will trigger IP
> > + * header checks/COE in hardware
> > + */
> > +static inline bool stmmac_has_ip_ethertype(struct sk_buff *skb)
> > +{
> > +     __be16 proto = eth_header_parse_protocol(skb);
> > +
> > +     return (proto == htons(ETH_P_IP)) || (proto == htons(ETH_P_IPV6)) ||
> > +             (proto == htons(ETH_P_8021Q));
>
> proto == htons(ETH_P_8021Q) means that the skb has an IP EtherType?
> What if an IP header does not follow after the VLAN header?

It's probably best to do like I do here:
https://lore.kernel.org/netdev/20240102-new-gemini-ethernet-regression-v5-2-cf61ab3aa8cd@linaro.org/

+ if (ethertype == ETH_P_8021Q)
+   ethertype = ntohs(__vlan_get_protocol(skb, htons(ethertype), NULL));

Yours,
Linus Walleij
diff mbox series

Patch

diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index 37e64283f910..bb2ae6b32b2f 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -4371,6 +4371,17 @@  static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
 	return NETDEV_TX_OK;
 }
 
+/* Check if ethertype will trigger IP
+ * header checks/COE in hardware
+ */
+static inline bool stmmac_has_ip_ethertype(struct sk_buff *skb)
+{
+	__be16 proto = eth_header_parse_protocol(skb);
+
+	return (proto == htons(ETH_P_IP)) || (proto == htons(ETH_P_IPV6)) ||
+		(proto == htons(ETH_P_8021Q));
+}
+
 /**
  *  stmmac_xmit - Tx entry point of the driver
  *  @skb : the socket buffer
@@ -4435,9 +4446,13 @@  static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
 	/* DWMAC IPs can be synthesized to support tx coe only for a few tx
 	 * queues. In that case, checksum offloading for those queues that don't
 	 * support tx coe needs to fallback to software checksum calculation.
+	 *
+	 * Packets that won't trigger the COE e.g. most DSA-tagged packets will
+	 * also have to be checksummed in software.
 	 */
 	if (csum_insertion &&
-	    priv->plat->tx_queues_cfg[queue].coe_unsupported) {
+	    (priv->plat->tx_queues_cfg[queue].coe_unsupported ||
+	    !stmmac_has_ip_ethertype(skb))) {
 		if (unlikely(skb_checksum_help(skb)))
 			goto dma_map_err;
 		csum_insertion = !csum_insertion;
@@ -4997,7 +5012,7 @@  static void stmmac_dispatch_skb_zc(struct stmmac_priv *priv, u32 queue,
 	stmmac_rx_vlan(priv->dev, skb);
 	skb->protocol = eth_type_trans(skb, priv->dev);
 
-	if (unlikely(!coe))
+	if (unlikely(!coe) || !stmmac_has_ip_ethertype(skb))
 		skb_checksum_none_assert(skb);
 	else
 		skb->ip_summed = CHECKSUM_UNNECESSARY;
@@ -5513,7 +5528,7 @@  static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
 		stmmac_rx_vlan(priv->dev, skb);
 		skb->protocol = eth_type_trans(skb, priv->dev);
 
-		if (unlikely(!coe))
+		if (unlikely(!coe) || !stmmac_has_ip_ethertype(skb))
 			skb_checksum_none_assert(skb);
 		else
 			skb->ip_summed = CHECKSUM_UNNECESSARY;