forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
can: gs_usb: convert to NAPI/rx-offload to avoid OoO reception
The driver used to pass received CAN frames/skbs to the network stack with netif_rx(). In netif_rx() the skbs are queued to the local CPU. If IRQs are handled in round robin, OoO packets may occur. To avoid out-of-order reception convert the driver from netif_rx() to NAPI. For USB devices with timestamping support use the rx-offload helper can_rx_offload_queue_timestamp() for the RX, and can_rx_offload_get_echo_skb_queue_timestamp() for the TX path. Devices without timestamping support use can_rx_offload_queue_tail() for RX, and can_rx_offload_get_echo_skb_queue_tail() for the TX path. Link: https://lore.kernel.org/all/[email protected] Link: candle-usb/candleLight_fw#166 Link: https://lore.kernel.org/all/[email protected] Signed-off-by: Marc Kleine-Budde <[email protected]>
- Loading branch information
1 parent
8e0e295
commit 24bc41b
Showing
2 changed files
with
67 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,7 @@ | |
* Copyright (C) 2013-2016 Geschwister Schneider Technologie-, | ||
* Entwicklungs- und Vertriebs UG (Haftungsbeschränkt). | ||
* Copyright (C) 2016 Hubert Denkmair | ||
* Copyright (c) 2023 Pengutronix, Marc Kleine-Budde <[email protected]> | ||
* | ||
* Many thanks to all socketcan devs! | ||
*/ | ||
|
@@ -24,6 +25,7 @@ | |
#include <linux/can.h> | ||
#include <linux/can/dev.h> | ||
#include <linux/can/error.h> | ||
#include <linux/can/rx-offload.h> | ||
|
||
/* Device specific constants */ | ||
#define USB_GS_USB_1_VENDOR_ID 0x1d50 | ||
|
@@ -282,6 +284,8 @@ struct gs_host_frame { | |
#define GS_MAX_TX_URBS 10 | ||
/* Only launch a max of GS_MAX_RX_URBS usb requests at a time. */ | ||
#define GS_MAX_RX_URBS 30 | ||
#define GS_NAPI_WEIGHT 32 | ||
|
||
/* Maximum number of interfaces the driver supports per device. | ||
* Current hardware only supports 3 interfaces. The future may vary. | ||
*/ | ||
|
@@ -295,6 +299,7 @@ struct gs_tx_context { | |
struct gs_can { | ||
struct can_priv can; /* must be the first member */ | ||
|
||
struct can_rx_offload offload; | ||
struct gs_usb *parent; | ||
|
||
struct net_device *netdev; | ||
|
@@ -506,20 +511,59 @@ static void gs_update_state(struct gs_can *dev, struct can_frame *cf) | |
} | ||
} | ||
|
||
static void gs_usb_set_timestamp(struct gs_can *dev, struct sk_buff *skb, | ||
const struct gs_host_frame *hf) | ||
static u32 gs_usb_set_timestamp(struct gs_can *dev, struct sk_buff *skb, | ||
const struct gs_host_frame *hf) | ||
{ | ||
u32 timestamp; | ||
|
||
if (!(dev->feature & GS_CAN_FEATURE_HW_TIMESTAMP)) | ||
return; | ||
|
||
if (hf->flags & GS_CAN_FLAG_FD) | ||
timestamp = le32_to_cpu(hf->canfd_ts->timestamp_us); | ||
else | ||
timestamp = le32_to_cpu(hf->classic_can_ts->timestamp_us); | ||
|
||
gs_usb_skb_set_timestamp(dev, skb, timestamp); | ||
if (skb) | ||
gs_usb_skb_set_timestamp(dev, skb, timestamp); | ||
|
||
return timestamp; | ||
} | ||
|
||
static void gs_usb_rx_offload(struct gs_can *dev, struct sk_buff *skb, | ||
const struct gs_host_frame *hf) | ||
{ | ||
struct can_rx_offload *offload = &dev->offload; | ||
int rc; | ||
|
||
if (dev->feature & GS_CAN_FEATURE_HW_TIMESTAMP) { | ||
const u32 ts = gs_usb_set_timestamp(dev, skb, hf); | ||
|
||
rc = can_rx_offload_queue_timestamp(offload, skb, ts); | ||
} else { | ||
rc = can_rx_offload_queue_tail(offload, skb); | ||
} | ||
|
||
if (rc) | ||
dev->netdev->stats.rx_fifo_errors++; | ||
} | ||
|
||
static unsigned int | ||
gs_usb_get_echo_skb(struct gs_can *dev, struct sk_buff *skb, | ||
const struct gs_host_frame *hf) | ||
{ | ||
struct can_rx_offload *offload = &dev->offload; | ||
const u32 echo_id = hf->echo_id; | ||
unsigned int len; | ||
|
||
if (dev->feature & GS_CAN_FEATURE_HW_TIMESTAMP) { | ||
const u32 ts = gs_usb_set_timestamp(dev, skb, hf); | ||
|
||
len = can_rx_offload_get_echo_skb_queue_timestamp(offload, echo_id, | ||
ts, NULL); | ||
} else { | ||
len = can_rx_offload_get_echo_skb_queue_tail(offload, echo_id, | ||
NULL); | ||
} | ||
|
||
return len; | ||
} | ||
|
||
static void gs_usb_receive_bulk_callback(struct urb *urb) | ||
|
@@ -592,12 +636,7 @@ static void gs_usb_receive_bulk_callback(struct urb *urb) | |
gs_update_state(dev, cf); | ||
} | ||
|
||
gs_usb_set_timestamp(dev, skb, hf); | ||
|
||
stats->rx_packets++; | ||
stats->rx_bytes += hf->can_dlc; | ||
|
||
netif_rx(skb); | ||
gs_usb_rx_offload(dev, skb, hf); | ||
} else { /* echo_id == hf->echo_id */ | ||
if (hf->echo_id >= GS_MAX_TX_URBS) { | ||
netdev_err(netdev, | ||
|
@@ -617,12 +656,8 @@ static void gs_usb_receive_bulk_callback(struct urb *urb) | |
} | ||
|
||
skb = dev->can.echo_skb[hf->echo_id]; | ||
gs_usb_set_timestamp(dev, skb, hf); | ||
|
||
stats->tx_packets++; | ||
stats->tx_bytes += can_get_echo_skb(netdev, hf->echo_id, | ||
NULL); | ||
|
||
stats->tx_bytes += gs_usb_get_echo_skb(dev, skb, hf); | ||
gs_free_tx_context(txc); | ||
|
||
atomic_dec(&dev->active_tx_urbs); | ||
|
@@ -641,9 +676,12 @@ static void gs_usb_receive_bulk_callback(struct urb *urb) | |
cf->can_id |= CAN_ERR_CRTL; | ||
cf->len = CAN_ERR_DLC; | ||
cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; | ||
netif_rx(skb); | ||
|
||
gs_usb_rx_offload(dev, skb, hf); | ||
} | ||
|
||
can_rx_offload_irq_finish(&dev->offload); | ||
|
||
resubmit_urb: | ||
usb_fill_bulk_urb(urb, parent->udev, | ||
usb_rcvbulkpipe(parent->udev, GS_USB_ENDPOINT_IN), | ||
|
@@ -857,6 +895,8 @@ static int gs_can_open(struct net_device *netdev) | |
dev->hf_size_tx = struct_size(hf, classic_can, 1); | ||
} | ||
|
||
can_rx_offload_enable(&dev->offload); | ||
|
||
if (!parent->active_channels) { | ||
if (dev->feature & GS_CAN_FEATURE_HW_TIMESTAMP) | ||
gs_usb_timestamp_init(parent); | ||
|
@@ -965,6 +1005,7 @@ static int gs_can_open(struct net_device *netdev) | |
gs_usb_timestamp_stop(parent); | ||
} | ||
|
||
can_rx_offload_disable(&dev->offload); | ||
close_candev(netdev); | ||
|
||
return rc; | ||
|
@@ -1037,6 +1078,8 @@ static int gs_can_close(struct net_device *netdev) | |
dev->tx_context[rc].echo_id = GS_MAX_TX_URBS; | ||
} | ||
|
||
can_rx_offload_disable(&dev->offload); | ||
|
||
/* close the netdev */ | ||
close_candev(netdev); | ||
|
||
|
@@ -1336,18 +1379,21 @@ static struct gs_can *gs_make_candev(unsigned int channel, | |
dev->can.data_bittiming_const = &dev->data_bt_const; | ||
} | ||
|
||
can_rx_offload_add_manual(netdev, &dev->offload, GS_NAPI_WEIGHT); | ||
SET_NETDEV_DEV(netdev, &intf->dev); | ||
|
||
rc = register_candev(dev->netdev); | ||
if (rc) { | ||
dev_err(&intf->dev, | ||
"Couldn't register candev for channel %d (%pe)\n", | ||
channel, ERR_PTR(rc)); | ||
goto out_free_candev; | ||
goto out_can_rx_offload_del; | ||
} | ||
|
||
return dev; | ||
|
||
out_can_rx_offload_del: | ||
can_rx_offload_del(&dev->offload); | ||
out_free_candev: | ||
free_candev(dev->netdev); | ||
return ERR_PTR(rc); | ||
|
@@ -1356,6 +1402,7 @@ static struct gs_can *gs_make_candev(unsigned int channel, | |
static void gs_destroy_candev(struct gs_can *dev) | ||
{ | ||
unregister_candev(dev->netdev); | ||
can_rx_offload_del(&dev->offload); | ||
free_candev(dev->netdev); | ||
} | ||
|
||
|