linux-can.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/3] can: kvaser_pciefd: Fix ISR race conditions
@ 2025-03-31  7:25 Axel Forsman
  2025-03-31  7:25 ` [PATCH 1/3] can: kvaser_pciefd: Force IRQ edge in case of nested IRQ Axel Forsman
                   ` (2 more replies)
  0 siblings, 3 replies; 8+ messages in thread
From: Axel Forsman @ 2025-03-31  7:25 UTC (permalink / raw)
  To: linux-can; +Cc: mkl, mailhol.vincent, Axel Forsman

This patch series fixes a couple of race conditions in the
kvaser_pciefd driver surfaced by enabling MSI interrupts and the new
Kvaser PCIe 8xCAN.

Axel Forsman (3):
  can: kvaser_pciefd: Force IRQ edge in case of nested IRQ
  can: kvaser_pciefd: Fix echo_skb race conditions
  can: kvaser_pciefd: Continue parsing DMA buf after dropped RX

 drivers/net/can/kvaser_pciefd.c | 161 ++++++++++++++++++--------------
 1 file changed, 92 insertions(+), 69 deletions(-)

-- 
2.47.2


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 1/3] can: kvaser_pciefd: Force IRQ edge in case of nested IRQ
  2025-03-31  7:25 [PATCH 0/3] can: kvaser_pciefd: Fix ISR race conditions Axel Forsman
@ 2025-03-31  7:25 ` Axel Forsman
  2025-04-07 14:34   ` Marc Kleine-Budde
  2025-03-31  7:25 ` [PATCH 2/3] can: kvaser_pciefd: Fix echo_skb race conditions Axel Forsman
  2025-03-31  7:25 ` [PATCH 3/3] can: kvaser_pciefd: Continue parsing DMA buf after dropped RX Axel Forsman
  2 siblings, 1 reply; 8+ messages in thread
From: Axel Forsman @ 2025-03-31  7:25 UTC (permalink / raw)
  To: linux-can; +Cc: mkl, mailhol.vincent, Axel Forsman, stable, Jimmy Assarsson

Avoid the driver missing IRQs by temporarily masking IRQs in the ISR
to enforce an edge even if a different IRQ is signalled before handled
IRQs are cleared.

Fixes: 48f827d4f48f ("can: kvaser_pciefd: Move reset of DMA RX buffers to the end of the ISR")
Cc: stable@vger.kernel.org
Signed-off-by: Axel Forsman <axfo@kvaser.com>
Tested-by: Jimmy Assarsson <extja@kvaser.com>
Reviewed-by: Jimmy Assarsson <extja@kvaser.com>
---
 drivers/net/can/kvaser_pciefd.c | 83 ++++++++++++++++-----------------
 1 file changed, 39 insertions(+), 44 deletions(-)

diff --git a/drivers/net/can/kvaser_pciefd.c b/drivers/net/can/kvaser_pciefd.c
index fa04a7ced02b..0d1b895509c3 100644
--- a/drivers/net/can/kvaser_pciefd.c
+++ b/drivers/net/can/kvaser_pciefd.c
@@ -1646,24 +1646,28 @@ static int kvaser_pciefd_read_buffer(struct kvaser_pciefd *pcie, int dma_buf)
 	return res;
 }
 
-static u32 kvaser_pciefd_receive_irq(struct kvaser_pciefd *pcie)
+static void kvaser_pciefd_receive_irq(struct kvaser_pciefd *pcie)
 {
+	__le32 __iomem *srb_cmd_reg = KVASER_PCIEFD_SRB_ADDR(pcie) + KVASER_PCIEFD_SRB_CMD_REG;
 	u32 irq = ioread32(KVASER_PCIEFD_SRB_ADDR(pcie) + KVASER_PCIEFD_SRB_IRQ_REG);
 
-	if (irq & KVASER_PCIEFD_SRB_IRQ_DPD0)
-		kvaser_pciefd_read_buffer(pcie, 0);
+	iowrite32(irq, KVASER_PCIEFD_SRB_ADDR(pcie) + KVASER_PCIEFD_SRB_IRQ_REG);
 
-	if (irq & KVASER_PCIEFD_SRB_IRQ_DPD1)
+	if (irq & KVASER_PCIEFD_SRB_IRQ_DPD0) {
+		kvaser_pciefd_read_buffer(pcie, 0);
+		iowrite32(KVASER_PCIEFD_SRB_CMD_RDB0, srb_cmd_reg); /* Rearm buffer */
+	}
+
+	if (irq & KVASER_PCIEFD_SRB_IRQ_DPD1) {
 		kvaser_pciefd_read_buffer(pcie, 1);
+		iowrite32(KVASER_PCIEFD_SRB_CMD_RDB1, srb_cmd_reg); /* Rearm buffer */
+	}
 
 	if (unlikely(irq & KVASER_PCIEFD_SRB_IRQ_DOF0 ||
 		     irq & KVASER_PCIEFD_SRB_IRQ_DOF1 ||
 		     irq & KVASER_PCIEFD_SRB_IRQ_DUF0 ||
 		     irq & KVASER_PCIEFD_SRB_IRQ_DUF1))
 		dev_err(&pcie->pci->dev, "DMA IRQ error 0x%08X\n", irq);
-
-	iowrite32(irq, KVASER_PCIEFD_SRB_ADDR(pcie) + KVASER_PCIEFD_SRB_IRQ_REG);
-	return irq;
 }
 
 static void kvaser_pciefd_transmit_irq(struct kvaser_pciefd_can *can)
@@ -1691,29 +1695,22 @@ static irqreturn_t kvaser_pciefd_irq_handler(int irq, void *dev)
 	struct kvaser_pciefd *pcie = (struct kvaser_pciefd *)dev;
 	const struct kvaser_pciefd_irq_mask *irq_mask = pcie->driver_data->irq_mask;
 	u32 pci_irq = ioread32(KVASER_PCIEFD_PCI_IRQ_ADDR(pcie));
-	u32 srb_irq = 0;
-	u32 srb_release = 0;
 	int i;
 
 	if (!(pci_irq & irq_mask->all))
 		return IRQ_NONE;
 
+	iowrite32(0, KVASER_PCIEFD_PCI_IEN_ADDR(pcie));
+
 	if (pci_irq & irq_mask->kcan_rx0)
-		srb_irq = kvaser_pciefd_receive_irq(pcie);
+		kvaser_pciefd_receive_irq(pcie);
 
 	for (i = 0; i < pcie->nr_channels; i++) {
 		if (pci_irq & irq_mask->kcan_tx[i])
 			kvaser_pciefd_transmit_irq(pcie->can[i]);
 	}
 
-	if (srb_irq & KVASER_PCIEFD_SRB_IRQ_DPD0)
-		srb_release |= KVASER_PCIEFD_SRB_CMD_RDB0;
-
-	if (srb_irq & KVASER_PCIEFD_SRB_IRQ_DPD1)
-		srb_release |= KVASER_PCIEFD_SRB_CMD_RDB1;
-
-	if (srb_release)
-		iowrite32(srb_release, KVASER_PCIEFD_SRB_ADDR(pcie) + KVASER_PCIEFD_SRB_CMD_REG);
+	iowrite32(irq_mask->all, KVASER_PCIEFD_PCI_IEN_ADDR(pcie));
 
 	return IRQ_HANDLED;
 }
@@ -1733,13 +1730,22 @@ static void kvaser_pciefd_teardown_can_ctrls(struct kvaser_pciefd *pcie)
 	}
 }
 
+static void kvaser_pciefd_disable_irq_srcs(struct kvaser_pciefd *pcie)
+{
+	unsigned int i;
+
+	/* Masking PCI_IRQ is insufficient as running ISR will unmask it */
+	iowrite32(0, KVASER_PCIEFD_SRB_ADDR(pcie) + KVASER_PCIEFD_SRB_IEN_REG);
+	for (i = 0; i < pcie->nr_channels; ++i)
+		iowrite32(0, pcie->can[i]->reg_base + KVASER_PCIEFD_KCAN_IEN_REG);
+}
+
 static int kvaser_pciefd_probe(struct pci_dev *pdev,
 			       const struct pci_device_id *id)
 {
 	int ret;
 	struct kvaser_pciefd *pcie;
 	const struct kvaser_pciefd_irq_mask *irq_mask;
-	void __iomem *irq_en_base;
 
 	pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL);
 	if (!pcie)
@@ -1805,8 +1811,7 @@ static int kvaser_pciefd_probe(struct pci_dev *pdev,
 		  KVASER_PCIEFD_SRB_ADDR(pcie) + KVASER_PCIEFD_SRB_IEN_REG);
 
 	/* Enable PCI interrupts */
-	irq_en_base = KVASER_PCIEFD_PCI_IEN_ADDR(pcie);
-	iowrite32(irq_mask->all, irq_en_base);
+	iowrite32(irq_mask->all, KVASER_PCIEFD_PCI_IEN_ADDR(pcie));
 	/* Ready the DMA buffers */
 	iowrite32(KVASER_PCIEFD_SRB_CMD_RDB0,
 		  KVASER_PCIEFD_SRB_ADDR(pcie) + KVASER_PCIEFD_SRB_CMD_REG);
@@ -1820,8 +1825,7 @@ static int kvaser_pciefd_probe(struct pci_dev *pdev,
 	return 0;
 
 err_free_irq:
-	/* Disable PCI interrupts */
-	iowrite32(0, irq_en_base);
+	kvaser_pciefd_disable_irq_srcs(pcie);
 	free_irq(pcie->pci->irq, pcie);
 
 err_pci_free_irq_vectors:
@@ -1844,35 +1848,26 @@ static int kvaser_pciefd_probe(struct pci_dev *pdev,
 	return ret;
 }
 
-static void kvaser_pciefd_remove_all_ctrls(struct kvaser_pciefd *pcie)
-{
-	int i;
-
-	for (i = 0; i < pcie->nr_channels; i++) {
-		struct kvaser_pciefd_can *can = pcie->can[i];
-
-		if (can) {
-			iowrite32(0, can->reg_base + KVASER_PCIEFD_KCAN_IEN_REG);
-			unregister_candev(can->can.dev);
-			del_timer(&can->bec_poll_timer);
-			kvaser_pciefd_pwm_stop(can);
-			free_candev(can->can.dev);
-		}
-	}
-}
-
 static void kvaser_pciefd_remove(struct pci_dev *pdev)
 {
 	struct kvaser_pciefd *pcie = pci_get_drvdata(pdev);
+	unsigned int i;
 
-	kvaser_pciefd_remove_all_ctrls(pcie);
+	for (i = 0; i < pcie->nr_channels; ++i) {
+		struct kvaser_pciefd_can *can = pcie->can[i];
 
-	/* Disable interrupts */
-	iowrite32(0, KVASER_PCIEFD_SRB_ADDR(pcie) + KVASER_PCIEFD_SRB_CTRL_REG);
-	iowrite32(0, KVASER_PCIEFD_PCI_IEN_ADDR(pcie));
+		unregister_candev(can->can.dev);
+		del_timer(&can->bec_poll_timer);
+		kvaser_pciefd_pwm_stop(can);
+	}
 
+	kvaser_pciefd_disable_irq_srcs(pcie);
 	free_irq(pcie->pci->irq, pcie);
 	pci_free_irq_vectors(pcie->pci);
+
+	for (i = 0; i < pcie->nr_channels; ++i)
+		free_candev(pcie->can[i]->can.dev);
+
 	pci_iounmap(pdev, pcie->reg_base);
 	pci_release_regions(pdev);
 	pci_disable_device(pdev);
-- 
2.47.2


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 2/3] can: kvaser_pciefd: Fix echo_skb race conditions
  2025-03-31  7:25 [PATCH 0/3] can: kvaser_pciefd: Fix ISR race conditions Axel Forsman
  2025-03-31  7:25 ` [PATCH 1/3] can: kvaser_pciefd: Force IRQ edge in case of nested IRQ Axel Forsman
@ 2025-03-31  7:25 ` Axel Forsman
  2025-04-07 15:04   ` Marc Kleine-Budde
  2025-03-31  7:25 ` [PATCH 3/3] can: kvaser_pciefd: Continue parsing DMA buf after dropped RX Axel Forsman
  2 siblings, 1 reply; 8+ messages in thread
From: Axel Forsman @ 2025-03-31  7:25 UTC (permalink / raw)
  To: linux-can; +Cc: mkl, mailhol.vincent, Axel Forsman, stable, Jimmy Assarsson

The functions kvaser_pciefd_start_xmit() and
kvaser_pciefd_handle_ack_packet() raced to stop/wake TX queues and
get/put echo skbs, as kvaser_pciefd_can->echo_lock was only ever taken
when transmitting. E.g., this caused the following error:

    can_put_echo_skb: BUG! echo_skb 5 is occupied!

Instead, use the synchronization helpers in netdev_queues.h. As those
piggyback on BQL barriers, start updating in-flight packets and bytes
counts as well.

Cc: stable@vger.kernel.org
Signed-off-by: Axel Forsman <axfo@kvaser.com>
Tested-by: Jimmy Assarsson <extja@kvaser.com>
Reviewed-by: Jimmy Assarsson <extja@kvaser.com>
---
 drivers/net/can/kvaser_pciefd.c | 70 ++++++++++++++++++++++-----------
 1 file changed, 48 insertions(+), 22 deletions(-)

diff --git a/drivers/net/can/kvaser_pciefd.c b/drivers/net/can/kvaser_pciefd.c
index 0d1b895509c3..6251a1ddfa7e 100644
--- a/drivers/net/can/kvaser_pciefd.c
+++ b/drivers/net/can/kvaser_pciefd.c
@@ -16,6 +16,7 @@
 #include <linux/netdevice.h>
 #include <linux/pci.h>
 #include <linux/timer.h>
+#include <net/netdev_queues.h>
 
 MODULE_LICENSE("Dual BSD/GPL");
 MODULE_AUTHOR("Kvaser AB <support@kvaser.com>");
@@ -412,8 +413,9 @@ struct kvaser_pciefd_can {
 	u8 cmd_seq;
 	int err_rep_cnt;
 	int echo_idx;
+	unsigned int completed_tx_pkts;
+	unsigned int completed_tx_bytes;
 	spinlock_t lock; /* Locks sensitive registers (e.g. MODE) */
-	spinlock_t echo_lock; /* Locks the message echo buffer */
 	struct timer_list bec_poll_timer;
 	struct completion start_comp, flush_comp;
 };
@@ -745,11 +747,24 @@ static int kvaser_pciefd_stop(struct net_device *netdev)
 		del_timer(&can->bec_poll_timer);
 	}
 	can->can.state = CAN_STATE_STOPPED;
+	netdev_reset_queue(netdev);
 	close_candev(netdev);
 
 	return ret;
 }
 
+static unsigned int kvaser_pciefd_tx_avail(struct kvaser_pciefd_can *can)
+{
+	u8 count = FIELD_GET(KVASER_PCIEFD_KCAN_TX_NR_PACKETS_CURRENT_MASK,
+		ioread32(can->reg_base + KVASER_PCIEFD_KCAN_TX_NR_PACKETS_REG));
+
+	if (count < can->can.echo_skb_max) /* Free TX FIFO slot? */
+		/* Avoid reusing unacked seqno */
+		return !can->can.echo_skb[can->echo_idx];
+	else
+		return 0;
+}
+
 static int kvaser_pciefd_prepare_tx_packet(struct kvaser_pciefd_tx_packet *p,
 					   struct kvaser_pciefd_can *can,
 					   struct sk_buff *skb)
@@ -797,23 +812,31 @@ static netdev_tx_t kvaser_pciefd_start_xmit(struct sk_buff *skb,
 					    struct net_device *netdev)
 {
 	struct kvaser_pciefd_can *can = netdev_priv(netdev);
-	unsigned long irq_flags;
 	struct kvaser_pciefd_tx_packet packet;
+	unsigned int frame_len = 0;
 	int nr_words;
-	u8 count;
 
 	if (can_dev_dropped_skb(netdev, skb))
 		return NETDEV_TX_OK;
 
+	/*
+	 * Without room for a new message, stop the queue until at least
+	 * one successful transmit.
+	 */
+	if (!netif_subqueue_maybe_stop(netdev, 0, kvaser_pciefd_tx_avail(can), 1, 1))
+		return NETDEV_TX_BUSY;
+
 	nr_words = kvaser_pciefd_prepare_tx_packet(&packet, can, skb);
 
-	spin_lock_irqsave(&can->echo_lock, irq_flags);
 	/* Prepare and save echo skb in internal slot */
-	can_put_echo_skb(skb, netdev, can->echo_idx, 0);
+	frame_len = can_skb_get_frame_len(skb);
+	can_put_echo_skb(skb, netdev, can->echo_idx, frame_len);
 
 	/* Move echo index to the next slot */
 	can->echo_idx = (can->echo_idx + 1) % can->can.echo_skb_max;
 
+	netdev_sent_queue(netdev, frame_len);
+
 	/* Write header to fifo */
 	iowrite32(packet.header[0],
 		  can->reg_base + KVASER_PCIEFD_KCAN_FIFO_REG);
@@ -836,15 +859,6 @@ static netdev_tx_t kvaser_pciefd_start_xmit(struct sk_buff *skb,
 			     KVASER_PCIEFD_KCAN_FIFO_LAST_REG);
 	}
 
-	count = FIELD_GET(KVASER_PCIEFD_KCAN_TX_NR_PACKETS_CURRENT_MASK,
-			  ioread32(can->reg_base + KVASER_PCIEFD_KCAN_TX_NR_PACKETS_REG));
-	/* No room for a new message, stop the queue until at least one
-	 * successful transmit
-	 */
-	if (count >= can->can.echo_skb_max || can->can.echo_skb[can->echo_idx])
-		netif_stop_queue(netdev);
-	spin_unlock_irqrestore(&can->echo_lock, irq_flags);
-
 	return NETDEV_TX_OK;
 }
 
@@ -970,6 +984,8 @@ static int kvaser_pciefd_setup_can_ctrls(struct kvaser_pciefd *pcie)
 		can->kv_pcie = pcie;
 		can->cmd_seq = 0;
 		can->err_rep_cnt = 0;
+		can->completed_tx_pkts = 0;
+		can->completed_tx_bytes = 0;
 		can->bec.txerr = 0;
 		can->bec.rxerr = 0;
 
@@ -987,7 +1003,6 @@ static int kvaser_pciefd_setup_can_ctrls(struct kvaser_pciefd *pcie)
 		can->can.clock.freq = pcie->freq;
 		can->can.echo_skb_max = min(KVASER_PCIEFD_CAN_TX_MAX_COUNT, tx_nr_packets_max - 1);
 		can->echo_idx = 0;
-		spin_lock_init(&can->echo_lock);
 		spin_lock_init(&can->lock);
 
 		can->can.bittiming_const = &kvaser_pciefd_bittiming_const;
@@ -1510,19 +1525,16 @@ static int kvaser_pciefd_handle_ack_packet(struct kvaser_pciefd *pcie,
 		netdev_dbg(can->can.dev, "Packet was flushed\n");
 	} else {
 		int echo_idx = FIELD_GET(KVASER_PCIEFD_PACKET_SEQ_MASK, p->header[0]);
-		int len;
-		u8 count;
+		unsigned int len, frame_len = 0;
 		struct sk_buff *skb;
 
 		skb = can->can.echo_skb[echo_idx];
 		if (skb)
 			kvaser_pciefd_set_skb_timestamp(pcie, skb, p->timestamp);
-		len = can_get_echo_skb(can->can.dev, echo_idx, NULL);
-		count = FIELD_GET(KVASER_PCIEFD_KCAN_TX_NR_PACKETS_CURRENT_MASK,
-				  ioread32(can->reg_base + KVASER_PCIEFD_KCAN_TX_NR_PACKETS_REG));
+		len = can_get_echo_skb(can->can.dev, echo_idx, &frame_len);
 
-		if (count < can->can.echo_skb_max && netif_queue_stopped(can->can.dev))
-			netif_wake_queue(can->can.dev);
+		can->completed_tx_pkts++;
+		can->completed_tx_bytes += frame_len;
 
 		if (!one_shot_fail) {
 			can->can.dev->stats.tx_bytes += len;
@@ -1638,11 +1650,25 @@ static int kvaser_pciefd_read_buffer(struct kvaser_pciefd *pcie, int dma_buf)
 {
 	int pos = 0;
 	int res = 0;
+	unsigned int i;
 
 	do {
 		res = kvaser_pciefd_read_packet(pcie, &pos, dma_buf);
 	} while (!res && pos > 0 && pos < KVASER_PCIEFD_DMA_SIZE);
 
+	for (i = 0; i < pcie->nr_channels; ++i) {
+		struct kvaser_pciefd_can *can = pcie->can[i];
+
+		if (!can->completed_tx_pkts)
+			continue;
+		netif_subqueue_completed_wake(can->can.dev, 0,
+					      can->completed_tx_pkts,
+					      can->completed_tx_bytes,
+					      kvaser_pciefd_tx_avail(can), 1);
+		can->completed_tx_pkts = 0;
+		can->completed_tx_bytes = 0;
+	}
+
 	return res;
 }
 
-- 
2.47.2


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 3/3] can: kvaser_pciefd: Continue parsing DMA buf after dropped RX
  2025-03-31  7:25 [PATCH 0/3] can: kvaser_pciefd: Fix ISR race conditions Axel Forsman
  2025-03-31  7:25 ` [PATCH 1/3] can: kvaser_pciefd: Force IRQ edge in case of nested IRQ Axel Forsman
  2025-03-31  7:25 ` [PATCH 2/3] can: kvaser_pciefd: Fix echo_skb race conditions Axel Forsman
@ 2025-03-31  7:25 ` Axel Forsman
  2 siblings, 0 replies; 8+ messages in thread
From: Axel Forsman @ 2025-03-31  7:25 UTC (permalink / raw)
  To: linux-can; +Cc: mkl, mailhol.vincent, Axel Forsman, stable, Jimmy Assarsson

Going bus-off on a channel doing RX could result in dropped packets.

As netif_running() gets cleared before the channel abort procedure,
the handling of any last RDATA packets would see netif_rx() return
non-zero to signal a dropped packet. kvaser_pciefd_read_buffer() dealt
with this "error" by breaking out of processing the remaining DMA RX
buffer.

Only return an error from kvaser_pciefd_read_buffer() due to packet
corruption, otherwise handle it internally.

Cc: stable@vger.kernel.org
Signed-off-by: Axel Forsman <axfo@kvaser.com>
Tested-by: Jimmy Assarsson <extja@kvaser.com>
Reviewed-by: Jimmy Assarsson <extja@kvaser.com>
---
 drivers/net/can/kvaser_pciefd.c | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/drivers/net/can/kvaser_pciefd.c b/drivers/net/can/kvaser_pciefd.c
index 6251a1ddfa7e..1f4004204fa8 100644
--- a/drivers/net/can/kvaser_pciefd.c
+++ b/drivers/net/can/kvaser_pciefd.c
@@ -1216,7 +1216,7 @@ static int kvaser_pciefd_handle_data_packet(struct kvaser_pciefd *pcie,
 		skb = alloc_canfd_skb(priv->dev, &cf);
 		if (!skb) {
 			priv->dev->stats.rx_dropped++;
-			return -ENOMEM;
+			return 0;
 		}
 
 		cf->len = can_fd_dlc2len(dlc);
@@ -1228,7 +1228,7 @@ static int kvaser_pciefd_handle_data_packet(struct kvaser_pciefd *pcie,
 		skb = alloc_can_skb(priv->dev, (struct can_frame **)&cf);
 		if (!skb) {
 			priv->dev->stats.rx_dropped++;
-			return -ENOMEM;
+			return 0;
 		}
 		can_frame_set_cc_len((struct can_frame *)cf, dlc, priv->ctrlmode);
 	}
@@ -1246,7 +1246,9 @@ static int kvaser_pciefd_handle_data_packet(struct kvaser_pciefd *pcie,
 	priv->dev->stats.rx_packets++;
 	kvaser_pciefd_set_skb_timestamp(pcie, skb, p->timestamp);
 
-	return netif_rx(skb);
+	netif_rx(skb);
+
+	return 0;
 }
 
 static void kvaser_pciefd_change_state(struct kvaser_pciefd_can *can,
-- 
2.47.2


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH 1/3] can: kvaser_pciefd: Force IRQ edge in case of nested IRQ
  2025-03-31  7:25 ` [PATCH 1/3] can: kvaser_pciefd: Force IRQ edge in case of nested IRQ Axel Forsman
@ 2025-04-07 14:34   ` Marc Kleine-Budde
  2025-04-08 12:24     ` Axel Forsman
  0 siblings, 1 reply; 8+ messages in thread
From: Marc Kleine-Budde @ 2025-04-07 14:34 UTC (permalink / raw)
  To: Axel Forsman; +Cc: linux-can, mailhol.vincent, stable, Jimmy Assarsson

[-- Attachment #1: Type: text/plain, Size: 1568 bytes --]

On 31.03.2025 09:25:26, Axel Forsman wrote:
> Avoid the driver missing IRQs by temporarily masking IRQs in the ISR
> to enforce an edge even if a different IRQ is signalled before handled
> IRQs are cleared.
> 
> Fixes: 48f827d4f48f ("can: kvaser_pciefd: Move reset of DMA RX buffers to the end of the ISR")
> Cc: stable@vger.kernel.org
> Signed-off-by: Axel Forsman <axfo@kvaser.com>
> Tested-by: Jimmy Assarsson <extja@kvaser.com>
> Reviewed-by: Jimmy Assarsson <extja@kvaser.com>
> ---
>  drivers/net/can/kvaser_pciefd.c | 83 ++++++++++++++++-----------------
>  1 file changed, 39 insertions(+), 44 deletions(-)
> 
> diff --git a/drivers/net/can/kvaser_pciefd.c b/drivers/net/can/kvaser_pciefd.c
> index fa04a7ced02b..0d1b895509c3 100644
> --- a/drivers/net/can/kvaser_pciefd.c
> +++ b/drivers/net/can/kvaser_pciefd.c
> @@ -1646,24 +1646,28 @@ static int kvaser_pciefd_read_buffer(struct kvaser_pciefd *pcie, int dma_buf)
>  	return res;
>  }
>  
> -static u32 kvaser_pciefd_receive_irq(struct kvaser_pciefd *pcie)
> +static void kvaser_pciefd_receive_irq(struct kvaser_pciefd *pcie)
>  {
> +	__le32 __iomem *srb_cmd_reg = KVASER_PCIEFD_SRB_ADDR(pcie) + KVASER_PCIEFD_SRB_CMD_REG;

Why is this an __le32? The struct kvaser_pciefd::reg_base is __iomem
void *.

Marc

-- 
Pengutronix e.K.                 | Marc Kleine-Budde          |
Embedded Linux                   | https://www.pengutronix.de |
Vertretung Nürnberg              | Phone: +49-5121-206917-129 |
Amtsgericht Hildesheim, HRA 2686 | Fax:   +49-5121-206917-9   |

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 2/3] can: kvaser_pciefd: Fix echo_skb race conditions
  2025-03-31  7:25 ` [PATCH 2/3] can: kvaser_pciefd: Fix echo_skb race conditions Axel Forsman
@ 2025-04-07 15:04   ` Marc Kleine-Budde
  2025-04-09 10:09     ` Axel Forsman
  0 siblings, 1 reply; 8+ messages in thread
From: Marc Kleine-Budde @ 2025-04-07 15:04 UTC (permalink / raw)
  To: Axel Forsman; +Cc: linux-can, mailhol.vincent, stable, Jimmy Assarsson

[-- Attachment #1: Type: text/plain, Size: 7920 bytes --]

On 31.03.2025 09:25:27, Axel Forsman wrote:
> The functions kvaser_pciefd_start_xmit() and
> kvaser_pciefd_handle_ack_packet() raced to stop/wake TX queues and
> get/put echo skbs, as kvaser_pciefd_can->echo_lock was only ever taken
> when transmitting. E.g., this caused the following error:
> 
>     can_put_echo_skb: BUG! echo_skb 5 is occupied!
> 
> Instead, use the synchronization helpers in netdev_queues.h. As those
> piggyback on BQL barriers, start updating in-flight packets and bytes
> counts as well.

This looks like it does in the right direction. Using the
netif_subqueue_completed helpers is a great idea.

What usually works even better is to have 2 counters and a mask:
- unsigned int tx_head, tx_tail
- TXFIFO_DEPTH

The tx_head is incremented in the xmit function, tail is incremented in
the tx_done function.

There's no need to check how many buffers are free in the HW.

Have a look at the rockchip-canfd driver for an example.

Some comments inline.

> Cc: stable@vger.kernel.org
> Signed-off-by: Axel Forsman <axfo@kvaser.com>
> Tested-by: Jimmy Assarsson <extja@kvaser.com>
> Reviewed-by: Jimmy Assarsson <extja@kvaser.com>
> ---
>  drivers/net/can/kvaser_pciefd.c | 70 ++++++++++++++++++++++-----------
>  1 file changed, 48 insertions(+), 22 deletions(-)
> 
> diff --git a/drivers/net/can/kvaser_pciefd.c b/drivers/net/can/kvaser_pciefd.c
> index 0d1b895509c3..6251a1ddfa7e 100644
> --- a/drivers/net/can/kvaser_pciefd.c
> +++ b/drivers/net/can/kvaser_pciefd.c
> @@ -16,6 +16,7 @@
>  #include <linux/netdevice.h>
>  #include <linux/pci.h>
>  #include <linux/timer.h>
> +#include <net/netdev_queues.h>
>  
>  MODULE_LICENSE("Dual BSD/GPL");
>  MODULE_AUTHOR("Kvaser AB <support@kvaser.com>");
> @@ -412,8 +413,9 @@ struct kvaser_pciefd_can {
>  	u8 cmd_seq;
>  	int err_rep_cnt;
>  	int echo_idx;
> +	unsigned int completed_tx_pkts;
> +	unsigned int completed_tx_bytes;
>  	spinlock_t lock; /* Locks sensitive registers (e.g. MODE) */
> -	spinlock_t echo_lock; /* Locks the message echo buffer */
>  	struct timer_list bec_poll_timer;
>  	struct completion start_comp, flush_comp;
>  };
> @@ -745,11 +747,24 @@ static int kvaser_pciefd_stop(struct net_device *netdev)
>  		del_timer(&can->bec_poll_timer);
>  	}
>  	can->can.state = CAN_STATE_STOPPED;
> +	netdev_reset_queue(netdev);
>  	close_candev(netdev);
>  
>  	return ret;
>  }
>  
> +static unsigned int kvaser_pciefd_tx_avail(struct kvaser_pciefd_can *can)
> +{
> +	u8 count = FIELD_GET(KVASER_PCIEFD_KCAN_TX_NR_PACKETS_CURRENT_MASK,
> +		ioread32(can->reg_base + KVASER_PCIEFD_KCAN_TX_NR_PACKETS_REG));
> +
> +	if (count < can->can.echo_skb_max) /* Free TX FIFO slot? */
> +		/* Avoid reusing unacked seqno */
> +		return !can->can.echo_skb[can->echo_idx];
> +	else
> +		return 0;
> +}
> +
>  static int kvaser_pciefd_prepare_tx_packet(struct kvaser_pciefd_tx_packet *p,
>  					   struct kvaser_pciefd_can *can,
>  					   struct sk_buff *skb)
> @@ -797,23 +812,31 @@ static netdev_tx_t kvaser_pciefd_start_xmit(struct sk_buff *skb,
>  					    struct net_device *netdev)
>  {
>  	struct kvaser_pciefd_can *can = netdev_priv(netdev);
> -	unsigned long irq_flags;
>  	struct kvaser_pciefd_tx_packet packet;
> +	unsigned int frame_len = 0;
>  	int nr_words;
> -	u8 count;
>  
>  	if (can_dev_dropped_skb(netdev, skb))
>  		return NETDEV_TX_OK;
>  
> +	/*
> +	 * Without room for a new message, stop the queue until at least
> +	 * one successful transmit.
> +	 */
> +	if (!netif_subqueue_maybe_stop(netdev, 0, kvaser_pciefd_tx_avail(can), 1, 1))
> +		return NETDEV_TX_BUSY;

Returning NETDEV_TX_BUSY is quite expensive, stop the queue at the end
of this function, if the buffers are full.

> +
>  	nr_words = kvaser_pciefd_prepare_tx_packet(&packet, can, skb);
>  
> -	spin_lock_irqsave(&can->echo_lock, irq_flags);
>  	/* Prepare and save echo skb in internal slot */
> -	can_put_echo_skb(skb, netdev, can->echo_idx, 0);
> +	frame_len = can_skb_get_frame_len(skb);
> +	can_put_echo_skb(skb, netdev, can->echo_idx, frame_len);
>  
>  	/* Move echo index to the next slot */
>  	can->echo_idx = (can->echo_idx + 1) % can->can.echo_skb_max;
>  
> +	netdev_sent_queue(netdev, frame_len);
> +
>  	/* Write header to fifo */
>  	iowrite32(packet.header[0],
>  		  can->reg_base + KVASER_PCIEFD_KCAN_FIFO_REG);
> @@ -836,15 +859,6 @@ static netdev_tx_t kvaser_pciefd_start_xmit(struct sk_buff *skb,
>  			     KVASER_PCIEFD_KCAN_FIFO_LAST_REG);
>  	}
>  
> -	count = FIELD_GET(KVASER_PCIEFD_KCAN_TX_NR_PACKETS_CURRENT_MASK,
> -			  ioread32(can->reg_base + KVASER_PCIEFD_KCAN_TX_NR_PACKETS_REG));
> -	/* No room for a new message, stop the queue until at least one
> -	 * successful transmit
> -	 */
> -	if (count >= can->can.echo_skb_max || can->can.echo_skb[can->echo_idx])
> -		netif_stop_queue(netdev);
> -	spin_unlock_irqrestore(&can->echo_lock, irq_flags);
> -
>  	return NETDEV_TX_OK;
>  }
>  
> @@ -970,6 +984,8 @@ static int kvaser_pciefd_setup_can_ctrls(struct kvaser_pciefd *pcie)
>  		can->kv_pcie = pcie;
>  		can->cmd_seq = 0;
>  		can->err_rep_cnt = 0;
> +		can->completed_tx_pkts = 0;
> +		can->completed_tx_bytes = 0;
>  		can->bec.txerr = 0;
>  		can->bec.rxerr = 0;
>  
> @@ -987,7 +1003,6 @@ static int kvaser_pciefd_setup_can_ctrls(struct kvaser_pciefd *pcie)
>  		can->can.clock.freq = pcie->freq;
>  		can->can.echo_skb_max = min(KVASER_PCIEFD_CAN_TX_MAX_COUNT, tx_nr_packets_max - 1);
>  		can->echo_idx = 0;
> -		spin_lock_init(&can->echo_lock);
>  		spin_lock_init(&can->lock);
>  
>  		can->can.bittiming_const = &kvaser_pciefd_bittiming_const;
> @@ -1510,19 +1525,16 @@ static int kvaser_pciefd_handle_ack_packet(struct kvaser_pciefd *pcie,
>  		netdev_dbg(can->can.dev, "Packet was flushed\n");
>  	} else {
>  		int echo_idx = FIELD_GET(KVASER_PCIEFD_PACKET_SEQ_MASK, p->header[0]);
> -		int len;
> -		u8 count;
> +		unsigned int len, frame_len = 0;
>  		struct sk_buff *skb;
>  
>  		skb = can->can.echo_skb[echo_idx];
>  		if (skb)
>  			kvaser_pciefd_set_skb_timestamp(pcie, skb, p->timestamp);
> -		len = can_get_echo_skb(can->can.dev, echo_idx, NULL);
> -		count = FIELD_GET(KVASER_PCIEFD_KCAN_TX_NR_PACKETS_CURRENT_MASK,
> -				  ioread32(can->reg_base + KVASER_PCIEFD_KCAN_TX_NR_PACKETS_REG));
> +		len = can_get_echo_skb(can->can.dev, echo_idx, &frame_len);
>  
> -		if (count < can->can.echo_skb_max && netif_queue_stopped(can->can.dev))
> -			netif_wake_queue(can->can.dev);
> +		can->completed_tx_pkts++;
> +		can->completed_tx_bytes += frame_len;
>  
>  		if (!one_shot_fail) {
>  			can->can.dev->stats.tx_bytes += len;
> @@ -1638,11 +1650,25 @@ static int kvaser_pciefd_read_buffer(struct kvaser_pciefd *pcie, int dma_buf)
>  {
>  	int pos = 0;
>  	int res = 0;
> +	unsigned int i;
>  
>  	do {
>  		res = kvaser_pciefd_read_packet(pcie, &pos, dma_buf);
>  	} while (!res && pos > 0 && pos < KVASER_PCIEFD_DMA_SIZE);
>  
> +	for (i = 0; i < pcie->nr_channels; ++i) {
> +		struct kvaser_pciefd_can *can = pcie->can[i];
> +
> +		if (!can->completed_tx_pkts)
> +			continue;
> +		netif_subqueue_completed_wake(can->can.dev, 0,
> +					      can->completed_tx_pkts,
> +					      can->completed_tx_bytes,
> +					      kvaser_pciefd_tx_avail(can), 1);

You can do this as soon as as one packet is finished, if you want to
avoid too frequent wakeups, use threshold of more than 1.

Marc

> +		can->completed_tx_pkts = 0;
> +		can->completed_tx_bytes = 0;
> +	}
> +
>  	return res;
>  }
>  
> -- 
> 2.47.2
> 
> 
> 

-- 
Pengutronix e.K.                 | Marc Kleine-Budde          |
Embedded Linux                   | https://www.pengutronix.de |
Vertretung Nürnberg              | Phone: +49-5121-206917-129 |
Amtsgericht Hildesheim, HRA 2686 | Fax:   +49-5121-206917-9   |

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 1/3] can: kvaser_pciefd: Force IRQ edge in case of nested IRQ
  2025-04-07 14:34   ` Marc Kleine-Budde
@ 2025-04-08 12:24     ` Axel Forsman
  0 siblings, 0 replies; 8+ messages in thread
From: Axel Forsman @ 2025-04-08 12:24 UTC (permalink / raw)
  To: Marc Kleine-Budde; +Cc: linux-can, mailhol.vincent, stable, Jimmy Assarsson

Marc Kleine-Budde <mkl@pengutronix.de> writes:

> On 31.03.2025 09:25:26, Axel Forsman wrote:
>> -static u32 kvaser_pciefd_receive_irq(struct kvaser_pciefd *pcie)
>> +static void kvaser_pciefd_receive_irq(struct kvaser_pciefd *pcie)
>>  {
>> +	__le32 __iomem *srb_cmd_reg = KVASER_PCIEFD_SRB_ADDR(pcie) + KVASER_PCIEFD_SRB_CMD_REG;
>
> Why is this an __le32? The struct kvaser_pciefd::reg_base is __iomem
> void *.

Just as a hint that the register is 32-bit.
But you are right, I will change to "void __iomem *" for consistency
in the next iteration.


/Axel

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 2/3] can: kvaser_pciefd: Fix echo_skb race conditions
  2025-04-07 15:04   ` Marc Kleine-Budde
@ 2025-04-09 10:09     ` Axel Forsman
  0 siblings, 0 replies; 8+ messages in thread
From: Axel Forsman @ 2025-04-09 10:09 UTC (permalink / raw)
  To: Marc Kleine-Budde; +Cc: linux-can, mailhol.vincent, stable, Jimmy Assarsson

Marc Kleine-Budde <mkl@pengutronix.de> writes:

> On 31.03.2025 09:25:27, Axel Forsman wrote:
>> The functions kvaser_pciefd_start_xmit() and
>> kvaser_pciefd_handle_ack_packet() raced to stop/wake TX queues and
>> get/put echo skbs, as kvaser_pciefd_can->echo_lock was only ever taken
>> when transmitting. E.g., this caused the following error:
>> 
>>     can_put_echo_skb: BUG! echo_skb 5 is occupied!
>> 
>> Instead, use the synchronization helpers in netdev_queues.h. As those
>> piggyback on BQL barriers, start updating in-flight packets and bytes
>> counts as well.
>
> This looks like it does in the right direction. Using the
> netif_subqueue_completed helpers is a great idea.
>
> What usually works even better is to have 2 counters and a mask:
> - unsigned int tx_head, tx_tail
> - TXFIFO_DEPTH
>
> The tx_head is incremented in the xmit function, tail is incremented in
> the tx_done function.
>
> There's no need to check how many buffers are free in the HW.
>
> Have a look at the rockchip-canfd driver for an example.

An attempt was made to keep this a bugfix-only commit,
but I do agree it is nicer to maintain an in-flight counter
instead of querying the device.

(A mask is inapplicable,
as the size of the device TX FIFO is not necessarily a power-of-2,
though the packets have sequence numbers so it does not matter.)

I guess a write barrier would be needed before tx_tail is incremented,
for the xmit function not to see it before __can_get_echo_skb() has
cleared the echo skb slot? (Or else, can_put_echo_skb() errors if the
slot is already non-NULL.) It is not obvious to me how rockchip-canfd
handles this?

>> +	/*
>> +	 * Without room for a new message, stop the queue until at least
>> +	 * one successful transmit.
>> +	 */
>> +	if (!netif_subqueue_maybe_stop(netdev, 0, kvaser_pciefd_tx_avail(can), 1, 1))
>> +		return NETDEV_TX_BUSY;
>
> Returning NETDEV_TX_BUSY is quite expensive, stop the queue at the end
> of this function, if the buffers are full.

Will do.

>> @@ -1638,11 +1650,25 @@ static int kvaser_pciefd_read_buffer(struct kvaser_pciefd *pcie, int dma_buf)
>>  {
>>  	int pos = 0;
>>  	int res = 0;
>> +	unsigned int i;
>>  
>>  	do {
>>  		res = kvaser_pciefd_read_packet(pcie, &pos, dma_buf);
>>  	} while (!res && pos > 0 && pos < KVASER_PCIEFD_DMA_SIZE);
>>  
>> +	for (i = 0; i < pcie->nr_channels; ++i) {
>> +		struct kvaser_pciefd_can *can = pcie->can[i];
>> +
>> +		if (!can->completed_tx_pkts)
>> +			continue;
>> +		netif_subqueue_completed_wake(can->can.dev, 0,
>> +					      can->completed_tx_pkts,
>> +					      can->completed_tx_bytes,
>> +					      kvaser_pciefd_tx_avail(can), 1);
>
> You can do this as soon as as one packet is finished, if you want to
> avoid too frequent wakeups, use threshold of more than 1.

I did that initially, but changed it to follow the advice of the
netdev_tx_completed_queue() docstring. Since the RX buffer for a single
IRQ may have multiple packets, would not BQL see incorrect intervals in
that case?


Thanks for the review
Axel Forsman

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2025-04-09 10:09 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-03-31  7:25 [PATCH 0/3] can: kvaser_pciefd: Fix ISR race conditions Axel Forsman
2025-03-31  7:25 ` [PATCH 1/3] can: kvaser_pciefd: Force IRQ edge in case of nested IRQ Axel Forsman
2025-04-07 14:34   ` Marc Kleine-Budde
2025-04-08 12:24     ` Axel Forsman
2025-03-31  7:25 ` [PATCH 2/3] can: kvaser_pciefd: Fix echo_skb race conditions Axel Forsman
2025-04-07 15:04   ` Marc Kleine-Budde
2025-04-09 10:09     ` Axel Forsman
2025-03-31  7:25 ` [PATCH 3/3] can: kvaser_pciefd: Continue parsing DMA buf after dropped RX Axel Forsman

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).