Subject: kern/30349: Input error counter of wm(4) doesn't count error frames
To: None <kern-bug-people@netbsd.org, gnats-admin@netbsd.org,>
From: None <umezawa@iij.ad.jp>
List: netbsd-bugs
Date: 05/27/2005 04:57:00
>Number:         30349
>Category:       kern
>Synopsis:       Input error counter of wm(4) doesn't count error frames
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    kern-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Fri May 27 04:57:00 +0000 2005
>Originator:     UMEZAWA Takeshi
>Release:        1.6.2
>Organization:
Internet Initiative Japan
>Environment:
NetBSD idgw07.iij.ad.jp 1.6.2_STABLE NetBSD 1.6.2_STABLE (GENERIC) #0: Mon May  2 11:40:58 JST 2005     root@idgw07.iij.ad.jp:/usr/src/sys/arch/i386/compile/GENERIC i386
>Description:
if_ierrors of wm(4) is incremented only when wm_add_rxbuf() failed, and is
not incremented when hardware received error frames.

There is the code that increments if_ierrors according to error status of
RX descriptors. But error frames are never stored to RX buffer because
RCTL_SBP (Store Bad Packets) bit of WMREG_RCTL is not set.

>How-To-Repeat:
Connect with duplex mismatch, and receive packets.
There must be many input errors, but if_ierrors is not (or rarely)
incremented.
>Fix:
There are two ways to count error frames.

The first way is, as written in Full Description, setting RCTL_SBP bit.
And the second way is correcting error counts from hardware statistics
registers periodically.

The advantage of the first way is:
  1. If the number of error frames is small, the impact for performance
     is also small.
The advantages of the second way is:
  1. Regardless of the number of error frames, the impact for
     performance is constant.
  2. Regardless of the number of received (or error) frames, error
     counter can be correctly incremented.

I have written a patch that implements the second way. Note that this
patch requires the patch which is submitted in PR kern/30221.

diff -u old/if_wm.c new/if_wm.c
--- old/if_wm.c	Fri May 27 13:52:32 2005
+++ new/if_wm.c	Fri May 27 13:52:44 2005
@@ -1749,7 +1749,6 @@
 		 */
 		if (errors &
 		     (WRX_ER_CE|WRX_ER_SE|WRX_ER_SEQ|WRX_ER_CXE|WRX_ER_RXE)) {
-			ifp->if_ierrors++;
 			if (errors & WRX_ER_SE)
 				printf("%s: symbol error\n",
 				    sc->sc_dev.dv_xname);
@@ -1943,6 +1942,15 @@
 	struct ifnet *ifp = &sc->sc_ethercom.ec_if;
 
 	ifp->if_collisions += CSR_READ(sc, WMREG_COLC);
+
+	ifp->if_ierrors += 0ULL + /* ensure quad_t */
+	    CSR_READ(sc, WMREG_CRCERRS) +
+	    CSR_READ(sc, WMREG_ALGNERRC) +
+	    CSR_READ(sc, WMREG_SYMERRC) +
+	    CSR_READ(sc, WMREG_RXERRC) +
+	    CSR_READ(sc, WMREG_SEC) +
+	    CSR_READ(sc, WMREG_CEXTERR) +
+	    CSR_READ(sc, WMREG_RLEC);
 }
 
 /*
diff -u old/if_wmreg.h new/if_wmreg.h
--- old/if_wmreg.h	Fri May 27 13:52:32 2005
+++ new/if_wmreg.h	Fri May 27 13:52:44 2005
@@ -514,7 +514,14 @@
 #define	TSPMT_TSMT(x)	(x)		/* TCP seg min transfer */
 #define	TSPMT_TSPBP(x)	((x) << 16)	/* TCP seg pkt buf padding */
 
+#define	WMREG_CRCERRS	0x4000	/* CRC Error Count */
+#define	WMREG_ALGNERRC	0x4004	/* Alignment Error Count */
+#define	WMREG_SYMERRC	0x4008	/* Symbol Error Count */
+#define	WMREG_RXERRC	0x400c	/* RX Error Count */
 #define	WMREG_COLC	0x4028	/* Collision Count */
+#define	WMREG_SEC	0x4038	/* Sequence Error Count */
+#define	WMREG_CEXTERR	0x403c	/* Carrier Extension Error Count */
+#define	WMREG_RLEC	0x4040	/* Receive Length Error Count */
 
 #define	WMREG_RXCSUM	0x5000	/* Receive Checksum register */
 #define	RXCSUM_PCSS	0x000000ff	/* Packet Checksum Start */