All of lore.kernel.org
 help / color / mirror / Atom feed
From: Moon Yeounsu <yyyynoom@gmail.com>
To: Andrew Lunn <andrew+netdev@lunn.ch>,
	"David S. Miller" <davem@davemloft.net>,
	Eric Dumazet <edumazet@google.com>,
	Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>
Cc: Moon Yeounsu <yyyynoom@gmail.com>,
	netdev@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [PATCH net-next] net: dlink: add synchronization for stats update
Date: Tue, 22 Apr 2025 04:16:44 +0900	[thread overview]
Message-ID: <20250421191645.43526-2-yyyynoom@gmail.com> (raw)

There are two paths that call `get_stats()`:
    1. From user space via the `ip` command
    2. From interrupt context via `rio_interrupt()`

Case 1 is synchronized by `rtnl_lock()`, so it is safe.
However, the two cases above are not synchronized with each other.
Therefore, `spin_lock` is needed to protect `get_stats()` as it can be
preempted by an interrupt. In this context, `spin_lock_irq()` is required
(using `spin_lock_bh()` may result in a deadlock).

While `spin_lock` protects `get_stats()`, it does not protect updates to
`dev->stats.tx_errors` and `dev->stats.collisions`, which may be
concurrently modified by the interrupt handler and user space.
By using temporary variables in `np->tx_errors` and `np->collisions`,
we can safely update `dev->stats` without additional locking.

Tested-on: D-Link DGE-550T Rev-A3
Signed-off-by: Moon Yeounsu <yyyynoom@gmail.com>
---
Question:
	This might be a bit off-topic, but I don’t fully understand why a single global
	`rtnl_lock` is used for synchronization. While I may not be fully aware of the 
	design rationale, it seems somewhat suboptimal. I believe it could be improved.
---
 drivers/net/ethernet/dlink/dl2k.c | 11 +++++++++--
 drivers/net/ethernet/dlink/dl2k.h |  5 +++++
 2 files changed, 14 insertions(+), 2 deletions(-)

diff --git a/drivers/net/ethernet/dlink/dl2k.c b/drivers/net/ethernet/dlink/dl2k.c
index d0ea92607870..2d929a83e101 100644
--- a/drivers/net/ethernet/dlink/dl2k.c
+++ b/drivers/net/ethernet/dlink/dl2k.c
@@ -865,7 +865,7 @@ tx_error (struct net_device *dev, int tx_status)
 	frame_id = (tx_status & 0xffff0000);
 	printk (KERN_ERR "%s: Transmit error, TxStatus %4.4x, FrameId %d.\n",
 		dev->name, tx_status, frame_id);
-	dev->stats.tx_errors++;
+	np->tx_errors++;
 	/* Ttransmit Underrun */
 	if (tx_status & 0x10) {
 		dev->stats.tx_fifo_errors++;
@@ -904,7 +904,7 @@ tx_error (struct net_device *dev, int tx_status)
 	}
 	/* Maximum Collisions */
 	if (tx_status & 0x08)
-		dev->stats.collisions++;
+		np->collisions++;
 	/* Restart the Tx */
 	dw32(MACCtrl, dr16(MACCtrl) | TxEnable);
 }
@@ -1074,6 +1074,7 @@ get_stats (struct net_device *dev)
 #endif
 	unsigned int stat_reg;
 
+	spin_lock_irq(&np->stats_lock);
 	/* All statistics registers need to be acknowledged,
 	   else statistic overflow could cause problems */
 
@@ -1085,6 +1086,7 @@ get_stats (struct net_device *dev)
 	dev->stats.multicast = dr32(McstFramesRcvdOk);
 	dev->stats.collisions += dr32(SingleColFrames)
 			     +  dr32(MultiColFrames);
+	dev->stats.collisions += np->collisions;
 
 	/* detailed tx errors */
 	stat_reg = dr16(FramesAbortXSColls);
@@ -1095,6 +1097,8 @@ get_stats (struct net_device *dev)
 	dev->stats.tx_carrier_errors += stat_reg;
 	dev->stats.tx_errors += stat_reg;
 
+	dev->stats.tx_errors += np->tx_errors;
+
 	/* Clear all other statistic register. */
 	dr32(McstOctetXmtOk);
 	dr16(BcstFramesXmtdOk);
@@ -1123,6 +1127,9 @@ get_stats (struct net_device *dev)
 	dr16(TCPCheckSumErrors);
 	dr16(UDPCheckSumErrors);
 	dr16(IPCheckSumErrors);
+
+	spin_unlock_irq(&np->stats_lock);
+
 	return &dev->stats;
 }
 
diff --git a/drivers/net/ethernet/dlink/dl2k.h b/drivers/net/ethernet/dlink/dl2k.h
index 195dc6cfd895..dc8755a69b73 100644
--- a/drivers/net/ethernet/dlink/dl2k.h
+++ b/drivers/net/ethernet/dlink/dl2k.h
@@ -372,6 +372,8 @@ struct netdev_private {
 	struct pci_dev *pdev;
 	void __iomem *ioaddr;
 	void __iomem *eeprom_addr;
+	// To ensure synchronization when stats are updated.
+	spinlock_t stats_lock;
 	spinlock_t tx_lock;
 	spinlock_t rx_lock;
 	unsigned int rx_buf_sz;		/* Based on MTU+slack. */
@@ -401,6 +403,9 @@ struct netdev_private {
 	u16 negotiate;		/* Negotiated media */
 	int phy_addr;		/* PHY addresses. */
 	u16 led_mode;		/* LED mode read from EEPROM (IP1000A only) */
+
+	u64 collisions;
+	u64 tx_errors;
 };
 
 /* The station address location in the EEPROM. */
-- 
2.49.0


             reply	other threads:[~2025-04-21 19:17 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-04-21 19:16 Moon Yeounsu [this message]
2025-04-21 23:23 ` [PATCH net-next] net: dlink: add synchronization for stats update Jacob Keller
2025-04-22 22:53   ` Moon Yeounsu
2025-04-23 16:28     ` Jacob Keller
2025-04-24  1:42 ` Jakub Kicinski

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20250421191645.43526-2-yyyynoom@gmail.com \
    --to=yyyynoom@gmail.com \
    --cc=andrew+netdev@lunn.ch \
    --cc=davem@davemloft.net \
    --cc=edumazet@google.com \
    --cc=kuba@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.