[PATCH 02/11] can: rx-offload: add skb queue for use during ISR

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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




[Index of Archives]     [Automotive Discussions]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux]     [Linux OMAP]     [Linux MIPS]     [eCos]     [Asterisk Internet PBX]     [Linux API]     [CAN Bus]

  Powered by Linux