[PATCH v6 2/3] can: rx-offload: add a per-IRQ receive context

Ciprian Marian Costea ciprianmarian.costea at oss.nxp.com
Thu Oct 1 00:45:06 PDT 2026


On 10/1/2026 6:39 AM, Bough Chen wrote:
> On Fri, Sep 25, 2026 at 04:45:57PM +0200, Ciprian Costea wrote:
>> From: Ciprian Marian Costea <ciprianmarian.costea at oss.nxp.com>
>>
>> The IRQ handler fills skb_irq_queue without a lock and the finish helpers
>> then splice it into skb_queue under skb_queue.lock. This breaks when a
>> driver uses the helpers from more than one IRQ line. On NXP S32G2, the
>> flexcan handlers can run at the same time on different CPUs and corrupt
>> skb_irq_queue.
>>
>> Add struct can_rx_offload_irq, one per IRQ line, which holds
>> skb_irq_queue, skb_queue_len_max and the mailbox range. skb_queue and napi
>> stay in struct can_rx_offload and are shared by all contexts.
>>
>> With a single context the finish helpers splice the queue as before. With
>> more than one, the skbs are sorted into skb_queue by timestamp, unless the
>> driver queues them without one, in which case they are appended.
>>
>> The queue, echo and finish helpers now take a struct can_rx_offload_irq,
>> the add helpers take the offload and the context. The NAPI weight is the
>> largest weight of the registered contexts and the queue length limit
>> derived from it is shared.
>>
>> Convert all users to a single context, no functional change for them.
>> flexcan is converted to one context per IRQ line in the next patch.
>>
>> Suggested-by: Marc Kleine-Budde <mkl at pengutronix.de>
>> Assisted-by: LLM
>> Signed-off-by: Ciprian Marian Costea <ciprianmarian.costea at oss.nxp.com>
>> ---
>>   drivers/net/can/at91_can.c                    |  18 +-
>>   drivers/net/can/bxcan.c                       |  14 +-
>>   drivers/net/can/can327.c                      |   8 +-
>>   drivers/net/can/dev/rx-offload.c              | 196 ++++++++++++------
>>   drivers/net/can/flexcan/flexcan-core.c        |  26 +--
>>   drivers/net/can/flexcan/flexcan-ethtool.c     |   4 +-
>>   drivers/net/can/flexcan/flexcan.h             |   1 +
>>   drivers/net/can/m_can/m_can.c                 |   7 +-
>>   drivers/net/can/m_can/m_can.h                 |   1 +
>>   .../net/can/rockchip/rockchip_canfd-core.c    |   9 +-
>>   drivers/net/can/rockchip/rockchip_canfd-rx.c  |   2 +-
>>   drivers/net/can/rockchip/rockchip_canfd-tx.c  |   2 +-
>>   drivers/net/can/rockchip/rockchip_canfd.h     |   1 +
>>   .../net/can/spi/mcp251xfd/mcp251xfd-core.c    |  13 +-
>>   drivers/net/can/spi/mcp251xfd/mcp251xfd-rx.c  |   2 +-
>>   drivers/net/can/spi/mcp251xfd/mcp251xfd-tef.c |   2 +-
>>   drivers/net/can/spi/mcp251xfd/mcp251xfd.h     |   1 +
>>   drivers/net/can/ti_hecc.c                     |  18 +-
>>   drivers/net/can/usb/gs_usb.c                  |  23 +-
>>   drivers/net/can/usb/nct6694_canfd.c           |  20 +-
>>   include/linux/can/rx-offload.h                |  48 ++++-
>>   21 files changed, 263 insertions(+), 153 deletions(-)
> 
> Hi Ciprian,
> 
> This is quite a large patch.
> Since the actual functional change is only that a few instances of
>      struct can_rx_offload_irq offload_irq
> are being merged into
>      struct can_rx_offload offload,
> have you considered doing it the other way around, for example by merging
>      struct can_rx_offload offload
> into
>      struct can_rx_offload_queue offload_queue?
>   
> This would avoid touching all vendor drivers and keep most of the changes
> limited to rx-offload.h and rx-offload.c.
> 
> Regards
> Haibo Chen

Hello Haibo,

This is a good suggestion indeed.
v7 will follow this layout: struct can_rx_offload stays the per-IRQ
source struct and skb_queue/napi move into a new struct
can_rx_offload_queue. Therefore only rx-offload.{c,h} and flexcan driver
will change.

Regards,
Ciprian

>>
>> diff --git a/drivers/net/can/at91_can.c b/drivers/net/can/at91_can.c
>> index 09aa676a03fb..f7b775f7c6d7 100644
>> --- a/drivers/net/can/at91_can.c
>> +++ b/drivers/net/can/at91_can.c
>> @@ -151,6 +151,7 @@ struct at91_devtype_data {
>>   struct at91_priv {
>>   	struct can_priv can;		/* must be the first member! */
>>   	struct can_rx_offload offload;
>> +	struct can_rx_offload_irq offload_irq;
>>   	struct phy *transceiver;
>>   
>>   	void __iomem *reg_base;
>> @@ -568,7 +569,7 @@ static void at91_rx_overflow_err(struct net_device *dev)
>>   	cf->can_id |= CAN_ERR_CRTL;
>>   	cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
>>   
>> -	err = can_rx_offload_queue_timestamp(&priv->offload, skb, timestamp);
>> +	err = can_rx_offload_queue_timestamp(&priv->offload_irq, skb, timestamp);
>>   	if (err)
>>   		stats->rx_fifo_errors++;
>>   }
>> @@ -745,7 +746,7 @@ static void at91_irq_err_line(struct net_device *dev, const u32 reg_sr)
>>   		cf->data[7] = bec.rxerr;
>>   	}
>>   
>> -	err = can_rx_offload_queue_timestamp(&priv->offload, skb, timestamp);
>> +	err = can_rx_offload_queue_timestamp(&priv->offload_irq, skb, timestamp);
>>   	if (err)
>>   		stats->rx_fifo_errors++;
>>   }
>> @@ -810,7 +811,7 @@ static void at91_irq_err_frame(struct net_device *dev, const u32 reg_sr)
>>   	if (!cf)
>>   		return;
>>   
>> -	err = can_rx_offload_queue_timestamp(&priv->offload, skb, timestamp);
>> +	err = can_rx_offload_queue_timestamp(&priv->offload_irq, skb, timestamp);
>>   	if (err)
>>   		stats->rx_fifo_errors++;
>>   }
>> @@ -836,7 +837,7 @@ static irqreturn_t at91_irq(int irq, void *dev_id)
>>   	 * Some bits of AT91_SR are cleared on read, keep them in reg_sr.
>>   	 */
>>   	while ((reg_sr_rx = at91_get_reg_sr_rx(priv, &reg_sr))) {
>> -		ret = can_rx_offload_irq_offload_timestamp(&priv->offload,
>> +		ret = can_rx_offload_irq_offload_timestamp(&priv->offload_irq,
>>   							   reg_sr_rx);
>>   		handled = IRQ_HANDLED;
>>   
>> @@ -864,7 +865,7 @@ static irqreturn_t at91_irq(int irq, void *dev_id)
>>   	}
>>   
>>   	if (handled)
>> -		can_rx_offload_irq_finish(&priv->offload);
>> +		can_rx_offload_irq_finish(&priv->offload_irq);
>>   
>>   	return handled;
>>   }
>> @@ -1120,10 +1121,11 @@ static int at91_can_probe(struct platform_device *pdev)
>>   	priv->pdata = dev_get_platdata(&pdev->dev);
>>   	priv->mb0_id = 0x7ff;
>>   	priv->offload.mailbox_read = at91_mailbox_read;
>> -	priv->offload.mb_first = devtype_data->rx_first;
>> -	priv->offload.mb_last = devtype_data->rx_last;
>> +	priv->offload_irq.mb_first = devtype_data->rx_first;
>> +	priv->offload_irq.mb_last = devtype_data->rx_last;
>>   
>> -	err = can_rx_offload_add_timestamp(dev, &priv->offload);
>> +	err = can_rx_offload_add_timestamp(dev, &priv->offload,
>> +					   &priv->offload_irq);
>>   	if (err) {
>>   		dev_err(&pdev->dev, "can_rx_offload_add_timestamp() failed\n");
>>   		goto exit_free;
>> diff --git a/drivers/net/can/bxcan.c b/drivers/net/can/bxcan.c
>> index baf494d20bef..fd032e094ee3 100644
>> --- a/drivers/net/can/bxcan.c
>> +++ b/drivers/net/can/bxcan.c
>> @@ -165,6 +165,7 @@ struct bxcan_regs {
>>   struct bxcan_priv {
>>   	struct can_priv can;
>>   	struct can_rx_offload offload;
>> +	struct can_rx_offload_irq offload_irq;
>>   	struct device *dev;
>>   	struct net_device *ndev;
>>   
>> @@ -420,8 +421,8 @@ static irqreturn_t bxcan_rx_isr(int irq, void *dev_id)
>>   	if (!(rf0r & BXCAN_RF0R_FMP0_MASK))
>>   		return IRQ_NONE;
>>   
>> -	can_rx_offload_irq_offload_fifo(&priv->offload);
>> -	can_rx_offload_irq_finish(&priv->offload);
>> +	can_rx_offload_irq_offload_fifo(&priv->offload_irq);
>> +	can_rx_offload_irq_finish(&priv->offload_irq);
>>   
>>   	return IRQ_HANDLED;
>>   }
>> @@ -507,7 +508,7 @@ static void bxcan_handle_state_change(struct net_device *ndev, u32 esr)
>>   	if (skb) {
>>   		int err;
>>   
>> -		err = can_rx_offload_queue_timestamp(&priv->offload, skb,
>> +		err = can_rx_offload_queue_timestamp(&priv->offload_irq, skb,
>>   						     priv->timestamp);
>>   		if (err)
>>   			ndev->stats.rx_fifo_errors++;
>> @@ -592,7 +593,7 @@ static void bxcan_handle_bus_err(struct net_device *ndev, u32 esr)
>>   	if (skb) {
>>   		int err;
>>   
>> -		err = can_rx_offload_queue_timestamp(&priv->offload, skb,
>> +		err = can_rx_offload_queue_timestamp(&priv->offload_irq, skb,
>>   						     priv->timestamp);
>>   		if (err)
>>   			ndev->stats.rx_fifo_errors++;
>> @@ -618,7 +619,7 @@ static irqreturn_t bxcan_state_change_isr(int irq, void *dev_id)
>>   
>>   	msr |= BXCAN_MSR_ERRI;
>>   	writel(msr, &regs->msr);
>> -	can_rx_offload_irq_finish(&priv->offload);
>> +	can_rx_offload_irq_finish(&priv->offload_irq);
>>   
>>   	return IRQ_HANDLED;
>>   }
>> @@ -1008,7 +1009,8 @@ static int bxcan_probe(struct platform_device *pdev)
>>   		CAN_CTRLMODE_LISTENONLY	| CAN_CTRLMODE_BERR_REPORTING;
>>   
>>   	priv->offload.mailbox_read = bxcan_mailbox_read;
>> -	err = can_rx_offload_add_fifo(ndev, &priv->offload, BXCAN_NAPI_WEIGHT);
>> +	err = can_rx_offload_add_fifo(ndev, &priv->offload,
>> +				      &priv->offload_irq, BXCAN_NAPI_WEIGHT);
>>   	if (err) {
>>   		dev_err(dev, "failed to add FIFO rx_offload\n");
>>   		goto out_free_candev;
>> diff --git a/drivers/net/can/can327.c b/drivers/net/can/can327.c
>> index 90f5e35f3c8f..6d505ac7d8b9 100644
>> --- a/drivers/net/can/can327.c
>> +++ b/drivers/net/can/can327.c
>> @@ -69,6 +69,7 @@ struct can327 {
>>   	struct can_priv can;
>>   
>>   	struct can_rx_offload offload;
>> +	struct can_rx_offload_irq offload_irq;
>>   
>>   	/* TTY buffers */
>>   	u8 txbuf[CAN327_SIZE_TXBUF];
>> @@ -272,11 +273,11 @@ static void can327_feed_frame_to_netdev(struct can327 *elm, struct sk_buff *skb)
>>   	/* Queue for NAPI pickup.
>>   	 * rx-offload will update stats and LEDs for us.
>>   	 */
>> -	if (can_rx_offload_queue_tail(&elm->offload, skb))
>> +	if (can_rx_offload_queue_tail(&elm->offload_irq, skb))
>>   		elm->dev->stats.rx_fifo_errors++;
>>   
>>   	/* Wake NAPI */
>> -	can_rx_offload_irq_finish(&elm->offload);
>> +	can_rx_offload_irq_finish(&elm->offload_irq);
>>   }
>>   
>>   /* Called when we're out of ideas and just want it all to end. */
>> @@ -772,7 +773,8 @@ static int can327_netdev_open(struct net_device *dev)
>>   	can327_init_device(elm);
>>   	spin_unlock_bh(&elm->lock);
>>   
>> -	err = can_rx_offload_add_manual(dev, &elm->offload, CAN327_NAPI_WEIGHT);
>> +	err = can_rx_offload_add_manual(dev, &elm->offload,
>> +					&elm->offload_irq, CAN327_NAPI_WEIGHT);
>>   	if (err) {
>>   		close_candev(dev);
>>   		return err;
>> diff --git a/drivers/net/can/dev/rx-offload.c b/drivers/net/can/dev/rx-offload.c
>> index 46e7b6db4a1e..854be0b2c051 100644
>> --- a/drivers/net/can/dev/rx-offload.c
>> +++ b/drivers/net/can/dev/rx-offload.c
>> @@ -21,19 +21,19 @@ can_rx_offload_get_cb(struct sk_buff *skb)
>>   }
>>   
>>   static inline bool
>> -can_rx_offload_le(struct can_rx_offload *offload,
>> +can_rx_offload_le(struct can_rx_offload_irq *offload_irq,
>>   		  unsigned int a, unsigned int b)
>>   {
>> -	if (offload->inc)
>> +	if (offload_irq->inc)
>>   		return a <= b;
>>   	else
>>   		return a >= b;
>>   }
>>   
>>   static inline unsigned int
>> -can_rx_offload_inc(struct can_rx_offload *offload, unsigned int *val)
>> +can_rx_offload_inc(struct can_rx_offload_irq *offload_irq, unsigned int *val)
>>   {
>> -	if (offload->inc)
>> +	if (offload_irq->inc)
>>   		return (*val)++;
>>   	else
>>   		return (*val)--;
>> @@ -118,7 +118,7 @@ static int can_rx_offload_compare(struct sk_buff *a, struct sk_buff *b)
>>   
>>   /**
>>    * can_rx_offload_offload_one() - Read one CAN frame from HW
>> - * @offload: pointer to rx_offload context
>> + * @offload_irq: pointer to rx_offload per-IRQ context
>>    * @n: number of mailbox to read
>>    *
>>    * The task of this function is to read a CAN frame from mailbox @n
>> @@ -126,9 +126,9 @@ static int can_rx_offload_compare(struct sk_buff *a, struct sk_buff *b)
>>    * sk_buff.
>>    *
>>    * If the struct can_rx_offload::skb_queue exceeds the maximal queue
>> - * length (struct can_rx_offload::skb_queue_len_max) or no skb can be
>> - * allocated, the mailbox contents is discarded by reading it into an
>> - * overflow buffer. This way the mailbox is marked as free by the
>> + * length (struct can_rx_offload_irq::skb_queue_len_max) or no skb can
>> + * be allocated, the mailbox contents is discarded by reading it into
>> + * an overflow buffer. This way the mailbox is marked as free by the
>>    * driver.
>>    *
>>    * Return: A pointer to skb containing the CAN frame on success.
>> @@ -138,8 +138,10 @@ static int can_rx_offload_compare(struct sk_buff *a, struct sk_buff *b)
>>    *         ERR_PTR() in case of an error
>>    */
>>   static struct sk_buff *
>> -can_rx_offload_offload_one(struct can_rx_offload *offload, unsigned int n)
>> +can_rx_offload_offload_one(struct can_rx_offload_irq *offload_irq,
>> +			   unsigned int n)
>>   {
>> +	struct can_rx_offload *offload = offload_irq->offload;
>>   	struct sk_buff *skb;
>>   	struct can_rx_offload_cb *cb;
>>   	bool drop = false;
>> @@ -147,7 +149,7 @@ can_rx_offload_offload_one(struct can_rx_offload *offload, unsigned int n)
>>   
>>   	/* If queue is full drop frame */
>>   	if (unlikely(skb_queue_len(&offload->skb_queue) >
>> -		     offload->skb_queue_len_max))
>> +		     offload_irq->skb_queue_len_max))
>>   		drop = true;
>>   
>>   	skb = offload->mailbox_read(offload, n, &timestamp, drop);
>> @@ -172,25 +174,25 @@ can_rx_offload_offload_one(struct can_rx_offload *offload, unsigned int n)
>>   	return skb;
>>   }
>>   
>> -int can_rx_offload_irq_offload_timestamp(struct can_rx_offload *offload,
>> +int can_rx_offload_irq_offload_timestamp(struct can_rx_offload_irq *offload_irq,
>>   					 u64 pending)
>>   {
>>   	unsigned int i;
>>   	int received = 0;
>>   
>> -	for (i = offload->mb_first;
>> -	     can_rx_offload_le(offload, i, offload->mb_last);
>> -	     can_rx_offload_inc(offload, &i)) {
>> +	for (i = offload_irq->mb_first;
>> +	     can_rx_offload_le(offload_irq, i, offload_irq->mb_last);
>> +	     can_rx_offload_inc(offload_irq, &i)) {
>>   		struct sk_buff *skb;
>>   
>>   		if (!(pending & BIT_ULL(i)))
>>   			continue;
>>   
>> -		skb = can_rx_offload_offload_one(offload, i);
>> +		skb = can_rx_offload_offload_one(offload_irq, i);
>>   		if (IS_ERR_OR_NULL(skb))
>>   			continue;
>>   
>> -		__skb_queue_add_sort(&offload->skb_irq_queue, skb,
>> +		__skb_queue_add_sort(&offload_irq->skb_irq_queue, skb,
>>   				     can_rx_offload_compare);
>>   		received++;
>>   	}
>> @@ -199,19 +201,19 @@ int can_rx_offload_irq_offload_timestamp(struct can_rx_offload *offload,
>>   }
>>   EXPORT_SYMBOL_GPL(can_rx_offload_irq_offload_timestamp);
>>   
>> -int can_rx_offload_irq_offload_fifo(struct can_rx_offload *offload)
>> +int can_rx_offload_irq_offload_fifo(struct can_rx_offload_irq *offload_irq)
>>   {
>>   	struct sk_buff *skb;
>>   	int received = 0;
>>   
>>   	while (1) {
>> -		skb = can_rx_offload_offload_one(offload, 0);
>> +		skb = can_rx_offload_offload_one(offload_irq, 0);
>>   		if (IS_ERR(skb))
>>   			continue;
>>   		if (!skb)
>>   			break;
>>   
>> -		__skb_queue_tail(&offload->skb_irq_queue, skb);
>> +		__skb_queue_tail(&offload_irq->skb_irq_queue, skb);
>>   		received++;
>>   	}
>>   
>> @@ -219,13 +221,14 @@ int can_rx_offload_irq_offload_fifo(struct can_rx_offload *offload)
>>   }
>>   EXPORT_SYMBOL_GPL(can_rx_offload_irq_offload_fifo);
>>   
>> -int can_rx_offload_queue_timestamp(struct can_rx_offload *offload,
>> +int can_rx_offload_queue_timestamp(struct can_rx_offload_irq *offload_irq,
>>   				   struct sk_buff *skb, u32 timestamp)
>>   {
>> +	struct can_rx_offload *offload = offload_irq->offload;
>>   	struct can_rx_offload_cb *cb;
>>   
>>   	if (skb_queue_len(&offload->skb_queue) >
>> -	    offload->skb_queue_len_max) {
>> +	    offload_irq->skb_queue_len_max) {
>>   		dev_kfree_skb_any(skb);
>>   		return -ENOBUFS;
>>   	}
>> @@ -233,7 +236,7 @@ int can_rx_offload_queue_timestamp(struct can_rx_offload *offload,
>>   	cb = can_rx_offload_get_cb(skb);
>>   	cb->timestamp = timestamp;
>>   
>> -	__skb_queue_add_sort(&offload->skb_irq_queue, skb,
>> +	__skb_queue_add_sort(&offload_irq->skb_irq_queue, skb,
>>   			     can_rx_offload_compare);
>>   
>>   	return 0;
>> @@ -241,11 +244,11 @@ int can_rx_offload_queue_timestamp(struct can_rx_offload *offload,
>>   EXPORT_SYMBOL_GPL(can_rx_offload_queue_timestamp);
>>   
>>   unsigned int
>> -can_rx_offload_get_echo_skb_queue_timestamp(struct can_rx_offload *offload,
>> +can_rx_offload_get_echo_skb_queue_timestamp(struct can_rx_offload_irq *offload_irq,
>>   					    unsigned int idx, u32 timestamp,
>>   					    unsigned int *frame_len_ptr)
>>   {
>> -	struct net_device *dev = offload->dev;
>> +	struct net_device *dev = offload_irq->offload->dev;
>>   	struct net_device_stats *stats = &dev->stats;
>>   	struct sk_buff *skb;
>>   	unsigned int len;
>> @@ -255,7 +258,7 @@ can_rx_offload_get_echo_skb_queue_timestamp(struct can_rx_offload *offload,
>>   	if (!skb)
>>   		return 0;
>>   
>> -	err = can_rx_offload_queue_timestamp(offload, skb, timestamp);
>> +	err = can_rx_offload_queue_timestamp(offload_irq, skb, timestamp);
>>   	if (err) {
>>   		stats->rx_errors++;
>>   		stats->tx_fifo_errors++;
>> @@ -265,27 +268,29 @@ can_rx_offload_get_echo_skb_queue_timestamp(struct can_rx_offload *offload,
>>   }
>>   EXPORT_SYMBOL_GPL(can_rx_offload_get_echo_skb_queue_timestamp);
>>   
>> -int can_rx_offload_queue_tail(struct can_rx_offload *offload,
>> +int can_rx_offload_queue_tail(struct can_rx_offload_irq *offload_irq,
>>   			      struct sk_buff *skb)
>>   {
>> +	struct can_rx_offload *offload = offload_irq->offload;
>> +
>>   	if (skb_queue_len(&offload->skb_queue) >
>> -	    offload->skb_queue_len_max) {
>> +	    offload_irq->skb_queue_len_max) {
>>   		dev_kfree_skb_any(skb);
>>   		return -ENOBUFS;
>>   	}
>>   
>> -	__skb_queue_tail(&offload->skb_irq_queue, skb);
>> +	__skb_queue_tail(&offload_irq->skb_irq_queue, skb);
>>   
>>   	return 0;
>>   }
>>   EXPORT_SYMBOL_GPL(can_rx_offload_queue_tail);
>>   
>>   unsigned int
>> -can_rx_offload_get_echo_skb_queue_tail(struct can_rx_offload *offload,
>> +can_rx_offload_get_echo_skb_queue_tail(struct can_rx_offload_irq *offload_irq,
>>   				       unsigned int idx,
>>   				       unsigned int *frame_len_ptr)
>>   {
>> -	struct net_device *dev = offload->dev;
>> +	struct net_device *dev = offload_irq->offload->dev;
>>   	struct net_device_stats *stats = &dev->stats;
>>   	struct sk_buff *skb;
>>   	unsigned int len;
>> @@ -295,7 +300,7 @@ can_rx_offload_get_echo_skb_queue_tail(struct can_rx_offload *offload,
>>   	if (!skb)
>>   		return 0;
>>   
>> -	err = can_rx_offload_queue_tail(offload, skb);
>> +	err = can_rx_offload_queue_tail(offload_irq, skb);
>>   	if (err) {
>>   		stats->rx_errors++;
>>   		stats->tx_fifo_errors++;
>> @@ -305,20 +310,44 @@ can_rx_offload_get_echo_skb_queue_tail(struct can_rx_offload *offload,
>>   }
>>   EXPORT_SYMBOL_GPL(can_rx_offload_get_echo_skb_queue_tail);
>>   
>> -void can_rx_offload_irq_finish(struct can_rx_offload *offload)
>> +static void
>> +can_rx_offload_move_to_skb_queue(struct can_rx_offload_irq *offload_irq)
>>   {
>> +	struct can_rx_offload *offload = offload_irq->offload;
>>   	unsigned long flags;
>> +
>> +	spin_lock_irqsave(&offload->skb_queue.lock, flags);
>> +
>> +	if (offload->irq_cnt > 1 && offload->sort) {
>> +		/* Merge this IRQ's sorted queue into skb_queue to keep the
>> +		 * overall timestamp order.
>> +		 */
>> +		struct sk_buff *skb;
>> +
>> +		while ((skb = __skb_dequeue(&offload_irq->skb_irq_queue)))
>> +			__skb_queue_add_sort(&offload->skb_queue, skb,
>> +					     can_rx_offload_compare);
>> +	} else {
>> +		/* Single producer, or no timestamp to sort by. */
>> +		skb_queue_splice_tail_init(&offload_irq->skb_irq_queue,
>> +					   &offload->skb_queue);
>> +	}
>> +
>> +	spin_unlock_irqrestore(&offload->skb_queue.lock, flags);
>> +}
>> +
>> +void can_rx_offload_irq_finish(struct can_rx_offload_irq *offload_irq)
>> +{
>> +	struct can_rx_offload *offload = offload_irq->offload;
>>   	int queue_len;
>>   
>> -	if (skb_queue_empty_lockless(&offload->skb_irq_queue))
>> +	if (skb_queue_empty_lockless(&offload_irq->skb_irq_queue))
>>   		return;
>>   
>> -	spin_lock_irqsave(&offload->skb_queue.lock, flags);
>> -	skb_queue_splice_tail_init(&offload->skb_irq_queue, &offload->skb_queue);
>> -	spin_unlock_irqrestore(&offload->skb_queue.lock, flags);
>> +	can_rx_offload_move_to_skb_queue(offload_irq);
>>   
>>   	queue_len = skb_queue_len(&offload->skb_queue);
>> -	if (queue_len > offload->skb_queue_len_max / 8)
>> +	if (queue_len > offload_irq->skb_queue_len_max / 8)
>>   		netdev_dbg(offload->dev, "%s: queue_len=%d\n",
>>   			   __func__, queue_len);
>>   
>> @@ -326,20 +355,18 @@ void can_rx_offload_irq_finish(struct can_rx_offload *offload)
>>   }
>>   EXPORT_SYMBOL_GPL(can_rx_offload_irq_finish);
>>   
>> -void can_rx_offload_threaded_irq_finish(struct can_rx_offload *offload)
>> +void can_rx_offload_threaded_irq_finish(struct can_rx_offload_irq *offload_irq)
>>   {
>> -	unsigned long flags;
>> +	struct can_rx_offload *offload = offload_irq->offload;
>>   	int queue_len;
>>   
>> -	if (skb_queue_empty_lockless(&offload->skb_irq_queue))
>> +	if (skb_queue_empty_lockless(&offload_irq->skb_irq_queue))
>>   		return;
>>   
>> -	spin_lock_irqsave(&offload->skb_queue.lock, flags);
>> -	skb_queue_splice_tail_init(&offload->skb_irq_queue, &offload->skb_queue);
>> -	spin_unlock_irqrestore(&offload->skb_queue.lock, flags);
>> +	can_rx_offload_move_to_skb_queue(offload_irq);
>>   
>>   	queue_len = skb_queue_len(&offload->skb_queue);
>> -	if (queue_len > offload->skb_queue_len_max / 8)
>> +	if (queue_len > offload_irq->skb_queue_len_max / 8)
>>   		netdev_dbg(offload->dev, "%s: queue_len=%d\n",
>>   			   __func__, queue_len);
>>   
>> @@ -351,64 +378,88 @@ EXPORT_SYMBOL_GPL(can_rx_offload_threaded_irq_finish);
>>   
>>   static int can_rx_offload_init_queue(struct net_device *dev,
>>   				     struct can_rx_offload *offload,
>> +				     struct can_rx_offload_irq *offload_irq,
>>   				     unsigned int weight)
>>   {
>> -	offload->dev = dev;
>> -
>> -	/* Limit queue len to 4x the weight (rounded to next power of two) */
>> -	offload->skb_queue_len_max = 2 << fls(weight);
>> -	offload->skb_queue_len_max *= 4;
>> -	skb_queue_head_init(&offload->skb_queue);
>> -	__skb_queue_head_init(&offload->skb_irq_queue);
>> +	struct can_rx_offload_irq *pos;
>> +	u32 skb_queue_len_max;
>> +
>> +	offload_irq->offload = offload;
>> +	__skb_queue_head_init(&offload_irq->skb_irq_queue);
>> +
>> +	/* The first registered IRQ initializes the shared state. */
>> +	if (!offload->irq_cnt) {
>> +		offload->dev = dev;
>> +		skb_queue_head_init(&offload->skb_queue);
>> +		INIT_LIST_HEAD(&offload->irqs);
>> +		netif_napi_add_weight(dev, &offload->napi,
>> +				      can_rx_offload_napi_poll, weight);
>> +	} else if (weight > offload->napi.weight) {
>> +		/* All contexts feed the same NAPI, keep the largest weight. */
>> +		offload->napi.weight = weight;
>> +	}
>> +	list_add_tail(&offload_irq->node, &offload->irqs);
>> +	offload->irq_cnt++;
>>   
>> -	netif_napi_add_weight(dev, &offload->napi, can_rx_offload_napi_poll,
>> -			      weight);
>> +	/* Limit queue len to 4x the weight (rounded to next power of two).
>> +	 * All contexts feed the same skb_queue, so they share its limit.
>> +	 */
>> +	skb_queue_len_max = 2 << fls(offload->napi.weight);
>> +	skb_queue_len_max *= 4;
>> +	list_for_each_entry(pos, &offload->irqs, node)
>> +		pos->skb_queue_len_max = skb_queue_len_max;
>>   
>>   	dev_dbg(dev->dev.parent, "%s: skb_queue_len_max=%d\n",
>> -		__func__, offload->skb_queue_len_max);
>> +		__func__, skb_queue_len_max);
>>   
>>   	return 0;
>>   }
>>   
>>   int can_rx_offload_add_timestamp(struct net_device *dev,
>> -				 struct can_rx_offload *offload)
>> +				 struct can_rx_offload *offload,
>> +				 struct can_rx_offload_irq *offload_irq)
>>   {
>>   	unsigned int weight;
>>   
>> -	if (offload->mb_first > BITS_PER_LONG_LONG ||
>> -	    offload->mb_last > BITS_PER_LONG_LONG || !offload->mailbox_read)
>> +	if (offload_irq->mb_first > BITS_PER_LONG_LONG ||
>> +	    offload_irq->mb_last > BITS_PER_LONG_LONG || !offload->mailbox_read)
>>   		return -EINVAL;
>>   
>> -	if (offload->mb_first < offload->mb_last) {
>> -		offload->inc = true;
>> -		weight = offload->mb_last - offload->mb_first;
>> +	if (offload_irq->mb_first < offload_irq->mb_last) {
>> +		offload_irq->inc = true;
>> +		weight = offload_irq->mb_last - offload_irq->mb_first;
>>   	} else {
>> -		offload->inc = false;
>> -		weight = offload->mb_first - offload->mb_last;
>> +		offload_irq->inc = false;
>> +		weight = offload_irq->mb_first - offload_irq->mb_last;
>>   	}
>>   
>> -	return can_rx_offload_init_queue(dev, offload, weight);
>> +	offload->sort = true;
>> +
>> +	return can_rx_offload_init_queue(dev, offload, offload_irq, weight);
>>   }
>>   EXPORT_SYMBOL_GPL(can_rx_offload_add_timestamp);
>>   
>>   int can_rx_offload_add_fifo(struct net_device *dev,
>> -			    struct can_rx_offload *offload, unsigned int weight)
>> +			    struct can_rx_offload *offload,
>> +			    struct can_rx_offload_irq *offload_irq,
>> +			    unsigned int weight)
>>   {
>>   	if (!offload->mailbox_read)
>>   		return -EINVAL;
>>   
>> -	return can_rx_offload_init_queue(dev, offload, weight);
>> +	return can_rx_offload_init_queue(dev, offload, offload_irq, weight);
>>   }
>>   EXPORT_SYMBOL_GPL(can_rx_offload_add_fifo);
>>   
>>   int can_rx_offload_add_manual(struct net_device *dev,
>>   			      struct can_rx_offload *offload,
>> +			      struct can_rx_offload_irq *offload_irq,
>>   			      unsigned int weight)
>>   {
>>   	if (offload->mailbox_read)
>>   		return -EINVAL;
>>   
>> -	return can_rx_offload_init_queue(dev, offload, weight);
>> +	return can_rx_offload_init_queue(dev, offload, offload_irq, weight);
>>   }
>>   EXPORT_SYMBOL_GPL(can_rx_offload_add_manual);
>>   
>> @@ -420,8 +471,19 @@ EXPORT_SYMBOL_GPL(can_rx_offload_enable);
>>   
>>   void can_rx_offload_del(struct can_rx_offload *offload)
>>   {
>> +	struct can_rx_offload_irq *offload_irq, *tmp;
>> +
>>   	netif_napi_del(&offload->napi);
>>   	skb_queue_purge(&offload->skb_queue);
>> -	__skb_queue_purge(&offload->skb_irq_queue);
>> +
>> +	/* Drop the contexts, so that re-adding them (e.g. on the next
>> +	 * ndo_open()) initializes the shared state again.
>> +	 */
>> +	list_for_each_entry_safe(offload_irq, tmp, &offload->irqs, node) {
>> +		__skb_queue_purge(&offload_irq->skb_irq_queue);
>> +		list_del_init(&offload_irq->node);
>> +	}
>> +	offload->irq_cnt = 0;
>> +	offload->sort = false;
>>   }
>>   EXPORT_SYMBOL_GPL(can_rx_offload_del);
>> diff --git a/drivers/net/can/flexcan/flexcan-core.c b/drivers/net/can/flexcan/flexcan-core.c
>> index f5d22c61503f..a81132d3afa8 100644
>> --- a/drivers/net/can/flexcan/flexcan-core.c
>> +++ b/drivers/net/can/flexcan/flexcan-core.c
>> @@ -885,7 +885,7 @@ static void flexcan_irq_bus_err(struct net_device *dev, u32 reg_esr)
>>   	if (tx_errors)
>>   		dev->stats.tx_errors++;
>>   
>> -	err = can_rx_offload_queue_timestamp(&priv->offload, skb, timestamp);
>> +	err = can_rx_offload_queue_timestamp(&priv->offload_irq, skb, timestamp);
>>   	if (err)
>>   		dev->stats.rx_fifo_errors++;
>>   }
>> @@ -932,7 +932,7 @@ static void flexcan_irq_state(struct net_device *dev, u32 reg_esr)
>>   	if (unlikely(new_state == CAN_STATE_BUS_OFF))
>>   		can_bus_off(dev);
>>   
>> -	err = can_rx_offload_queue_timestamp(&priv->offload, skb, timestamp);
>> +	err = can_rx_offload_queue_timestamp(&priv->offload_irq, skb, timestamp);
>>   	if (err)
>>   		dev->stats.rx_fifo_errors++;
>>   }
>> @@ -1088,7 +1088,7 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id)
>>   
>>   		while ((reg_iflag_rx = flexcan_read_reg_iflag_rx(priv))) {
>>   			handled = IRQ_HANDLED;
>> -			ret = can_rx_offload_irq_offload_timestamp(&priv->offload,
>> +			ret = can_rx_offload_irq_offload_timestamp(&priv->offload_irq,
>>   								   reg_iflag_rx);
>>   			if (!ret)
>>   				break;
>> @@ -1099,7 +1099,7 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id)
>>   		reg_iflag1 = priv->read(&regs->iflag1);
>>   		if (reg_iflag1 & FLEXCAN_IFLAG_RX_FIFO_AVAILABLE) {
>>   			handled = IRQ_HANDLED;
>> -			can_rx_offload_irq_offload_fifo(&priv->offload);
>> +			can_rx_offload_irq_offload_fifo(&priv->offload_irq);
>>   		}
>>   
>>   		/* FIFO overflow interrupt */
>> @@ -1120,7 +1120,7 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id)
>>   
>>   		handled = IRQ_HANDLED;
>>   		stats->tx_bytes +=
>> -			can_rx_offload_get_echo_skb_queue_timestamp(&priv->offload, 0,
>> +			can_rx_offload_get_echo_skb_queue_timestamp(&priv->offload_irq, 0,
>>   								    reg_ctrl << 16, NULL);
>>   		stats->tx_packets++;
>>   
>> @@ -1189,7 +1189,7 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id)
>>   	}
>>   
>>   	if (handled)
>> -		can_rx_offload_irq_finish(&priv->offload);
>> +		can_rx_offload_irq_finish(&priv->offload_irq);
>>   
>>   	return handled;
>>   }
>> @@ -1410,16 +1410,18 @@ static int flexcan_rx_offload_setup(struct net_device *dev)
>>   	priv->offload.mailbox_read = flexcan_mailbox_read;
>>   
>>   	if (priv->devtype_data.quirks & FLEXCAN_QUIRK_USE_RX_MAILBOX) {
>> -		priv->offload.mb_first = FLEXCAN_RX_MB_RX_MAILBOX_FIRST;
>> -		priv->offload.mb_last = priv->mb_count - 2;
>> +		priv->offload_irq.mb_first = FLEXCAN_RX_MB_RX_MAILBOX_FIRST;
>> +		priv->offload_irq.mb_last = priv->mb_count - 2;
>>   
>> -		priv->rx_mask = GENMASK_ULL(priv->offload.mb_last,
>> -					    priv->offload.mb_first);
>> -		err = can_rx_offload_add_timestamp(dev, &priv->offload);
>> +		priv->rx_mask = GENMASK_ULL(priv->offload_irq.mb_last,
>> +					    priv->offload_irq.mb_first);
>> +		err = can_rx_offload_add_timestamp(dev, &priv->offload,
>> +						   &priv->offload_irq);
>>   	} else {
>>   		priv->rx_mask = FLEXCAN_IFLAG_RX_FIFO_OVERFLOW |
>>   			FLEXCAN_IFLAG_RX_FIFO_AVAILABLE;
>>   		err = can_rx_offload_add_fifo(dev, &priv->offload,
>> +					      &priv->offload_irq,
>>   					      FLEXCAN_NAPI_WEIGHT);
>>   	}
>>   
>> @@ -1600,7 +1602,7 @@ static int flexcan_chip_start(struct net_device *dev)
>>   	}
>>   
>>   	if (priv->devtype_data.quirks & FLEXCAN_QUIRK_USE_RX_MAILBOX) {
>> -		for (i = priv->offload.mb_first; i <= priv->offload.mb_last; i++) {
>> +		for (i = priv->offload_irq.mb_first; i <= priv->offload_irq.mb_last; i++) {
>>   			mb = flexcan_get_mb(priv, i);
>>   			priv->write(FLEXCAN_MB_CODE_RX_EMPTY,
>>   				    &mb->can_ctrl);
>> diff --git a/drivers/net/can/flexcan/flexcan-ethtool.c b/drivers/net/can/flexcan/flexcan-ethtool.c
>> index 50e86b2da532..e34afafbbd54 100644
>> --- a/drivers/net/can/flexcan/flexcan-ethtool.c
>> +++ b/drivers/net/can/flexcan/flexcan-ethtool.c
>> @@ -28,8 +28,8 @@ flexcan_get_ringparam(struct net_device *ndev, struct ethtool_ringparam *ring,
>>   	ring->tx_max_pending = priv->mb_count;
>>   
>>   	if (priv->devtype_data.quirks & FLEXCAN_QUIRK_USE_RX_MAILBOX)
>> -		ring->rx_pending = priv->offload.mb_last -
>> -			priv->offload.mb_first + 1;
>> +		ring->rx_pending = priv->offload_irq.mb_last -
>> +			priv->offload_irq.mb_first + 1;
>>   	else
>>   		ring->rx_pending = 6;	/* RX-FIFO depth is fixed */
>>   
>> diff --git a/drivers/net/can/flexcan/flexcan.h b/drivers/net/can/flexcan/flexcan.h
>> index 16692a2502eb..90f9c2b9fed8 100644
>> --- a/drivers/net/can/flexcan/flexcan.h
>> +++ b/drivers/net/can/flexcan/flexcan.h
>> @@ -88,6 +88,7 @@ struct flexcan_stop_mode {
>>   struct flexcan_priv {
>>   	struct can_priv can;
>>   	struct can_rx_offload offload;
>> +	struct can_rx_offload_irq offload_irq;
>>   	struct device *dev;
>>   
>>   	struct flexcan_regs __iomem *regs;
>> diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c
>> index 16f80607e150..be66c52a09ff 100644
>> --- a/drivers/net/can/m_can/m_can.c
>> +++ b/drivers/net/can/m_can/m_can.c
>> @@ -543,7 +543,7 @@ static void m_can_receive_skb(struct m_can_classdev *cdev,
>>   		struct net_device_stats *stats = &cdev->net->stats;
>>   		int err;
>>   
>> -		err = can_rx_offload_queue_timestamp(&cdev->offload, skb,
>> +		err = can_rx_offload_queue_timestamp(&cdev->offload_irq, skb,
>>   						     timestamp);
>>   		if (err)
>>   			stats->rx_fifo_errors++;
>> @@ -1100,7 +1100,7 @@ static unsigned int m_can_tx_update_stats(struct m_can_classdev *cdev,
>>   
>>   	if (cdev->is_peripheral)
>>   		stats->tx_bytes +=
>> -			can_rx_offload_get_echo_skb_queue_timestamp(&cdev->offload,
>> +			can_rx_offload_get_echo_skb_queue_timestamp(&cdev->offload_irq,
>>   								    msg_mark,
>>   								    timestamp,
>>   								    &frame_len);
>> @@ -1303,7 +1303,7 @@ static int m_can_interrupt_handler(struct m_can_classdev *cdev)
>>   	}
>>   
>>   	if (cdev->is_peripheral)
>> -		can_rx_offload_threaded_irq_finish(&cdev->offload);
>> +		can_rx_offload_threaded_irq_finish(&cdev->offload_irq);
>>   
>>   	return IRQ_HANDLED;
>>   }
>> @@ -2535,6 +2535,7 @@ int m_can_class_register(struct m_can_classdev *cdev)
>>   
>>   	if (cdev->is_peripheral) {
>>   		ret = can_rx_offload_add_manual(cdev->net, &cdev->offload,
>> +						&cdev->offload_irq,
>>   						NAPI_POLL_WEIGHT);
>>   		if (ret)
>>   			goto out_reset_control_assert;
>> diff --git a/drivers/net/can/m_can/m_can.h b/drivers/net/can/m_can/m_can.h
>> index 4743342b2fba..dfec64aeffa5 100644
>> --- a/drivers/net/can/m_can/m_can.h
>> +++ b/drivers/net/can/m_can/m_can.h
>> @@ -81,6 +81,7 @@ struct m_can_tx_op {
>>   struct m_can_classdev {
>>   	struct can_priv can;
>>   	struct can_rx_offload offload;
>> +	struct can_rx_offload_irq offload_irq;
>>   	struct napi_struct napi;
>>   	struct net_device *net;
>>   	struct device *dev;
>> diff --git a/drivers/net/can/rockchip/rockchip_canfd-core.c b/drivers/net/can/rockchip/rockchip_canfd-core.c
>> index 37c1c22c40c9..db4fa0c8a171 100644
>> --- a/drivers/net/can/rockchip/rockchip_canfd-core.c
>> +++ b/drivers/net/can/rockchip/rockchip_canfd-core.c
>> @@ -563,7 +563,7 @@ static int rkcanfd_handle_error_int(struct rkcanfd_priv *priv)
>>   	if (!cf)
>>   		return 0;
>>   
>> -	err = can_rx_offload_queue_timestamp(&priv->offload, skb, timestamp);
>> +	err = can_rx_offload_queue_timestamp(&priv->offload_irq, skb, timestamp);
>>   	if (err)
>>   		stats->rx_fifo_errors++;
>>   
>> @@ -608,7 +608,7 @@ static int rkcanfd_handle_state_error_int(struct rkcanfd_priv *priv)
>>   		cf->data[7] = bec.rxerr;
>>   	}
>>   
>> -	err = can_rx_offload_queue_timestamp(&priv->offload, skb, timestamp);
>> +	err = can_rx_offload_queue_timestamp(&priv->offload_irq, skb, timestamp);
>>   	if (err)
>>   		stats->rx_fifo_errors++;
>>   
>> @@ -641,7 +641,7 @@ rkcanfd_handle_rx_fifo_overflow_int(struct rkcanfd_priv *priv)
>>   	cf->data[6] = bec.txerr;
>>   	cf->data[7] = bec.rxerr;
>>   
>> -	err = can_rx_offload_queue_timestamp(&priv->offload, skb, timestamp);
>> +	err = can_rx_offload_queue_timestamp(&priv->offload_irq, skb, timestamp);
>>   	if (err)
>>   		stats->rx_fifo_errors++;
>>   
>> @@ -715,7 +715,7 @@ static irqreturn_t rkcanfd_irq(int irq, void *dev_id)
>>   	if (reg_int & RKCANFD_REG_INT_OVERLOAD_INT)
>>   		netdev_info(priv->ndev, "%s: OVERLOAD_INT\n", __func__);
>>   
>> -	can_rx_offload_irq_finish(&priv->offload);
>> +	can_rx_offload_irq_finish(&priv->offload_irq);
>>   
>>   	return IRQ_HANDLED;
>>   }
>> @@ -930,6 +930,7 @@ static int rkcanfd_probe(struct platform_device *pdev)
>>   	}
>>   
>>   	err = can_rx_offload_add_manual(ndev, &priv->offload,
>> +					&priv->offload_irq,
>>   					RKCANFD_NAPI_WEIGHT);
>>   	if (err)
>>   		goto out_free_candev;
>> diff --git a/drivers/net/can/rockchip/rockchip_canfd-rx.c b/drivers/net/can/rockchip/rockchip_canfd-rx.c
>> index 24e87daa1df0..85af71cc60f2 100644
>> --- a/drivers/net/can/rockchip/rockchip_canfd-rx.c
>> +++ b/drivers/net/can/rockchip/rockchip_canfd-rx.c
>> @@ -269,7 +269,7 @@ static int rkcanfd_handle_rx_int_one(struct rkcanfd_priv *priv)
>>   	memcpy(skb_cfd, cfd, len);
>>   	rkcanfd_skb_set_timestamp(priv, skb, header->ts);
>>   
>> -	err = can_rx_offload_queue_timestamp(&priv->offload, skb, header->ts);
>> +	err = can_rx_offload_queue_timestamp(&priv->offload_irq, skb, header->ts);
>>   	if (err)
>>   		stats->rx_fifo_errors++;
>>   
>> diff --git a/drivers/net/can/rockchip/rockchip_canfd-tx.c b/drivers/net/can/rockchip/rockchip_canfd-tx.c
>> index 12200dcfd338..3cfbdb2b8373 100644
>> --- a/drivers/net/can/rockchip/rockchip_canfd-tx.c
>> +++ b/drivers/net/can/rockchip/rockchip_canfd-tx.c
>> @@ -160,7 +160,7 @@ void rkcanfd_handle_tx_done_one(struct rkcanfd_priv *priv, const u32 ts,
>>   	if (skb)
>>   		rkcanfd_skb_set_timestamp(priv, skb, ts);
>>   	stats->tx_bytes +=
>> -		can_rx_offload_get_echo_skb_queue_timestamp(&priv->offload,
>> +		can_rx_offload_get_echo_skb_queue_timestamp(&priv->offload_irq,
>>   							    tx_tail, ts,
>>   							    frame_len_p);
>>   	stats->tx_packets++;
>> diff --git a/drivers/net/can/rockchip/rockchip_canfd.h b/drivers/net/can/rockchip/rockchip_canfd.h
>> index 95bea9bfd8a2..6b4cdbaffe4a 100644
>> --- a/drivers/net/can/rockchip/rockchip_canfd.h
>> +++ b/drivers/net/can/rockchip/rockchip_canfd.h
>> @@ -472,6 +472,7 @@ struct rkcanfd_stats {
>>   struct rkcanfd_priv {
>>   	struct can_priv can;
>>   	struct can_rx_offload offload;
>> +	struct can_rx_offload_irq offload_irq;
>>   	struct net_device *ndev;
>>   
>>   	void __iomem *regs;
>> diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c b/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c
>> index f441f2265299..e4ad461cd9e8 100644
>> --- a/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c
>> +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c
>> @@ -972,7 +972,7 @@ static int mcp251xfd_handle_rxovif(struct mcp251xfd_priv *priv)
>>   	cf->can_id |= CAN_ERR_CRTL;
>>   	cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
>>   
>> -	err = can_rx_offload_queue_timestamp(&priv->offload, skb, ts_raw);
>> +	err = can_rx_offload_queue_timestamp(&priv->offload_irq, skb, ts_raw);
>>   	if (err)
>>   		stats->rx_fifo_errors++;
>>   
>> @@ -1077,7 +1077,7 @@ static int mcp251xfd_handle_ivmif(struct mcp251xfd_priv *priv)
>>   		return 0;
>>   
>>   	mcp251xfd_skb_set_timestamp_raw(priv, skb, ts_raw);
>> -	err = can_rx_offload_queue_timestamp(&priv->offload, skb, ts_raw);
>> +	err = can_rx_offload_queue_timestamp(&priv->offload_irq, skb, ts_raw);
>>   	if (err)
>>   		stats->rx_fifo_errors++;
>>   
>> @@ -1151,7 +1151,7 @@ static int mcp251xfd_handle_cerrif(struct mcp251xfd_priv *priv)
>>   		cf->data[7] = bec.rxerr;
>>   	}
>>   
>> -	err = can_rx_offload_queue_timestamp(&priv->offload, skb, ts_raw);
>> +	err = can_rx_offload_queue_timestamp(&priv->offload_irq, skb, ts_raw);
>>   	if (err)
>>   		stats->rx_fifo_errors++;
>>   
>> @@ -1515,7 +1515,7 @@ static irqreturn_t mcp251xfd_irq(int irq, void *dev_id)
>>   				  priv->regs_status.intf);
>>   
>>   		if (!(intf_pending)) {
>> -			can_rx_offload_threaded_irq_finish(&priv->offload);
>> +			can_rx_offload_threaded_irq_finish(&priv->offload_irq);
>>   			return handled;
>>   		}
>>   
>> @@ -1610,7 +1610,7 @@ static irqreturn_t mcp251xfd_irq(int irq, void *dev_id)
>>   			 * directly.
>>   			 */
>>   			if (priv->can.state == CAN_STATE_BUS_OFF) {
>> -				can_rx_offload_threaded_irq_finish(&priv->offload);
>> +				can_rx_offload_threaded_irq_finish(&priv->offload_irq);
>>   				return IRQ_HANDLED;
>>   			}
>>   		}
>> @@ -1619,7 +1619,7 @@ static irqreturn_t mcp251xfd_irq(int irq, void *dev_id)
>>   	} while (1);
>>   
>>   out_fail:
>> -	can_rx_offload_threaded_irq_finish(&priv->offload);
>> +	can_rx_offload_threaded_irq_finish(&priv->offload_irq);
>>   
>>   	netdev_err(priv->ndev, "IRQ handler returned %d (intf=0x%08x).\n",
>>   		   err, priv->regs_status.intf);
>> @@ -2358,6 +2358,7 @@ static int mcp251xfd_probe(struct spi_device *spi)
>>   		goto out_free_candev;
>>   
>>   	err = can_rx_offload_add_manual(ndev, &priv->offload,
>> +					&priv->offload_irq,
>>   					MCP251XFD_NAPI_WEIGHT);
>>   	if (err)
>>   		goto out_free_candev;
>> diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-rx.c b/drivers/net/can/spi/mcp251xfd/mcp251xfd-rx.c
>> index fe897f3e4c12..8c5a371bcf61 100644
>> --- a/drivers/net/can/spi/mcp251xfd/mcp251xfd-rx.c
>> +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-rx.c
>> @@ -201,7 +201,7 @@ mcp251xfd_handle_rxif_one(struct mcp251xfd_priv *priv,
>>   
>>   	mcp251xfd_skb_set_timestamp(skb, timestamp);
>>   	mcp251xfd_hw_rx_obj_to_skb(priv, hw_rx_obj, skb);
>> -	err = can_rx_offload_queue_timestamp(&priv->offload, skb, hw_rx_obj->ts);
>> +	err = can_rx_offload_queue_timestamp(&priv->offload_irq, skb, hw_rx_obj->ts);
>>   	if (err)
>>   		stats->rx_fifo_errors++;
>>   
>> diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-tef.c b/drivers/net/can/spi/mcp251xfd/mcp251xfd-tef.c
>> index e94321849fd7..752068691958 100644
>> --- a/drivers/net/can/spi/mcp251xfd/mcp251xfd-tef.c
>> +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-tef.c
>> @@ -104,7 +104,7 @@ mcp251xfd_handle_tefif_one(struct mcp251xfd_priv *priv,
>>   	if (skb)
>>   		mcp251xfd_skb_set_timestamp_raw(priv, skb, hw_tef_obj->ts);
>>   	stats->tx_bytes +=
>> -		can_rx_offload_get_echo_skb_queue_timestamp(&priv->offload,
>> +		can_rx_offload_get_echo_skb_queue_timestamp(&priv->offload_irq,
>>   							    tef_tail, hw_tef_obj->ts,
>>   							    frame_len_ptr);
>>   	stats->tx_packets++;
>> diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd.h b/drivers/net/can/spi/mcp251xfd/mcp251xfd.h
>> index d3f4704e2678..0706bc5459be 100644
>> --- a/drivers/net/can/spi/mcp251xfd/mcp251xfd.h
>> +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd.h
>> @@ -624,6 +624,7 @@ enum mcp251xfd_flags {
>>   struct mcp251xfd_priv {
>>   	struct can_priv can;
>>   	struct can_rx_offload offload;
>> +	struct can_rx_offload_irq offload_irq;
>>   	struct net_device *ndev;
>>   
>>   	struct regmap *map_reg;			/* register access */
>> diff --git a/drivers/net/can/ti_hecc.c b/drivers/net/can/ti_hecc.c
>> index 1d3dbf28b105..bf7a2df0435a 100644
>> --- a/drivers/net/can/ti_hecc.c
>> +++ b/drivers/net/can/ti_hecc.c
>> @@ -177,6 +177,7 @@ static const struct can_bittiming_const ti_hecc_bittiming_const = {
>>   struct ti_hecc_priv {
>>   	struct can_priv can;	/* MUST be first member/field */
>>   	struct can_rx_offload offload;
>> +	struct can_rx_offload_irq offload_irq;
>>   	struct net_device *ndev;
>>   	struct clk *clk;
>>   	void __iomem *base;
>> @@ -623,7 +624,7 @@ static int ti_hecc_error(struct net_device *ndev, int int_status,
>>   			cf->data[3] = CAN_ERR_PROT_LOC_ACK;
>>   
>>   		timestamp = hecc_read(priv, HECC_CANLNT);
>> -		err = can_rx_offload_queue_timestamp(&priv->offload, skb,
>> +		err = can_rx_offload_queue_timestamp(&priv->offload_irq, skb,
>>   						     timestamp);
>>   		if (err)
>>   			ndev->stats.rx_fifo_errors++;
>> @@ -659,7 +660,7 @@ static void ti_hecc_change_state(struct net_device *ndev,
>>   	}
>>   
>>   	timestamp = hecc_read(priv, HECC_CANLNT);
>> -	err = can_rx_offload_queue_timestamp(&priv->offload, skb, timestamp);
>> +	err = can_rx_offload_queue_timestamp(&priv->offload_irq, skb, timestamp);
>>   	if (err)
>>   		ndev->stats.rx_fifo_errors++;
>>   }
>> @@ -747,7 +748,7 @@ static irqreturn_t ti_hecc_interrupt(int irq, void *dev_id)
>>   			spin_unlock_irqrestore(&priv->mbx_lock, flags);
>>   			stamp = hecc_read_stamp(priv, mbxno);
>>   			stats->tx_bytes +=
>> -				can_rx_offload_get_echo_skb_queue_timestamp(&priv->offload,
>> +				can_rx_offload_get_echo_skb_queue_timestamp(&priv->offload_irq,
>>   									    mbxno, stamp, NULL);
>>   			stats->tx_packets++;
>>   			--priv->tx_tail;
>> @@ -762,7 +763,7 @@ static irqreturn_t ti_hecc_interrupt(int irq, void *dev_id)
>>   
>>   		/* offload RX mailboxes and let NAPI deliver them */
>>   		while ((rx_pending = hecc_read(priv, HECC_CANRMP))) {
>> -			can_rx_offload_irq_offload_timestamp(&priv->offload,
>> +			can_rx_offload_irq_offload_timestamp(&priv->offload_irq,
>>   							     rx_pending);
>>   		}
>>   	}
>> @@ -776,7 +777,7 @@ static irqreturn_t ti_hecc_interrupt(int irq, void *dev_id)
>>   		int_status = hecc_read(priv, HECC_CANGIF0);
>>   	}
>>   
>> -	can_rx_offload_irq_finish(&priv->offload);
>> +	can_rx_offload_irq_finish(&priv->offload_irq);
>>   
>>   	return IRQ_HANDLED;
>>   }
>> @@ -930,9 +931,10 @@ static int ti_hecc_probe(struct platform_device *pdev)
>>   	}
>>   
>>   	priv->offload.mailbox_read = ti_hecc_mailbox_read;
>> -	priv->offload.mb_first = HECC_RX_FIRST_MBOX;
>> -	priv->offload.mb_last = HECC_RX_LAST_MBOX;
>> -	err = can_rx_offload_add_timestamp(ndev, &priv->offload);
>> +	priv->offload_irq.mb_first = HECC_RX_FIRST_MBOX;
>> +	priv->offload_irq.mb_last = HECC_RX_LAST_MBOX;
>> +	err = can_rx_offload_add_timestamp(ndev, &priv->offload,
>> +					   &priv->offload_irq);
>>   	if (err) {
>>   		dev_err(&pdev->dev, "can_rx_offload_add_timestamp() failed\n");
>>   		goto probe_exit_disable_clk;
>> diff --git a/drivers/net/can/usb/gs_usb.c b/drivers/net/can/usb/gs_usb.c
>> index 3b9b2f104d86..db9c17642131 100644
>> --- a/drivers/net/can/usb/gs_usb.c
>> +++ b/drivers/net/can/usb/gs_usb.c
>> @@ -305,6 +305,7 @@ struct gs_can {
>>   	struct can_priv can; /* must be the first member */
>>   
>>   	struct can_rx_offload offload;
>> +	struct can_rx_offload_irq offload_irq;
>>   	struct gs_usb *parent;
>>   
>>   	struct net_device *netdev;
>> @@ -539,15 +540,15 @@ static u32 gs_usb_set_timestamp(struct gs_can *dev, struct sk_buff *skb,
>>   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;
>> +	struct can_rx_offload_irq *offload_irq = &dev->offload_irq;
>>   	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);
>> +		rc = can_rx_offload_queue_timestamp(offload_irq, skb, ts);
>>   	} else {
>> -		rc = can_rx_offload_queue_tail(offload, skb);
>> +		rc = can_rx_offload_queue_tail(offload_irq, skb);
>>   	}
>>   
>>   	if (rc)
>> @@ -558,18 +559,19 @@ 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;
>> +	struct can_rx_offload_irq *offload_irq = &dev->offload_irq;
>>   	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);
>> +		len = can_rx_offload_get_echo_skb_queue_timestamp(offload_irq,
>> +								  echo_id, ts,
>> +								  NULL);
>>   	} else {
>> -		len = can_rx_offload_get_echo_skb_queue_tail(offload, echo_id,
>> -							     NULL);
>> +		len = can_rx_offload_get_echo_skb_queue_tail(offload_irq,
>> +							     echo_id, NULL);
>>   	}
>>   
>>   	return len;
>> @@ -743,7 +745,7 @@ static void gs_usb_receive_bulk_callback(struct urb *urb)
>>   		gs_usb_rx_offload(dev, skb, hf);
>>   	}
>>   
>> -	can_rx_offload_irq_finish(&dev->offload);
>> +	can_rx_offload_irq_finish(&dev->offload_irq);
>>   
>>   resubmit_urb:
>>   	usb_fill_bulk_urb(urb, parent->udev,
>> @@ -1487,7 +1489,8 @@ static struct gs_can *gs_make_candev(unsigned int channel,
>>   		dev->can.fd.data_bittiming_const = &dev->data_bt_const;
>>   	}
>>   
>> -	can_rx_offload_add_manual(netdev, &dev->offload, GS_NAPI_WEIGHT);
>> +	can_rx_offload_add_manual(netdev, &dev->offload,
>> +				  &dev->offload_irq, GS_NAPI_WEIGHT);
>>   	SET_NETDEV_DEV(netdev, &intf->dev);
>>   
>>   	rc = register_candev(dev->netdev);
>> diff --git a/drivers/net/can/usb/nct6694_canfd.c b/drivers/net/can/usb/nct6694_canfd.c
>> index e5f7f8849a73..7d0360708b93 100644
>> --- a/drivers/net/can/usb/nct6694_canfd.c
>> +++ b/drivers/net/can/usb/nct6694_canfd.c
>> @@ -133,6 +133,7 @@ struct __packed nct6694_canfd_frame {
>>   struct nct6694_canfd_priv {
>>   	struct can_priv can;	/* must be the first member */
>>   	struct can_rx_offload offload;
>> +	struct can_rx_offload_irq offload_irq;
>>   	struct net_device *ndev;
>>   	struct nct6694 *nct6694;
>>   	struct workqueue_struct *wq;
>> @@ -172,13 +173,13 @@ static const struct can_bittiming_const nct6694_canfd_bittiming_data_const = {
>>   	.brp_inc = 1,
>>   };
>>   
>> -static void nct6694_canfd_rx_offload(struct can_rx_offload *offload,
>> +static void nct6694_canfd_rx_offload(struct can_rx_offload_irq *offload_irq,
>>   				     struct sk_buff *skb)
>>   {
>> -	struct nct6694_canfd_priv *priv = rx_offload_to_priv(offload);
>> +	struct nct6694_canfd_priv *priv = rx_offload_to_priv(offload_irq->offload);
>>   	int ret;
>>   
>> -	ret = can_rx_offload_queue_tail(offload, skb);
>> +	ret = can_rx_offload_queue_tail(offload_irq, skb);
>>   	if (ret)
>>   		priv->ndev->stats.rx_fifo_errors++;
>>   }
>> @@ -202,7 +203,7 @@ static void nct6694_canfd_handle_lost_msg(struct net_device *ndev)
>>   	cf->can_id |= CAN_ERR_CRTL;
>>   	cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
>>   
>> -	nct6694_canfd_rx_offload(&priv->offload, skb);
>> +	nct6694_canfd_rx_offload(&priv->offload_irq, skb);
>>   }
>>   
>>   static void nct6694_canfd_handle_rx(struct net_device *ndev, u8 rx_evt)
>> @@ -262,7 +263,7 @@ static void nct6694_canfd_handle_rx(struct net_device *ndev, u8 rx_evt)
>>   			memcpy(cf->data, frame->data, cf->len);
>>   	}
>>   
>> -	nct6694_canfd_rx_offload(&priv->offload, skb);
>> +	nct6694_canfd_rx_offload(&priv->offload_irq, skb);
>>   }
>>   
>>   static int nct6694_canfd_get_berr_counter(const struct net_device *ndev,
>> @@ -305,7 +306,7 @@ static void nct6694_canfd_handle_state_change(struct net_device *ndev, u8 status
>>   	}
>>   
>>   	if (skb)
>> -		nct6694_canfd_rx_offload(&priv->offload, skb);
>> +		nct6694_canfd_rx_offload(&priv->offload_irq, skb);
>>   }
>>   
>>   static void nct6694_canfd_handle_bus_err(struct net_device *ndev, u8 bus_err)
>> @@ -363,7 +364,7 @@ static void nct6694_canfd_handle_bus_err(struct net_device *ndev, u8 bus_err)
>>   	}
>>   
>>   	if (skb)
>> -		nct6694_canfd_rx_offload(&priv->offload, skb);
>> +		nct6694_canfd_rx_offload(&priv->offload_irq, skb);
>>   }
>>   
>>   static void nct6694_canfd_handle_tx(struct net_device *ndev)
>> @@ -371,7 +372,7 @@ static void nct6694_canfd_handle_tx(struct net_device *ndev)
>>   	struct nct6694_canfd_priv *priv = netdev_priv(ndev);
>>   	struct net_device_stats *stats = &ndev->stats;
>>   
>> -	stats->tx_bytes += can_rx_offload_get_echo_skb_queue_tail(&priv->offload,
>> +	stats->tx_bytes += can_rx_offload_get_echo_skb_queue_tail(&priv->offload_irq,
>>   								  0, NULL);
>>   	stats->tx_packets++;
>>   	netif_wake_queue(ndev);
>> @@ -422,7 +423,7 @@ static irqreturn_t nct6694_canfd_irq(int irq, void *data)
>>   	}
>>   
>>   	if (handled)
>> -		can_rx_offload_threaded_irq_finish(&priv->offload);
>> +		can_rx_offload_threaded_irq_finish(&priv->offload_irq);
>>   
>>   	priv->bec.rxerr = event->rec;
>>   	priv->bec.txerr = event->tec;
>> @@ -775,6 +776,7 @@ static int nct6694_canfd_probe(struct platform_device *pdev)
>>   		goto free_candev;
>>   
>>   	ret = can_rx_offload_add_manual(ndev, &priv->offload,
>> +					&priv->offload_irq,
>>   					NCT6694_NAPI_WEIGHT);
>>   	if (ret) {
>>   		dev_err_probe(&pdev->dev, ret, "Failed to add rx_offload\n");
>> diff --git a/include/linux/can/rx-offload.h b/include/linux/can/rx-offload.h
>> index d29bb4521947..6f9a5739ecf5 100644
>> --- a/include/linux/can/rx-offload.h
>> +++ b/include/linux/can/rx-offload.h
>> @@ -20,40 +20,66 @@ struct can_rx_offload {
>>   					bool drop);
>>   
>>   	struct sk_buff_head skb_queue;
>> +
>> +	/* Registered per-IRQ contexts, only changed on add/del. A single
>> +	 * context lets the finish path splice, more than one makes it sort
>> +	 * the skbs into skb_queue.
>> +	 */
>> +	struct list_head irqs;
>> +	unsigned int irq_cnt;
>> +
>> +	/* Set if the queued skbs carry a timestamp to sort them by. */
>> +	bool sort;
>> +
>> +	/* weight is the largest one over the registered per-IRQ contexts */
>> +	struct napi_struct napi;
>> +};
>> +
>> +/* Per-IRQ receive context, one per IRQ line producing RX frames.
>> + * skb_irq_queue has a single producer, so it needs no lock. The finish
>> + * helpers move the skbs into can_rx_offload::skb_queue under its lock.
>> + */
>> +struct can_rx_offload_irq {
>> +	struct can_rx_offload *offload;
>> +
>>   	struct sk_buff_head skb_irq_queue;
>>   	u32 skb_queue_len_max;
>>   
>>   	unsigned int mb_first;
>>   	unsigned int mb_last;
>>   
>> -	struct napi_struct napi;
>> -
>>   	bool inc;
>> +
>> +	/* entry on can_rx_offload::irqs */
>> +	struct list_head node;
>>   };
>>   
>>   int can_rx_offload_add_timestamp(struct net_device *dev,
>> -				 struct can_rx_offload *offload);
>> +				 struct can_rx_offload *offload,
>> +				 struct can_rx_offload_irq *offload_irq);
>>   int can_rx_offload_add_fifo(struct net_device *dev,
>>   			    struct can_rx_offload *offload,
>> +			    struct can_rx_offload_irq *offload_irq,
>>   			    unsigned int weight);
>>   int can_rx_offload_add_manual(struct net_device *dev,
>>   			      struct can_rx_offload *offload,
>> +			      struct can_rx_offload_irq *offload_irq,
>>   			      unsigned int weight);
>> -int can_rx_offload_irq_offload_timestamp(struct can_rx_offload *offload,
>> +int can_rx_offload_irq_offload_timestamp(struct can_rx_offload_irq *offload_irq,
>>   					 u64 reg);
>> -int can_rx_offload_irq_offload_fifo(struct can_rx_offload *offload);
>> -int can_rx_offload_queue_timestamp(struct can_rx_offload *offload,
>> +int can_rx_offload_irq_offload_fifo(struct can_rx_offload_irq *offload_irq);
>> +int can_rx_offload_queue_timestamp(struct can_rx_offload_irq *offload_irq,
>>   				   struct sk_buff *skb, u32 timestamp);
>> -unsigned int can_rx_offload_get_echo_skb_queue_timestamp(struct can_rx_offload *offload,
>> +unsigned int can_rx_offload_get_echo_skb_queue_timestamp(struct can_rx_offload_irq *offload_irq,
>>   							 unsigned int idx, u32 timestamp,
>>   							 unsigned int *frame_len_ptr);
>> -int can_rx_offload_queue_tail(struct can_rx_offload *offload,
>> +int can_rx_offload_queue_tail(struct can_rx_offload_irq *offload_irq,
>>   			      struct sk_buff *skb);
>> -unsigned int can_rx_offload_get_echo_skb_queue_tail(struct can_rx_offload *offload,
>> +unsigned int can_rx_offload_get_echo_skb_queue_tail(struct can_rx_offload_irq *offload_irq,
>>   						    unsigned int idx,
>>   						    unsigned int *frame_len_ptr);
>> -void can_rx_offload_irq_finish(struct can_rx_offload *offload);
>> -void can_rx_offload_threaded_irq_finish(struct can_rx_offload *offload);
>> +void can_rx_offload_irq_finish(struct can_rx_offload_irq *offload_irq);
>> +void can_rx_offload_threaded_irq_finish(struct can_rx_offload_irq *offload_irq);
>>   void can_rx_offload_del(struct can_rx_offload *offload);
>>   void can_rx_offload_enable(struct can_rx_offload *offload);
>>   
>> -- 
>> 2.43.0
>>




More information about the Linux-rockchip mailing list