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 | 74 ++++++++++++++++++++++++++++++------------ include/linux/can/rx-offload.h | 5 +++ 2 files changed, 58 insertions(+), 21 deletions(-) diff --git a/drivers/net/can/rx-offload.c b/drivers/net/can/rx-offload.c index 3b18026..cf60896 100644 --- a/drivers/net/can/rx-offload.c +++ b/drivers/net/can/rx-offload.c @@ -174,10 +174,9 @@ static int can_rx_offload_compare(struct sk_buff *a, struct sk_buff *b) int can_rx_offload_irq_offload_timestamp(struct can_rx_offload *offload, u64 pending) { - struct sk_buff_head skb_queue; unsigned int i; - __skb_queue_head_init(&skb_queue); + can_rx_offload_irq_start(offload); for (i = offload->mb_first; can_rx_offload_le(offload, i, offload->mb_last); @@ -191,26 +190,13 @@ int can_rx_offload_irq_offload_timestamp(struct can_rx_offload *offload, if (IS_ERR_OR_NULL(skb)) continue; - __skb_queue_add_sort(&skb_queue, skb, can_rx_offload_compare); + __skb_queue_add_sort(&offload->irq_skb_queue, skb, + can_rx_offload_compare); } - if (!skb_queue_empty(&skb_queue)) { - unsigned long flags; - u32 queue_len; + can_rx_offload_irq_end(offload); - spin_lock_irqsave(&offload->skb_queue.lock, flags); - skb_queue_splice_tail(&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); - } - - return skb_queue_len(&skb_queue); + return skb_queue_len(&offload->irq_skb_queue); } EXPORT_SYMBOL_GPL(can_rx_offload_irq_offload_timestamp); @@ -219,6 +205,7 @@ int can_rx_offload_irq_offload_fifo(struct can_rx_offload *offload) struct sk_buff *skb; int received = 0; + can_rx_offload_irq_start(offload); while (1) { skb = can_rx_offload_offload_one(offload, 0); if (IS_ERR(skb)) @@ -227,11 +214,11 @@ int can_rx_offload_irq_offload_fifo(struct can_rx_offload *offload) break; skb_queue_tail(&offload->skb_queue, skb); + __skb_queue_tail(&offload->irq_skb_queue, skb); received++; } - if (received) - can_rx_offload_schedule(offload); + can_rx_offload_irq_end(offload); return received; } @@ -301,6 +288,51 @@ int can_rx_offload_queue_tail(struct can_rx_offload *offload, } EXPORT_SYMBOL_GPL(can_rx_offload_queue_tail); +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 f1b3808..f83f53e 100644 --- a/include/linux/can/rx-offload.h +++ b/include/linux/can/rx-offload.h @@ -20,6 +20,7 @@ struct can_rx_offload { bool drop); struct sk_buff_head skb_queue; + struct sk_buff_head irq_skb_queue; u32 skb_queue_len_max; unsigned int mb_first; @@ -47,6 +48,10 @@ unsigned int can_rx_offload_get_echo_skb(struct can_rx_offload *offload, unsigned int idx, u32 timestamp); int can_rx_offload_queue_tail(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