RE: [PATCH] can: m_can: Enable NAPI before enabling interrupts
From: Hamby, Jake (US)
Date: Tue Sep 10 2024 - 13:00:28 EST
> -----Original Message-----
> From: Hamby, Jake (US)
> Sent: Friday, September 6, 2024 4:20 PM
> To: linux-can@xxxxxxxxxxxxxxx; linux-kernel@xxxxxxxxxxxxxxx
> Subject: [PATCH] can: m_can: Enable NAPI before enabling interrupts
>
> If any error flags are set when bringing up the CAN device, e.g. due to
> CAN bus traffic before initializing the device, when m_can_start is
> called and interrupts are enabled, m_can_isr is called immediately,
> which disables all CAN interrupts and calls napi_schedule.
>
> Because napi_enable isn't called until later in m_can_open, the call to
> napi_schedule never schedules the m_can_poll callback and the device is
> left with interrupts disabled and can't receive any CAN packets until
> rebooted. This can be verified by running "cansend" from another device
> before setting the bitrate and calling "ip link set up can0" on the test
> device. Adding debug lines to m_can_isr shows it's called with flags
> (IR_EP | IR_EW | IR_CRCE), which calls m_can_disable_all_interrupts and
> napi_schedule, and then m_can_poll is never called.
>
> Move the call to napi_enable above the call to m_can_start to enable any
> initial interrupt flags to be handled by m_can_poll so that interrupts
> are reenabled. Add a call to napi_disable in the error handling section
> of m_can_open, to handle the case where later functions return errors.
>
> Also, in m_can_close, move the call to napi_disable below the call to
> m_can_stop to ensure all interrupts are handled when bringing down the
> device. This race condition is much less likely to occur.
>
> While testing, I noticed that IR_TSW (timestamp wraparound) fires at
> about 1 Hz, but the driver doesn't care about it. Add it to the list of
> interrupts to disable in m_can_chip_config to reduce unneeded wakeups.
>
> Tested on a Microchip SAMA7G54 MPU. The fix should be applicable to any
> SoC with a Bosch M_CAN controller.
> ---
> drivers/net/can/m_can/m_can.c | 19 ++++++++++++-------
> 1 file changed, 12 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/net/can/m_can/m_can.c
> b/drivers/net/can/m_can/m_can.c
> index 012c3d22b01d..4ced830f5ece 100644
> --- a/drivers/net/can/m_can/m_can.c
> +++ b/drivers/net/can/m_can/m_can.c
> @@ -1434,7 +1434,8 @@ static int m_can_chip_config(struct net_device
> *dev)
>
> /* Disable unused interrupts */
> interrupts &= ~(IR_ARA | IR_ELO | IR_DRX | IR_TEFF | IR_TFE | IR_TCF
> |
> - IR_HPM | IR_RF1F | IR_RF1W | IR_RF1N | IR_RF0F);
> + IR_HPM | IR_RF1F | IR_RF1W | IR_RF1N | IR_RF0F |
> + IR_TSW);
>
> err = m_can_config_enable(cdev);
> if (err)
> @@ -1763,13 +1764,14 @@ static int m_can_close(struct net_device *dev)
>
> netif_stop_queue(dev);
>
> - if (!cdev->is_peripheral)
> - napi_disable(&cdev->napi);
> -
> m_can_stop(dev);
> m_can_clk_stop(cdev);
> free_irq(dev->irq, dev);
>
> + /* disable NAPI after disabling interrupts */
> + if (!cdev->is_peripheral)
> + napi_disable(&cdev->napi);
> +
> m_can_clean(dev);
>
> if (cdev->is_peripheral) {
> @@ -2031,6 +2033,10 @@ static int m_can_open(struct net_device *dev)
> if (cdev->is_peripheral)
> can_rx_offload_enable(&cdev->offload);
>
> + /* enable NAPI before enabling interrupts */
> + if (!cdev->is_peripheral)
> + napi_enable(&cdev->napi);
> +
> /* register interrupt handler */
> if (cdev->is_peripheral) {
> cdev->tx_wq = alloc_ordered_workqueue("mcan_wq",
> @@ -2063,9 +2069,6 @@ static int m_can_open(struct net_device *dev)
> if (err)
> goto exit_start_fail;
>
> - if (!cdev->is_peripheral)
> - napi_enable(&cdev->napi);
> -
> netif_start_queue(dev);
>
> return 0;
> @@ -2077,6 +2080,8 @@ static int m_can_open(struct net_device *dev)
> if (cdev->is_peripheral)
> destroy_workqueue(cdev->tx_wq);
> out_wq_fail:
> + if (!cdev->is_peripheral)
> + napi_disable(&cdev->napi);
> if (cdev->is_peripheral)
> can_rx_offload_disable(&cdev->offload);
> close_candev(dev);
> --
> 2.34.1
>
> Best Regards,
> Jake Hamby
Signed-off-by: "Jake Hamby" <Jake.Hamby@xxxxxxxxxxxx>