can: gs_usb: gs_can_open(): improve error handling
authorMarc Kleine-Budde <mkl@pengutronix.de>
Fri, 7 Jul 2023 11:43:10 +0000 (13:43 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 27 Jul 2023 06:50:27 +0000 (08:50 +0200)
commit 2603be9e8167ddc7bea95dcfab9ffc33414215aa upstream.

The gs_usb driver handles USB devices with more than 1 CAN channel.
The RX path for all channels share the same bulk endpoint (the
transmitted bulk data encodes the channel number). These per-device
resources are allocated and submitted by the first opened channel.

During this allocation, the resources are either released immediately
in case of a failure or the URBs are anchored. All anchored URBs are
finally killed with gs_usb_disconnect().

Currently, gs_can_open() returns with an error if the allocation of a
URB or a buffer fails. However, if usb_submit_urb() fails, the driver
continues with the URBs submitted so far, even if no URBs were
successfully submitted.

Treat every error as fatal and free all allocated resources
immediately.

Switch to goto-style error handling, to prepare the driver for more
per-device resource allocation.

Cc: stable@vger.kernel.org
Cc: John Whittington <git@jbrengineering.co.uk>
Link: https://lore.kernel.org/all/20230716-gs_usb-fix-time-stamp-counter-v1-1-9017cefcd9d5@pengutronix.de
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/net/can/usb/gs_usb.c

index 9c2c25f..fe532d5 100644 (file)
@@ -833,6 +833,7 @@ static int gs_can_open(struct net_device *netdev)
                .mode = cpu_to_le32(GS_CAN_MODE_START),
        };
        struct gs_host_frame *hf;
+       struct urb *urb = NULL;
        u32 ctrlmode;
        u32 flags = 0;
        int rc, i;
@@ -858,13 +859,14 @@ static int gs_can_open(struct net_device *netdev)
 
        if (!parent->active_channels) {
                for (i = 0; i < GS_MAX_RX_URBS; i++) {
-                       struct urb *urb;
                        u8 *buf;
 
                        /* alloc rx urb */
                        urb = usb_alloc_urb(0, GFP_KERNEL);
-                       if (!urb)
-                               return -ENOMEM;
+                       if (!urb) {
+                               rc = -ENOMEM;
+                               goto out_usb_kill_anchored_urbs;
+                       }
 
                        /* alloc rx buffer */
                        buf = kmalloc(dev->parent->hf_size_rx,
@@ -872,8 +874,8 @@ static int gs_can_open(struct net_device *netdev)
                        if (!buf) {
                                netdev_err(netdev,
                                           "No memory left for USB buffer\n");
-                               usb_free_urb(urb);
-                               return -ENOMEM;
+                               rc = -ENOMEM;
+                               goto out_usb_free_urb;
                        }
 
                        /* fill, anchor, and submit rx urb */
@@ -896,9 +898,7 @@ static int gs_can_open(struct net_device *netdev)
                                netdev_err(netdev,
                                           "usb_submit failed (err=%d)\n", rc);
 
-                               usb_unanchor_urb(urb);
-                               usb_free_urb(urb);
-                               break;
+                               goto out_usb_unanchor_urb;
                        }
 
                        /* Drop reference,
@@ -944,7 +944,8 @@ static int gs_can_open(struct net_device *netdev)
                if (dev->feature & GS_CAN_FEATURE_HW_TIMESTAMP)
                        gs_usb_timestamp_stop(dev);
                dev->can.state = CAN_STATE_STOPPED;
-               return rc;
+
+               goto out_usb_kill_anchored_urbs;
        }
 
        parent->active_channels++;
@@ -952,6 +953,18 @@ static int gs_can_open(struct net_device *netdev)
                netif_start_queue(netdev);
 
        return 0;
+
+out_usb_unanchor_urb:
+       usb_unanchor_urb(urb);
+out_usb_free_urb:
+       usb_free_urb(urb);
+out_usb_kill_anchored_urbs:
+       if (!parent->active_channels)
+               usb_kill_anchored_urbs(&dev->tx_submitted);
+
+       close_candev(netdev);
+
+       return rc;
 }
 
 static int gs_can_close(struct net_device *netdev)