| From 8bcdf89cbad36217af34fc7ca7d1287154234ce0 Mon Sep 17 00:00:00 2001 |
| From: Ido Schimmel <idosch@mellanox.com> |
| Date: Wed, 15 Jan 2020 13:53:45 +0200 |
| Subject: [PATCH] mlxsw: spectrum: Do not modify cloned SKBs during xmit |
| |
| commit 2da51ce75d86ab1f7770ac1391a9a1697ddaa60c 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 |
| swp2 |
| t3 - The device processes data buffer from t1. Packet is transmitted via |
| swp2 |
| |
| Usually, the device is fast enough and transmits the packet before its |
| Tx header is overwritten, but this is not the case in emulated |
| environments. |
| |
| 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. |
| |
| v2: |
| * Use skb_cow_head() instead of skb_unshare() as suggested by Jakub |
| * Remove unnecessary check regarding headroom |
| |
| Fixes: 56ade8fe3fe1 ("mlxsw: spectrum: Add initial support for Spectrum ASIC") |
| Signed-off-by: Ido Schimmel <idosch@mellanox.com> |
| Reported-by: Shalom Toledo <shalomt@mellanox.com> |
| Acked-by: Jiri Pirko <jiri@mellanox.com> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c |
| index e1a9ce611387..da260149c0ad 100644 |
| --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c |
| +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c |
| @@ -738,23 +738,17 @@ static netdev_tx_t mlxsw_sp_port_xmit(struct sk_buff *skb, |
| u64 len; |
| int err; |
| |
| + if (skb_cow_head(skb, MLXSW_TXHDR_LEN)) { |
| + this_cpu_inc(mlxsw_sp_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_sp->core, &tx_info)) |
| return NETDEV_TX_BUSY; |
| |
| - 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_sp_port->pcpu_stats->tx_dropped); |
| - dev_kfree_skb_any(skb_orig); |
| - return NETDEV_TX_OK; |
| - } |
| - dev_consume_skb_any(skb_orig); |
| - } |
| - |
| if (eth_skb_pad(skb)) { |
| this_cpu_inc(mlxsw_sp_port->pcpu_stats->tx_dropped); |
| return NETDEV_TX_OK; |
| -- |
| 2.7.4 |
| |