[netdevice] Retain and report detailed error breakdowns
authorMichael Brown <mcb30@etherboot.org>
Sat, 8 Nov 2008 02:18:30 +0000 (02:18 +0000)
committerMichael Brown <mcb30@etherboot.org>
Sat, 8 Nov 2008 05:30:30 +0000 (05:30 +0000)
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
src/interface/efi/efi_snp.c
src/interface/pxe/pxe_undi.c
src/net/netdevice.c
src/usr/ifmgmt.c

index c4f4a7b..7be5b81 100644 (file)
@@ -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;
index f84bf10..4013662 100644 (file)
@@ -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;
index f321062..4e4a3da 100644 (file)
@@ -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;
index 81c4ae8..e8587a9 100644 (file)
@@ -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 );
 }
 
 /**
index 9c88ab5..72d8613 100644 (file)
@@ -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" );
 }
 
 /**