summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Brown2008-11-08 03:18:30 +0100
committerMichael Brown2008-11-08 06:30:30 +0100
commit9a52ba0cfac0fffb39b5a4c148ec7641bcc38381 (patch)
tree12059ca7f75d37d1b62ffa550798fa8233834590
parent[phantom] Do not halt PEGs on driver shutdown (diff)
downloadipxe-9a52ba0cfac0fffb39b5a4c148ec7641bcc38381.tar.gz
ipxe-9a52ba0cfac0fffb39b5a4c148ec7641bcc38381.tar.xz
ipxe-9a52ba0cfac0fffb39b5a4c148ec7641bcc38381.zip
[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.
-rw-r--r--src/include/gpxe/netdevice.h31
-rw-r--r--src/interface/efi/efi_snp.c22
-rw-r--r--src/interface/pxe/pxe_undi.c11
-rw-r--r--src/net/netdevice.c46
-rw-r--r--src/usr/ifmgmt.c25
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
@@ -46,6 +46,45 @@ static struct net_protocol net_protocols_end[0]
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
*
* @v netdev 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
@@ -59,6 +59,25 @@ void ifclose ( struct net_device *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
*
* @v netdev 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" );
}
/**