]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - drivers/net/niu.c
NIU: Implement discard counters
[mv-sheeva.git] / drivers / net / niu.c
index 022866dc0915479a593b529b63f96fc8706cfa8b..7b6cdd07c85f0b761a9b42db7b7c3f8860c5b815 100644 (file)
@@ -3527,6 +3527,51 @@ out:
        }
 }
 
+static inline void niu_sync_rx_discard_stats(struct niu *np,
+                                            struct rx_ring_info *rp,
+                                            const int limit)
+{
+       /* This elaborate scheme is needed for reading the RX discard
+        * counters, as they are only 16-bit and can overflow quickly,
+        * and because the overflow indication bit is not usable as
+        * the counter value does not wrap, but remains at max value
+        * 0xFFFF.
+        *
+        * 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 though the limit parameter.
+        */
+       int rx_channel = rp->rx_channel;
+       u32 misc, wred;
+
+       /* RXMISC (Receive Miscellaneous Discard Count), covers the
+        * following discard events: IPP (Input Port Process),
+        * FFLP/TCAM, Full RCR (Receive Completion Ring) RBR (Receive
+        * Block Ring) prefetch buffer is empty.
+        */
+       misc = nr64(RXMISC(rx_channel));
+       if (unlikely((misc & RXMISC_COUNT) > limit)) {
+               nw64(RXMISC(rx_channel), 0);
+               rp->rx_errors += misc & RXMISC_COUNT;
+
+               if (unlikely(misc & RXMISC_OFLOW))
+                       dev_err(np->device, "rx-%d: Counter overflow "
+                               "RXMISC discard\n", rx_channel);
+       }
+
+       /* WRED (Weighted Random Early Discard) by hardware */
+       wred = nr64(RED_DIS_CNT(rx_channel));
+       if (unlikely((wred & RED_DIS_CNT_COUNT) > limit)) {
+               nw64(RED_DIS_CNT(rx_channel), 0);
+               rp->rx_dropped += wred & RED_DIS_CNT_COUNT;
+
+               if (unlikely(wred & RED_DIS_CNT_OFLOW))
+                       dev_err(np->device, "rx-%d: Counter overflow "
+                               "WRED discard\n", rx_channel);
+       }
+}
+
 static int niu_rx_work(struct niu *np, struct rx_ring_info *rp, int budget)
 {
        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)
 
        nw64(RX_DMA_CTL_STAT(rp->rx_channel), stat);
 
+       niu_sync_rx_discard_stats(np, rp, 0x7FFF);
+
        return work_done;
 }
 
@@ -6073,6 +6120,8 @@ static void niu_get_rx_stats(struct niu *np)
        for (i = 0; i < np->num_rx_rings; i++) {
                struct rx_ring_info *rp = &np->rx_rings[i];
 
+               niu_sync_rx_discard_stats(np, rp, 0);
+
                pkts += rp->rx_packets;
                bytes += rp->rx_bytes;
                dropped += rp->rx_dropped;
@@ -7014,6 +7063,8 @@ static void niu_get_ethtool_stats(struct net_device *dev,
        for (i = 0; i < np->num_rx_rings; i++) {
                struct rx_ring_info *rp = &np->rx_rings[i];
 
+               niu_sync_rx_discard_stats(np, rp, 0);
+
                data[0] = rp->rx_channel;
                data[1] = rp->rx_packets;
                data[2] = rp->rx_bytes;