[lkml]   [2020]   [Jan]   [28]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
Messages in this thread
Patch in this message
Subject[PATCH 5.4 062/104] mlxsw: switchx2: Do not modify cloned SKBs during xmit
From: Ido Schimmel <>

commit 63963d0f9d17be83d0e419e03282847ecc2c3715 upstream.

The driver needs to prepend a Tx header to each packet it is
transmitting. The header includes information such as the egress port
and traffic class.

The addition of the header requires the driver to modify the SKB's
header and therefore it must not be shared. Otherwise, we risk hitting
various race conditions.

For example, when a packet is flooded (cloned) by the bridge driver to
two switch ports swp1 and swp2:

t0 - mlxsw_sp_port_xmit() is called for swp1. Tx header is prepended with
swp1's port number
t1 - mlxsw_sp_port_xmit() is called for swp2. Tx header is prepended with
swp2's port number, overwriting swp1's port number
t2 - The device processes data buffer from t0. Packet is transmitted via
t3 - The device processes data buffer from t1. Packet is transmitted via

Usually, the device is fast enough and transmits the packet before its
Tx header is overwritten, but this is not the case in emulated

Fix this by making sure the SKB's header is writable by calling
skb_cow_head(). Since the function ensures we have headroom to push the
Tx header, the check further in the function can be removed.

* Use skb_cow_head() instead of skb_unshare() as suggested by Jakub
* Remove unnecessary check regarding headroom

Fixes: 31557f0f9755 ("mlxsw: Introduce Mellanox SwitchX-2 ASIC support")
Signed-off-by: Ido Schimmel <>
Reported-by: Shalom Toledo <>
Acked-by: Jiri Pirko <>
Signed-off-by: David S. Miller <>
Signed-off-by: Greg Kroah-Hartman <>

drivers/net/ethernet/mellanox/mlxsw/switchx2.c | 17 ++++++-----------
1 file changed, 6 insertions(+), 11 deletions(-)

--- a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c
@@ -299,22 +299,17 @@ static netdev_tx_t mlxsw_sx_port_xmit(st
u64 len;
int err;

+ if (skb_cow_head(skb, MLXSW_TXHDR_LEN)) {
+ this_cpu_inc(mlxsw_sx_port->pcpu_stats->tx_dropped);
+ dev_kfree_skb_any(skb);
+ return NETDEV_TX_OK;
+ }
memset(skb->cb, 0, sizeof(struct mlxsw_skb_cb));

if (mlxsw_core_skb_transmit_busy(mlxsw_sx->core, &tx_info))

- if (unlikely(skb_headroom(skb) < MLXSW_TXHDR_LEN)) {
- struct sk_buff *skb_orig = skb;
- skb = skb_realloc_headroom(skb, MLXSW_TXHDR_LEN);
- if (!skb) {
- this_cpu_inc(mlxsw_sx_port->pcpu_stats->tx_dropped);
- dev_kfree_skb_any(skb_orig);
- return NETDEV_TX_OK;
- }
- dev_consume_skb_any(skb_orig);
- }
mlxsw_sx_txhdr_construct(skb, &tx_info);
/* TX header is consumed by HW on the way so we shouldn't count its
* bytes as being sent.

 \ /
  Last update: 2020-01-28 15:04    [W:0.274 / U:6.868 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site