aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/niu.c
diff options
context:
space:
mode:
authorJesper Dangaard Brouer <hawk@comx.dk>2008-12-18 22:50:49 -0500
committerDavid S. Miller <davem@davemloft.net>2008-12-19 01:27:40 -0500
commitb8a606b871d37e03b92be1bd3deedeee97ea4f13 (patch)
treebece6e16df238b560ea1dec04e30c2080b3eca94 /drivers/net/niu.c
parent5664dd5561850df580414783cf3c8e0aa834c62e (diff)
NIU: Implement discard counters
Implementing discard counters for the NIU driver turned out to be more complicated than first assumed. The discard counters for the NIU neptune chip are only 16-bit (even though this is a 64-bit chip). These 16-bit counters can overflow quickly, especially considering this is a 10Gbit/s ethernet card. The overflow indication bit is, unfortunatly, not usable as the counter value does not wrap, but remains at max value 0xFFFF. Resulting in lost counts until the counter is reset. The read and reset scheme also poses a problem. Both in theory and in practice counters can be lost in between reading nr64() and clearing the counter nw64(). For this reason, the number of counter clearings nw64() is limited/reduced. On the fast-path the counters are only syncronized once it exceeds 0x7FFF. When read by userspace, its syncronized fully. Signed-off-by: Jesper Dangaard Brouer <hawk@comx.dk> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/niu.c')
-rw-r--r--drivers/net/niu.c51
1 files changed, 51 insertions, 0 deletions
diff --git a/drivers/net/niu.c b/drivers/net/niu.c
index 022866dc0915..7b6cdd07c85f 100644
--- a/drivers/net/niu.c
+++ b/drivers/net/niu.c
@@ -3527,6 +3527,51 @@ out:
3527 } 3527 }
3528} 3528}
3529 3529
3530static inline void niu_sync_rx_discard_stats(struct niu *np,
3531 struct rx_ring_info *rp,
3532 const int limit)
3533{
3534 /* This elaborate scheme is needed for reading the RX discard
3535 * counters, as they are only 16-bit and can overflow quickly,
3536 * and because the overflow indication bit is not usable as
3537 * the counter value does not wrap, but remains at max value
3538 * 0xFFFF.
3539 *
3540 * In theory and in practice counters can be lost in between
3541 * reading nr64() and clearing the counter nw64(). For this
3542 * reason, the number of counter clearings nw64() is
3543 * limited/reduced though the limit parameter.
3544 */
3545 int rx_channel = rp->rx_channel;
3546 u32 misc, wred;
3547
3548 /* RXMISC (Receive Miscellaneous Discard Count), covers the
3549 * following discard events: IPP (Input Port Process),
3550 * FFLP/TCAM, Full RCR (Receive Completion Ring) RBR (Receive
3551 * Block Ring) prefetch buffer is empty.
3552 */
3553 misc = nr64(RXMISC(rx_channel));
3554 if (unlikely((misc & RXMISC_COUNT) > limit)) {
3555 nw64(RXMISC(rx_channel), 0);
3556 rp->rx_errors += misc & RXMISC_COUNT;
3557
3558 if (unlikely(misc & RXMISC_OFLOW))
3559 dev_err(np->device, "rx-%d: Counter overflow "
3560 "RXMISC discard\n", rx_channel);
3561 }
3562
3563 /* WRED (Weighted Random Early Discard) by hardware */
3564 wred = nr64(RED_DIS_CNT(rx_channel));
3565 if (unlikely((wred & RED_DIS_CNT_COUNT) > limit)) {
3566 nw64(RED_DIS_CNT(rx_channel), 0);
3567 rp->rx_dropped += wred & RED_DIS_CNT_COUNT;
3568
3569 if (unlikely(wred & RED_DIS_CNT_OFLOW))
3570 dev_err(np->device, "rx-%d: Counter overflow "
3571 "WRED discard\n", rx_channel);
3572 }
3573}
3574
3530static int niu_rx_work(struct niu *np, struct rx_ring_info *rp, int budget) 3575static int niu_rx_work(struct niu *np, struct rx_ring_info *rp, int budget)
3531{ 3576{
3532 int qlen, rcr_done = 0, work_done = 0; 3577 int qlen, rcr_done = 0, work_done = 0;
@@ -3567,6 +3612,8 @@ static int niu_rx_work(struct niu *np, struct rx_ring_info *rp, int budget)
3567 3612
3568 nw64(RX_DMA_CTL_STAT(rp->rx_channel), stat); 3613 nw64(RX_DMA_CTL_STAT(rp->rx_channel), stat);
3569 3614
3615 niu_sync_rx_discard_stats(np, rp, 0x7FFF);
3616
3570 return work_done; 3617 return work_done;
3571} 3618}
3572 3619
@@ -6073,6 +6120,8 @@ static void niu_get_rx_stats(struct niu *np)
6073 for (i = 0; i < np->num_rx_rings; i++) { 6120 for (i = 0; i < np->num_rx_rings; i++) {
6074 struct rx_ring_info *rp = &np->rx_rings[i]; 6121 struct rx_ring_info *rp = &np->rx_rings[i];
6075 6122
6123 niu_sync_rx_discard_stats(np, rp, 0);
6124
6076 pkts += rp->rx_packets; 6125 pkts += rp->rx_packets;
6077 bytes += rp->rx_bytes; 6126 bytes += rp->rx_bytes;
6078 dropped += rp->rx_dropped; 6127 dropped += rp->rx_dropped;
@@ -7014,6 +7063,8 @@ static void niu_get_ethtool_stats(struct net_device *dev,
7014 for (i = 0; i < np->num_rx_rings; i++) { 7063 for (i = 0; i < np->num_rx_rings; i++) {
7015 struct rx_ring_info *rp = &np->rx_rings[i]; 7064 struct rx_ring_info *rp = &np->rx_rings[i];
7016 7065
7066 niu_sync_rx_discard_stats(np, rp, 0);
7067
7017 data[0] = rp->rx_channel; 7068 data[0] = rp->rx_channel;
7018 data[1] = rp->rx_packets; 7069 data[1] = rp->rx_packets;
7019 data[2] = rp->rx_bytes; 7070 data[2] = rp->rx_bytes;