When using rx-offload in manual mode, each skb requires a lock on the skb_queue. This commit avoids this by adding an unlocked skb queue that is appended at once at the end of the ISR. Having 1 lock at the end of the ISR should be ok as the HW is empty, not about to overlow. Signed-off-by: Kurt Van Dijck <dev.kurt@xxxxxxxxxxxxxxxxxxxxxx> --- drivers/net/can/rx-offload.c | 45 ++++++++++++++++++++++++++++++++++++++++++ include/linux/can/rx-offload.h | 5 +++++ 2 files changed, 50 insertions(+) diff --git a/drivers/net/can/rx-offload.c b/drivers/net/can/rx-offload.c index 138b8b8..5613e7e 100644 --- a/drivers/net/can/rx-offload.c +++ b/drivers/net/can/rx-offload.c @@ -287,6 +287,51 @@ int can_rx_offload_receive_skb(struct can_rx_offload *offload, } EXPORT_SYMBOL_GPL(can_rx_offload_receive_skb); +void can_rx_offload_irq_start(struct can_rx_offload *offload) +{ + __skb_queue_head_init(&offload->irq_skb_queue); +} +EXPORT_SYMBOL_GPL(can_rx_offload_irq_start); + +int can_rx_offload_irq_receive_skb(struct can_rx_offload *offload, + struct sk_buff *skb) +{ + struct net_device_stats *stats = &offload->dev->stats; + + if (skb_queue_len(&offload->skb_queue) + + skb_queue_len(&offload->irq_skb_queue) + >= offload->skb_queue_len_max) { + kfree_skb(skb); + stats->rx_errors++; + stats->rx_fifo_errors++; + return -ENOMEM; + } + __skb_queue_tail(&offload->irq_skb_queue, skb); + return 0; +} +EXPORT_SYMBOL_GPL(can_rx_offload_receive_skb); + + +void can_rx_offload_irq_end(struct can_rx_offload *offload) +{ + int queue_len; + unsigned long flags; + + if (skb_queue_empty(&offload->irq_skb_queue)) + return; + spin_lock_irqsave(&offload->skb_queue.lock, flags); + skb_queue_splice_tail(&offload->irq_skb_queue, &offload->skb_queue); + spin_unlock_irqrestore(&offload->skb_queue.lock, flags); + + queue_len = skb_queue_len(&offload->skb_queue); + if (queue_len > offload->skb_queue_len_max / 8) + netdev_dbg(offload->dev, "%s: queue_len=%d\n", + __func__, queue_len); + + can_rx_offload_schedule(offload); +} +EXPORT_SYMBOL_GPL(can_rx_offload_irq_end); + static int can_rx_offload_init_queue(struct net_device *dev, struct can_rx_offload *offload, unsigned int weight) diff --git a/include/linux/can/rx-offload.h b/include/linux/can/rx-offload.h index c324233..6b0b73c 100644 --- a/include/linux/can/rx-offload.h +++ b/include/linux/can/rx-offload.h @@ -20,6 +20,7 @@ struct can_rx_offload { u32 *timestamp, unsigned int mb); struct sk_buff_head skb_queue; + struct sk_buff_head irq_skb_queue; u32 skb_queue_len_max; unsigned int mb_first; @@ -49,6 +50,10 @@ int can_rx_offload_queue_tail(struct can_rx_offload *offload, struct sk_buff *skb); int can_rx_offload_receive_skb(struct can_rx_offload *offload, struct sk_buff *skb); +void can_rx_offload_irq_start(struct can_rx_offload *offload); +void can_rx_offload_irq_end(struct can_rx_offload *offload); +int can_rx_offload_irq_receive_skb(struct can_rx_offload *offload, + struct sk_buff *skb); void can_rx_offload_del(struct can_rx_offload *offload); void can_rx_offload_enable(struct can_rx_offload *offload); -- 1.8.5.rc3