diff options
author | Russell King <rmk+kernel@arm.linux.org.uk> | 2014-04-01 23:06:09 +0100 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2014-10-17 14:34:50 +0100 |
commit | 313c7fd3bb73c5ff942d48020bd6e3cc11a4f935 (patch) | |
tree | 14bf10f47cca05fb190901eb90ec7ee26641b048 | |
parent | bfe01a5ba2490f299e1d2d5508cbbbadd897bbe9 (diff) |
net: fec: fix receive error handling
The FEC receive error handling suffers from several problems:
1. When a FIFO overrun error occurs, the descriptor is closed and
reception stops. The remainder of the packet is discarded by the
MAC.
The documentation states that other status bits are invalid, and they
will be zero. However, practical experience on iMX6 shows this is
not the case - the CR (crc error) bit will also be set.
This leads to each FIFO overrun event incrementing both the fifo
error count and the crc error count, which makes the error statistics
less useful. Fix this by ignoring all other status bits of the FIFO
overrun is set, and add a comment to that effect.
2. A late collision invalidates all but the overrun condition; the
remaining error conditions must be ignored.
3. Despite accounting for errors, it continues to receive the errored
packets and pass them into the network stack as if they were
correctly received.
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
-rw-r--r-- | drivers/net/ethernet/freescale/fec.h | 1 | ||||
-rw-r--r-- | drivers/net/ethernet/freescale/fec_main.c | 47 |
2 files changed, 28 insertions, 20 deletions
diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h index ee41d98b44b6..ec4c76264eb9 100644 --- a/drivers/net/ethernet/freescale/fec.h +++ b/drivers/net/ethernet/freescale/fec.h @@ -202,6 +202,7 @@ struct bufdesc_ex { #define BD_ENET_RX_OV ((ushort)0x0002) #define BD_ENET_RX_CL ((ushort)0x0001) #define BD_ENET_RX_STATS ((ushort)0x013f) /* All status bits */ +#define BD_ENET_RX_ERROR ((ushort)0x003f) /* Enhanced buffer descriptor control/status used by Ethernet receive */ #define BD_ENET_RX_VLAN 0x00000004 diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 89355a719625..fd49bd70d15d 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -1218,28 +1218,35 @@ fec_enet_rx(struct net_device *ndev, int budget) writel(FEC_ENET_RXF, fep->hwp + FEC_IEVENT); /* Check for errors. */ - if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH | BD_ENET_RX_NO | - BD_ENET_RX_CR | BD_ENET_RX_OV)) { + if (status & BD_ENET_RX_ERROR) { ndev->stats.rx_errors++; - if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH)) { - /* Frame too long or too short. */ - ndev->stats.rx_length_errors++; - } - if (status & BD_ENET_RX_NO) /* Frame alignment */ - ndev->stats.rx_frame_errors++; - if (status & BD_ENET_RX_CR) /* CRC Error */ - ndev->stats.rx_crc_errors++; - if (status & BD_ENET_RX_OV) /* FIFO overrun */ - ndev->stats.rx_fifo_errors++; - } - /* Report late collisions as a frame error. - * On this error, the BD is closed, but we don't know what we - * have in the buffer. So, just drop this frame on the floor. - */ - if (status & BD_ENET_RX_CL) { - ndev->stats.rx_errors++; - ndev->stats.rx_frame_errors++; + if (status & BD_ENET_RX_OV) { + /* + * FIFO overrun - stored frame and the other + * status (M, LG, NO, CR, CL) are invalid. + * Although docs say these status bits will + * be zero, it has been observed on iMX6 FEC + * that OV is always accompanied by CR set. + */ + ndev->stats.rx_fifo_errors++; + } else if (status & BD_ENET_RX_CL) { + /* + * Report late collisions as a frame error. + * On this error, the BD is closed, but we + * don't know what we have in the buffer. + * So, just drop this frame on the floor. + */ + ndev->stats.rx_frame_errors++; + } else { + if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH)) + /* Frame too long or too short. */ + ndev->stats.rx_length_errors++; + if (status & BD_ENET_RX_NO) /* Frame alignment */ + ndev->stats.rx_frame_errors++; + if (status & BD_ENET_RX_CR) /* CRC Error */ + ndev->stats.rx_crc_errors++; + } goto rx_processing_done; } |