[PATCH v5 09/10] can: peak_usb: register the netdevs only once all channels are set up
From: Stéphane Grosjean via B4 Relay
Date: Tue Oct 20 2026 - 10:22:49 EST
From: Stéphane Grosjean <s.grosjean@xxxxxxxxxxxxxx>
peak_usb_create_dev() registers the netdev of a channel before calling
dev_init(), dev_set_bus() and dev_get_can_channel_id(). Userspace can
therefore open or configure the netdev while the channel is still being
initialized, outside of rtnl_lock():
- peak_usb_ndo_open() may run before dev_init() has set up the adapter
private data, e.g. pdev->usb_if, which is then dereferenced while
still NULL by pcan_usb_pro_start() or pcan_usb_fd_start();
- the commands sent by the open or bittiming callbacks may race with
the ones sent by dev_init() on the same command buffer;
- dev_set_bus(dev, 0) may switch the controller off right after
peak_usb_ndo_open() has started it.
On multi-channel adapters, the same applies to the channels already
created while a sibling is being initialized, since they share the
interface resources: on PCAN-USB FD, the command buffer and the
firmware info used by the sibling init, and the ctrlmode copied from
the first channel.
Moreover, if dev_init() or dev_set_bus() fails, the adapter private
data is released before unregister_candev(), which closes the netdev
if it has been opened in the meantime, and then accesses the released
data (interface object and, on PCAN-USB FD, command buffer).
Register the netdevs in peak_usb_probe(), only once all the channels
of the interface have been initialized, and only unregister the
registered ones in peak_usb_disconnect(). Since no netdev can be opened
during probe anymore, there is no RX to stop when probing a channel
fails. Since the channel id is now read before the netdev is
registered, PCAN-USB reports a failure to read it with dev_err()
instead of netdev_err().
Fixes: bb4785551f64 ("can: usb: PEAK-System Technik USB adapters driver core")
Signed-off-by: Stéphane Grosjean <s.grosjean@xxxxxxxxxxxxxx>
---
drivers/net/can/usb/peak_usb/pcan_usb.c | 7 +++-
drivers/net/can/usb/peak_usb/pcan_usb_core.c | 61 ++++++++++++++++------------
drivers/net/can/usb/peak_usb/pcan_usb_core.h | 1 +
3 files changed, 42 insertions(+), 27 deletions(-)
diff --git a/drivers/net/can/usb/peak_usb/pcan_usb.c b/drivers/net/can/usb/peak_usb/pcan_usb.c
index 4c8cfed31634..c7246465dcc7 100644
--- a/drivers/net/can/usb/peak_usb/pcan_usb.c
+++ b/drivers/net/can/usb/peak_usb/pcan_usb.c
@@ -390,9 +390,12 @@ static int pcan_usb_get_can_channel_id(struct peak_usb_device *dev, u32 *can_ch_
int err;
err = pcan_usb_wait_rsp(dev, PCAN_USB_CMD_DEVID, PCAN_USB_GET, args);
+ /* explicit use of dev_xxx() instead of netdev_xxx() here: this is
+ * also called during probe, before the netdev is registered.
+ */
if (err)
- netdev_err(dev->netdev, "getting can channel id failure: %d\n", err);
-
+ dev_err(dev->netdev->dev.parent,
+ "getting can channel id failure: %d\n", err);
else
*can_ch_id = args[0];
diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_core.c b/drivers/net/can/usb/peak_usb/pcan_usb_core.c
index 4c08656e7768..a12ee6ec71e6 100644
--- a/drivers/net/can/usb/peak_usb/pcan_usb_core.c
+++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.c
@@ -998,15 +998,6 @@ static int peak_usb_create_dev(const struct peak_usb_adapter *peak_usb_adapter,
SET_NETDEV_DEV(netdev, &intf->dev);
netdev->dev_id = ctrl_idx;
- err = register_candev(netdev);
- if (err) {
- dev_err(&intf->dev, "couldn't register CAN device: %d\n", err);
- goto lbl_restore_intf_data;
- }
-
- if (dev->prev_siblings)
- (dev->prev_siblings)->next_siblings = dev;
-
/* keep hw revision into the netdevice */
tmp16 = le16_to_cpu(usb_dev->descriptor.bcdDevice);
dev->device_rev = tmp16 >> 8;
@@ -1014,7 +1005,7 @@ static int peak_usb_create_dev(const struct peak_usb_adapter *peak_usb_adapter,
if (dev->adapter->dev_init) {
err = dev->adapter->dev_init(dev);
if (err)
- goto lbl_unregister_candev;
+ goto lbl_restore_intf_data;
}
/* set bus off */
@@ -1027,24 +1018,15 @@ static int peak_usb_create_dev(const struct peak_usb_adapter *peak_usb_adapter,
/* get CAN channel id early */
dev->adapter->dev_get_can_channel_id(dev, &dev->can_channel_id);
- netdev_info(netdev, "attached to %s channel %u (device 0x%08X)\n",
- peak_usb_adapter->name, ctrl_idx, dev->can_channel_id);
+ if (dev->prev_siblings)
+ (dev->prev_siblings)->next_siblings = dev;
return 0;
adap_dev_free:
- /* the whole interface is released when probing a channel fails:
- * stop RX on the channels already created, since their RX URBs may
- * carry records for this one.
- */
- peak_usb_stop_rx(dev->prev_siblings);
-
if (dev->adapter->dev_free)
dev->adapter->dev_free(dev);
-lbl_unregister_candev:
- unregister_candev(netdev);
-
lbl_restore_intf_data:
usb_set_intfdata(intf, dev->prev_siblings);
kfree(dev->cmd_buf);
@@ -1073,7 +1055,8 @@ static void peak_usb_disconnect(struct usb_interface *intf)
dev_prev_siblings = dev->prev_siblings;
strscpy(name, netdev->name, IFNAMSIZ);
- unregister_candev(netdev);
+ if (test_bit(PCAN_USB_STATE_REGISTERED, &dev->state))
+ unregister_candev(netdev);
kfree(dev->cmd_buf);
dev->next_siblings = NULL;
@@ -1094,6 +1077,7 @@ static int peak_usb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
const struct peak_usb_adapter *peak_usb_adapter;
+ struct peak_usb_device *dev;
int i, err = -ENOMEM;
/* get corresponding PCAN-USB adapter */
@@ -1108,13 +1092,40 @@ static int peak_usb_probe(struct usb_interface *intf,
for (i = 0; i < peak_usb_adapter->ctrl_count; i++) {
err = peak_usb_create_dev(peak_usb_adapter, intf, i);
+ if (err)
+ goto lbl_disconnect;
+ }
+
+ /* register the netdevs only once all the channels are set up: the
+ * channels of an interface share resources (command buffer, firmware
+ * info, ctrlmode copied from the 1st channel...) that must not be
+ * used through a netdev while a sibling is being initialized.
+ */
+ for (dev = usb_get_intfdata(intf); dev->prev_siblings;
+ dev = dev->prev_siblings)
+ ;
+
+ for (; dev; dev = dev->next_siblings) {
+ err = register_candev(dev->netdev);
if (err) {
- /* deregister already created devices */
- peak_usb_disconnect(intf);
- break;
+ dev_err(&intf->dev, "couldn't register CAN device: %d\n",
+ err);
+ goto lbl_disconnect;
}
+ set_bit(PCAN_USB_STATE_REGISTERED, &dev->state);
+
+ netdev_info(dev->netdev,
+ "attached to %s channel %u (device 0x%08X)\n",
+ peak_usb_adapter->name, dev->ctrl_idx,
+ dev->can_channel_id);
}
+ return 0;
+
+lbl_disconnect:
+ /* unregister (if needed) and free already created devices */
+ peak_usb_disconnect(intf);
+
return err;
}
diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_core.h b/drivers/net/can/usb/peak_usb/pcan_usb_core.h
index cb8777c3e08e..614854c998de 100644
--- a/drivers/net/can/usb/peak_usb/pcan_usb_core.h
+++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.h
@@ -114,6 +114,7 @@ struct peak_tx_urb_context {
*/
#define PCAN_USB_STATE_CONNECTED 0
#define PCAN_USB_STATE_STARTED 1
+#define PCAN_USB_STATE_REGISTERED 2
/* PEAK-System USB device */
struct peak_usb_device {
--
2.53.0