can: mcp251xfd: fix infinite loop when xmit fails
Subsystems (5)
PATCH v6 1/1 can: mcp251xfd: fix infinite loop when xmit fails
From: Vitor Soares <[email protected]>
When the mcp251xfd_start_xmit() function fails, the driver stops processing messages, and the interrupt routine does not return, running indefinitely even after killing the running application.
Error messages:
[ 441.298819] mcp251xfd spi2.0 can0: ERROR in mcp251xfd_start_xmit: -16 [ 441.306498] mcp251xfd spi2.0 can0: Transmit Event FIFO buffer not empty. (seq=0x000017c7, tef_tail=0x000017cf, tef_head=0x000017d0, tx_head=0x000017d3).
... and repeat forever.
The issue can be triggered when multiple devices share the same SPI interface. And there is concurrent access to the bus.
The problem occurs because tx_ring->head increments even if mcp251xfd_start_xmit() fails. Consequently, the driver skips one TX package while still expecting a response in mcp251xfd_handle_tefif_one().
This patch resolves the issue by starting a workqueue to write the tx obj synchronously if err = -EBUSY. In case of another error, it decrements tx_ring->head, removes skb from the echo stack, and drops the message.
---
v5->v6
- Move alloc/destroy workqueue to open()/stop() callbacks.
- Change workqueue to workqueue ordered.
- Return NETDEV_TX_BUSY if the worker is busy, replacing the previouse priv->tx_work_obj = NULL. With this change, setting priv->tx_work_obj = NULL is not necessary anymore.
V4->V5:
- Start a workqueue to write tx obj with spi_sync() when spi_async() == -EBUSY.
V3->V4:
- Leave can_put_echo_skb() and stop the queue if needed, before mcp251xfd_tx_obj_write().
- Re-sync head and remove echo skb if mcp251xfd_tx_obj_write() fails.
- Revert -> return NETDEV_TX_BUSY if mcp251xfd_tx_obj_write() == -EBUSY.
V2->V3:
- Add tx_dropped stats.
- netdev_sent_queue() only if can_put_echo_skb() succeed.
V1->V2:
- Return NETDEV_TX_BUSY if mcp251xfd_tx_obj_write() == -EBUSY.
- Rework the commit message to address the change above.
- Change can_put_echo_skb() to be called after mcp251xfd_tx_obj_write() succeed. Otherwise, we get Kernel NULL pointer dereference error.
.../net/can/spi/mcp251xfd/mcp251xfd-core.c | 14 ++++- drivers/net/can/spi/mcp251xfd/mcp251xfd-tx.c | 55 ++++++++++++++++--- drivers/net/can/spi/mcp251xfd/mcp251xfd.h | 5 ++ 3 files changed, 65 insertions(+), 9 deletions(-)
drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c +13 -1
@@ -1618,11 +1618,20 @@ static int mcp251xfd_open(struct net_device *ndev)clear_bit (MCP251XFD_FLAGS_DOWN , priv->flags);can_rx_offload_enable (&priv->offload); + priv->wq =alloc_ordered_workqueue ("%s-mcp251xfd_wq" , +WQ_FREEZABLE |WQ_MEM_RECLAIM , +dev_name (&spi->dev)); +if (!priv->wq) { + err = -ENOMEM ; + goto out_can_rx_offload_disable; + } +INIT_WORK (&priv->tx_work, mcp251xfd_tx_obj_write_sync); + err =request_threaded_irq (spi->irq,NULL , mcp251xfd_irq,IRQF_SHARED |IRQF_ONESHOT ,dev_name (&spi->dev), priv);if (err) - goto out_can_rx_offload_disable; + goto out_destroy_workqueue; err =mcp251xfd_chip_interrupts_enable (priv);if (err)
@@ -1634,6 +1643,8 @@ static int mcp251xfd_open(struct net_device *ndev) out_free_irq:free_irq (spi->irq, priv); + out_destroy_workqueue: +destroy_workqueue (priv->wq); out_can_rx_offload_disable:can_rx_offload_disable (&priv->offload);set_bit (MCP251XFD_FLAGS_DOWN , priv->flags);
@@ -1661,6 +1672,7 @@ static int mcp251xfd_stop(struct net_device *ndev)hrtimer_cancel (&priv->tx_irq_timer);mcp251xfd_chip_interrupts_disable (priv);free_irq (ndev->irq, priv); +destroy_workqueue (priv->wq);can_rx_offload_disable (&priv->offload);mcp251xfd_timestamp_stop (priv);mcp251xfd_chip_stop (priv,CAN_STATE_STOPPED );
drivers/net/can/spi/mcp251xfd/mcp251xfd-tx.c +47 -8
79 lines not on this page · raw message
drivers/net/can/spi/mcp251xfd/mcp251xfd.h +5 -0
@@ -633,6 +633,10 @@ struct mcp251xfd_priv {struct mcp251xfd_rx_ring *rx[MCP251XFD_FIFO_RX_NUM ];struct mcp251xfd_tx_ring tx[MCP251XFD_FIFO_TX_NUM ]; +struct workqueue_struct *wq; +struct work_struct tx_work; +struct mcp251xfd_tx_obj *tx_work_obj; +DECLARE_BITMAP (flags, __MCP251XFD_FLAGS_SIZE__);u8 rx_ring_num;
@@ -952,6 +956,7 @@ void mcp251xfd_skb_set_timestamp(const struct mcp251xfd_priv *priv,void mcp251xfd_timestamp_init (struct mcp251xfd_priv *priv);void mcp251xfd_timestamp_stop (struct mcp251xfd_priv *priv); +void mcp251xfd_tx_obj_write_sync (struct work_struct *work);netdev_tx_t mcp251xfd_start_xmit (struct sk_buff *skb,struct net_device *ndev);
Signature · 1 line
2.34.1
…
Quoted 1 line
This patch resolves the issue by starting …
Will further imperative wordings be more desirable for an improved change description?
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/process/submitting-patches.rst?h=v6.9#n94
Regards,
Markus
Quoted 9 lines · Markus Elfring
…
Quoted 1 line
This patch resolves the issue by starting …
Will further imperative wordings be more desirable for an improved change description?
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/process/submitting-patches.rst?h=v6.9#n94Regards,
Markus
Hi,
This is the semi-friendly patch-bot of Greg Kroah-Hartman.
Markus, you seem to have sent a nonsensical or otherwise pointless review comment to a patch submission on a Linux kernel developer mailing list. I strongly suggest that you not do this anymore. Please do not bother developers who are actively working to produce patches and features with comments that, in the end, are a waste of time.
Patch submitter, please ignore Markus's suggestion; you do not need to follow it at all. The person/bot/AI that sent it is being ignored by almost all Linux kernel maintainers for having a persistent pattern of behavior of producing distracting and pointless commentary, and inability to adapt to feedback. Please feel free to also ignore emails from them.
thanks,
greg k-h's patch email bot
Marc Kleine-Budde
maintainerQuoted 19 lines · Vitor Soares
From: Vitor Soares <[email protected]>
When the
mcp251xfd_start_xmit()function fails, the driver stops processing messages, and the interrupt routine does not return, running indefinitely even after killing the running application.Error messages:
[ 441.298819] mcp251xfd spi2.0 can0: ERROR in mcp251xfd_start_xmit: -16 [ 441.306498] mcp251xfd spi2.0 can0: Transmit Event FIFO buffer not empty. (seq=0x000017c7, tef_tail=0x000017cf, tef_head=0x000017d0, tx_head=0x000017d3).... and repeat forever.
The issue can be triggered when multiple devices share the same SPI interface. And there is concurrent access to the bus.
The problem occurs because
tx_ring->headincrements even ifmcp251xfd_start_xmit()fails. Consequently, the driver skips one TX package while still expecting a response inmcp251xfd_handle_tefif_one().
Quoted 8 lines
This patch resolves the issue by starting a workqueue to write the tx obj synchronously if err =
-EBUSY. In case of another error, it decrementstx_ring->head, removes skb from the echo stack, and drops the message.Fixes 55e5b97f003e ("can: mcp25xxfd: add driver for Microchip MCP25xxFD SPI CAN")Signed-off-by Vitor Soares <[email protected]>
I've reworked the last sentence for a bit and applied to linux-can.
Thanks,
Marc
Signature · 4 lines
Pengutronix e.K. | Marc Kleine-Budde | Embedded Linux | https://www.pengutronix.de | Vertretung Nürnberg | Phone: +49-5121-206917-129 | Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-9 |