For each TX CAN frame submitted to the USB device the driver saves the echo skb index in struct esd_tx_urb_context context objects. If the driver runs out of free context objects CAN transmission stops. This patch fixes some spots where such context objects are not freed correctly. In esd_usb_tx_done_msg() the check for netif_device_present() is moved after the identification and release of TX context and the release of the echo skb. This is allowed even if netif_device_present() would return false because the mentioned operations don't touch the device itself but only free local acquired resources. This keeps the context handling with the acknowledged TX jobs in sync. In esd_usb_start_xmit() a check is performed to see whether a context object could be allocated. Added a netif_stop_queue() there before the function is aborted. This makes sure the network queue is stopped and avoids getting tons of log messages in a situation without free TX objects. The adjacent log message now also prints the active jobs counter making a cross check between active jobs and "no free context" condition possible. In esd_usb_start_xmit() the error handling of usb_submit_urb() missed to free the context object together with the echo skb and decreasing the job count. Fixes: 96d8e90382dc ("can: Add driver for esd CAN-USB/2 device") Signed-off-by: Stefan Mätje --- drivers/net/can/usb/esd_usb.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/net/can/usb/esd_usb.c b/drivers/net/can/usb/esd_usb.c index fb0563582326..4ba6b6abd928 100644 --- a/drivers/net/can/usb/esd_usb.c +++ b/drivers/net/can/usb/esd_usb.c @@ -460,9 +460,6 @@ static void esd_usb_tx_done_msg(struct esd_usb_net_priv *priv, struct net_device *netdev = priv->netdev; struct esd_tx_urb_context *context; - if (!netif_device_present(netdev)) - return; - context = &priv->tx_contexts[msg->txdone.hnd & (ESD_USB_MAX_TX_URBS - 1)]; if (!msg->txdone.status) { @@ -478,6 +475,9 @@ static void esd_usb_tx_done_msg(struct esd_usb_net_priv *priv, context->echo_index = ESD_USB_MAX_TX_URBS; atomic_dec(&priv->active_tx_jobs); + if (!netif_device_present(netdev)) + return; + netif_wake_queue(netdev); } @@ -975,9 +975,11 @@ static netdev_tx_t esd_usb_start_xmit(struct sk_buff *skb, } } - /* This may never happen */ + /* This should never happen */ if (!context) { - netdev_warn(netdev, "couldn't find free context\n"); + netdev_warn(netdev, "No free context. Jobs: %d\n", + atomic_read(&priv->active_tx_jobs)); + netif_stop_queue(netdev); ret = NETDEV_TX_BUSY; goto releasebuf; } @@ -1008,6 +1010,8 @@ static netdev_tx_t esd_usb_start_xmit(struct sk_buff *skb, if (err) { can_free_echo_skb(netdev, context->echo_index, NULL); + /* Release used context on error */ + context->echo_index = ESD_USB_MAX_TX_URBS; atomic_dec(&priv->active_tx_jobs); usb_unanchor_urb(urb); -- 2.34.1