From 9a52ba0cfac0fffb39b5a4c148ec7641bcc38381 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 8 Nov 2008 02:18:30 +0000 Subject: [netdevice] Retain and report detailed error breakdowns netdev_rx_err() and netdev_tx_complete_err() get passed the error code, but currently use it only in debug messages. Retain error numbers and frequencey counts for up to NETDEV_MAX_UNIQUE_ERRORS (4) different errors for each of TX and RX. This allows the "ifstat" command to report the reasons for TX/RX errors in most cases, even in non-debug builds. --- src/include/gpxe/netdevice.h | 31 +++++++++++++++++++---------- src/interface/efi/efi_snp.c | 22 +++++++++++---------- src/interface/pxe/pxe_undi.c | 11 ++++++----- src/net/netdevice.c | 46 ++++++++++++++++++++++++++++++++++++++++---- src/usr/ifmgmt.c | 25 ++++++++++++++++++++++-- 5 files changed, 104 insertions(+), 31 deletions(-) diff --git a/src/include/gpxe/netdevice.h b/src/include/gpxe/netdevice.h index c4f4a7be..7be5b813 100644 --- a/src/include/gpxe/netdevice.h +++ b/src/include/gpxe/netdevice.h @@ -193,16 +193,25 @@ struct net_device_operations { void ( * irq ) ( struct net_device *netdev, int enable ); }; +/** Network device error */ +struct net_device_error { + /** Error status code */ + int rc; + /** Error count */ + unsigned int count; +}; + +/** Maximum number of unique errors that we will keep track of */ +#define NETDEV_MAX_UNIQUE_ERRORS 4 + /** Network device statistics */ struct net_device_stats { - /** Count of successfully completed transmissions */ - unsigned int tx_ok; - /** Count of transmission errors */ - unsigned int tx_err; - /** Count of successfully received packets */ - unsigned int rx_ok; - /** Count of reception errors */ - unsigned int rx_err; + /** Count of successful completions */ + unsigned int good; + /** Count of error completions */ + unsigned int bad; + /** Error breakdowns */ + struct net_device_error errors[NETDEV_MAX_UNIQUE_ERRORS]; }; /** @@ -250,8 +259,10 @@ struct net_device { struct list_head tx_queue; /** RX packet queue */ struct list_head rx_queue; - /** Device statistics */ - struct net_device_stats stats; + /** TX statistics */ + struct net_device_stats tx_stats; + /** RX statistics */ + struct net_device_stats rx_stats; /** Configuration settings applicable to this device */ struct simple_settings settings; diff --git a/src/interface/efi/efi_snp.c b/src/interface/efi/efi_snp.c index f84bf10e..40136623 100644 --- a/src/interface/efi/efi_snp.c +++ b/src/interface/efi/efi_snp.c @@ -317,14 +317,14 @@ efi_snp_statistics ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN reset, /* Gather statistics */ memset ( &stats_buf, 0, sizeof ( stats_buf ) ); - stats_buf.TxGoodFrames = snpdev->netdev->stats.tx_ok; - stats_buf.TxDroppedFrames = snpdev->netdev->stats.tx_err; - stats_buf.TxTotalFrames = ( snpdev->netdev->stats.tx_ok + - snpdev->netdev->stats.tx_err ); - stats_buf.RxGoodFrames = snpdev->netdev->stats.rx_ok; - stats_buf.RxDroppedFrames = snpdev->netdev->stats.rx_err; - stats_buf.RxTotalFrames = ( snpdev->netdev->stats.rx_ok + - snpdev->netdev->stats.rx_err ); + stats_buf.TxGoodFrames = snpdev->netdev->tx_stats.good; + stats_buf.TxDroppedFrames = snpdev->netdev->tx_stats.bad; + stats_buf.TxTotalFrames = ( snpdev->netdev->tx_stats.good + + snpdev->netdev->tx_stats.bad ); + stats_buf.RxGoodFrames = snpdev->netdev->rx_stats.good; + stats_buf.RxDroppedFrames = snpdev->netdev->rx_stats.bad; + stats_buf.RxTotalFrames = ( snpdev->netdev->rx_stats.good + + snpdev->netdev->rx_stats.bad ); if ( *stats_len > sizeof ( stats_buf ) ) *stats_len = sizeof ( stats_buf ); if ( stats ) @@ -332,8 +332,10 @@ efi_snp_statistics ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN reset, /* Reset statistics if requested to do so */ if ( reset ) { - memset ( &snpdev->netdev->stats, 0, - sizeof ( snpdev->netdev->stats ) ); + memset ( &snpdev->netdev->tx_stats, 0, + sizeof ( snpdev->netdev->tx_stats ) ); + memset ( &snpdev->netdev->rx_stats, 0, + sizeof ( snpdev->netdev->rx_stats ) ); } return 0; diff --git a/src/interface/pxe/pxe_undi.c b/src/interface/pxe/pxe_undi.c index f3210629..4e4a3da0 100644 --- a/src/interface/pxe/pxe_undi.c +++ b/src/interface/pxe/pxe_undi.c @@ -392,10 +392,10 @@ PXENV_EXIT_t pxenv_undi_get_statistics ( struct s_PXENV_UNDI_GET_STATISTICS *undi_get_statistics ) { DBG ( "PXENV_UNDI_GET_STATISTICS" ); - undi_get_statistics->XmtGoodFrames = pxe_netdev->stats.tx_ok; - undi_get_statistics->RcvGoodFrames = pxe_netdev->stats.rx_ok; - undi_get_statistics->RcvCRCErrors = pxe_netdev->stats.rx_err; - undi_get_statistics->RcvResourceErrors = pxe_netdev->stats.rx_err; + undi_get_statistics->XmtGoodFrames = pxe_netdev->tx_stats.good; + undi_get_statistics->RcvGoodFrames = pxe_netdev->rx_stats.good; + undi_get_statistics->RcvCRCErrors = pxe_netdev->rx_stats.bad; + undi_get_statistics->RcvResourceErrors = pxe_netdev->rx_stats.bad; undi_get_statistics->Status = PXENV_STATUS_SUCCESS; return PXENV_EXIT_SUCCESS; @@ -409,7 +409,8 @@ PXENV_EXIT_t pxenv_undi_clear_statistics ( struct s_PXENV_UNDI_CLEAR_STATISTICS *undi_clear_statistics ) { DBG ( "PXENV_UNDI_CLEAR_STATISTICS" ); - memset ( &pxe_netdev->stats, 0, sizeof ( pxe_netdev->stats ) ); + memset ( &pxe_netdev->tx_stats, 0, sizeof ( pxe_netdev->tx_stats ) ); + memset ( &pxe_netdev->rx_stats, 0, sizeof ( pxe_netdev->rx_stats ) ); undi_clear_statistics->Status = PXENV_STATUS_SUCCESS; return PXENV_EXIT_SUCCESS; diff --git a/src/net/netdevice.c b/src/net/netdevice.c index 81c4ae88..e8587a9d 100644 --- a/src/net/netdevice.c +++ b/src/net/netdevice.c @@ -45,6 +45,45 @@ static struct net_protocol net_protocols_end[0] /** List of network devices */ struct list_head net_devices = LIST_HEAD_INIT ( net_devices ); +/** + * Record network device statistic + * + * @v stats Network device statistics + * @v rc Status code + */ +static void netdev_record_stat ( struct net_device_stats *stats, int rc ) { + struct net_device_error *error; + struct net_device_error *least_common_error; + unsigned int i; + + /* If this is not an error, just update the good counter */ + if ( rc == 0 ) { + stats->good++; + return; + } + + /* Update the bad counter */ + stats->bad++; + + /* Locate the appropriate error record */ + least_common_error = &stats->errors[0]; + for ( i = 0 ; i < ( sizeof ( stats->errors ) / + sizeof ( stats->errors[0] ) ) ; i++ ) { + error = &stats->errors[i]; + /* Update matching record, if found */ + if ( error->rc == rc ) { + error->count++; + return; + } + if ( error->count < least_common_error->count ) + least_common_error = error; + } + + /* Overwrite the least common error record */ + least_common_error->rc = rc; + least_common_error->count = 1; +} + /** * Transmit raw packet via network device * @@ -91,12 +130,11 @@ void netdev_tx_complete_err ( struct net_device *netdev, struct io_buffer *iobuf, int rc ) { /* Update statistics counter */ + netdev_record_stat ( &netdev->tx_stats, rc ); if ( rc == 0 ) { - netdev->stats.tx_ok++; DBGC ( netdev, "NETDEV %p transmission %p complete\n", netdev, iobuf ); } else { - netdev->stats.tx_err++; DBGC ( netdev, "NETDEV %p transmission %p failed: %s\n", netdev, iobuf, strerror ( rc ) ); } @@ -158,7 +196,7 @@ void netdev_rx ( struct net_device *netdev, struct io_buffer *iobuf ) { list_add_tail ( &iobuf->list, &netdev->rx_queue ); /* Update statistics counter */ - netdev->stats.rx_ok++; + netdev_record_stat ( &netdev->rx_stats, 0 ); } /** @@ -183,7 +221,7 @@ void netdev_rx_err ( struct net_device *netdev, free_iob ( iobuf ); /* Update statistics counter */ - netdev->stats.rx_err++; + netdev_record_stat ( &netdev->rx_stats, rc ); } /** diff --git a/src/usr/ifmgmt.c b/src/usr/ifmgmt.c index 9c88ab53..72d86137 100644 --- a/src/usr/ifmgmt.c +++ b/src/usr/ifmgmt.c @@ -58,6 +58,25 @@ void ifclose ( struct net_device *netdev ) { netdev_close ( netdev ); } +/** + * Print network device error breakdown + * + * @v stats Network device statistics + * @v prefix Message prefix + */ +static void ifstat_errors ( struct net_device_stats *stats, + const char *prefix ) { + unsigned int i; + + for ( i = 0 ; i < ( sizeof ( stats->errors ) / + sizeof ( stats->errors[0] ) ) ; i++ ) { + if ( stats->errors[i].count ) + printf ( " [%s: %d x \"%s\"]\n", prefix, + stats->errors[i].count, + strerror ( stats->errors[i].rc ) ); + } +} + /** * Print status of network device * @@ -69,8 +88,10 @@ void ifstat ( struct net_device *netdev ) { netdev->name, netdev_hwaddr ( netdev ), netdev->dev->name, ( ( netdev->state & NETDEV_OPEN ) ? "open" : "closed" ), ( netdev_link_ok ( netdev ) ? "up" : "down" ), - netdev->stats.tx_ok, netdev->stats.tx_err, - netdev->stats.rx_ok, netdev->stats.rx_err ); + netdev->tx_stats.good, netdev->tx_stats.bad, + netdev->rx_stats.good, netdev->rx_stats.bad ); + ifstat_errors ( &netdev->tx_stats, "TXE" ); + ifstat_errors ( &netdev->rx_stats, "RXE" ); } /** -- cgit v1.2.3-55-g7522