mirror of https://github.com/ipxe/ipxe.git
[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.pull/1/head
parent
46f43d8ea7
commit
9a52ba0cfa
|
@ -193,16 +193,25 @@ struct net_device_operations {
|
||||||
void ( * irq ) ( struct net_device *netdev, int enable );
|
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 */
|
/** Network device statistics */
|
||||||
struct net_device_stats {
|
struct net_device_stats {
|
||||||
/** Count of successfully completed transmissions */
|
/** Count of successful completions */
|
||||||
unsigned int tx_ok;
|
unsigned int good;
|
||||||
/** Count of transmission errors */
|
/** Count of error completions */
|
||||||
unsigned int tx_err;
|
unsigned int bad;
|
||||||
/** Count of successfully received packets */
|
/** Error breakdowns */
|
||||||
unsigned int rx_ok;
|
struct net_device_error errors[NETDEV_MAX_UNIQUE_ERRORS];
|
||||||
/** Count of reception errors */
|
|
||||||
unsigned int rx_err;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -250,8 +259,10 @@ struct net_device {
|
||||||
struct list_head tx_queue;
|
struct list_head tx_queue;
|
||||||
/** RX packet queue */
|
/** RX packet queue */
|
||||||
struct list_head rx_queue;
|
struct list_head rx_queue;
|
||||||
/** Device statistics */
|
/** TX statistics */
|
||||||
struct net_device_stats stats;
|
struct net_device_stats tx_stats;
|
||||||
|
/** RX statistics */
|
||||||
|
struct net_device_stats rx_stats;
|
||||||
|
|
||||||
/** Configuration settings applicable to this device */
|
/** Configuration settings applicable to this device */
|
||||||
struct simple_settings settings;
|
struct simple_settings settings;
|
||||||
|
|
|
@ -317,14 +317,14 @@ efi_snp_statistics ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN reset,
|
||||||
|
|
||||||
/* Gather statistics */
|
/* Gather statistics */
|
||||||
memset ( &stats_buf, 0, sizeof ( stats_buf ) );
|
memset ( &stats_buf, 0, sizeof ( stats_buf ) );
|
||||||
stats_buf.TxGoodFrames = snpdev->netdev->stats.tx_ok;
|
stats_buf.TxGoodFrames = snpdev->netdev->tx_stats.good;
|
||||||
stats_buf.TxDroppedFrames = snpdev->netdev->stats.tx_err;
|
stats_buf.TxDroppedFrames = snpdev->netdev->tx_stats.bad;
|
||||||
stats_buf.TxTotalFrames = ( snpdev->netdev->stats.tx_ok +
|
stats_buf.TxTotalFrames = ( snpdev->netdev->tx_stats.good +
|
||||||
snpdev->netdev->stats.tx_err );
|
snpdev->netdev->tx_stats.bad );
|
||||||
stats_buf.RxGoodFrames = snpdev->netdev->stats.rx_ok;
|
stats_buf.RxGoodFrames = snpdev->netdev->rx_stats.good;
|
||||||
stats_buf.RxDroppedFrames = snpdev->netdev->stats.rx_err;
|
stats_buf.RxDroppedFrames = snpdev->netdev->rx_stats.bad;
|
||||||
stats_buf.RxTotalFrames = ( snpdev->netdev->stats.rx_ok +
|
stats_buf.RxTotalFrames = ( snpdev->netdev->rx_stats.good +
|
||||||
snpdev->netdev->stats.rx_err );
|
snpdev->netdev->rx_stats.bad );
|
||||||
if ( *stats_len > sizeof ( stats_buf ) )
|
if ( *stats_len > sizeof ( stats_buf ) )
|
||||||
*stats_len = sizeof ( stats_buf );
|
*stats_len = sizeof ( stats_buf );
|
||||||
if ( stats )
|
if ( stats )
|
||||||
|
@ -332,8 +332,10 @@ efi_snp_statistics ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN reset,
|
||||||
|
|
||||||
/* Reset statistics if requested to do so */
|
/* Reset statistics if requested to do so */
|
||||||
if ( reset ) {
|
if ( reset ) {
|
||||||
memset ( &snpdev->netdev->stats, 0,
|
memset ( &snpdev->netdev->tx_stats, 0,
|
||||||
sizeof ( snpdev->netdev->stats ) );
|
sizeof ( snpdev->netdev->tx_stats ) );
|
||||||
|
memset ( &snpdev->netdev->rx_stats, 0,
|
||||||
|
sizeof ( snpdev->netdev->rx_stats ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -392,10 +392,10 @@ PXENV_EXIT_t pxenv_undi_get_statistics ( struct s_PXENV_UNDI_GET_STATISTICS
|
||||||
*undi_get_statistics ) {
|
*undi_get_statistics ) {
|
||||||
DBG ( "PXENV_UNDI_GET_STATISTICS" );
|
DBG ( "PXENV_UNDI_GET_STATISTICS" );
|
||||||
|
|
||||||
undi_get_statistics->XmtGoodFrames = pxe_netdev->stats.tx_ok;
|
undi_get_statistics->XmtGoodFrames = pxe_netdev->tx_stats.good;
|
||||||
undi_get_statistics->RcvGoodFrames = pxe_netdev->stats.rx_ok;
|
undi_get_statistics->RcvGoodFrames = pxe_netdev->rx_stats.good;
|
||||||
undi_get_statistics->RcvCRCErrors = pxe_netdev->stats.rx_err;
|
undi_get_statistics->RcvCRCErrors = pxe_netdev->rx_stats.bad;
|
||||||
undi_get_statistics->RcvResourceErrors = pxe_netdev->stats.rx_err;
|
undi_get_statistics->RcvResourceErrors = pxe_netdev->rx_stats.bad;
|
||||||
|
|
||||||
undi_get_statistics->Status = PXENV_STATUS_SUCCESS;
|
undi_get_statistics->Status = PXENV_STATUS_SUCCESS;
|
||||||
return PXENV_EXIT_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 ) {
|
*undi_clear_statistics ) {
|
||||||
DBG ( "PXENV_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;
|
undi_clear_statistics->Status = PXENV_STATUS_SUCCESS;
|
||||||
return PXENV_EXIT_SUCCESS;
|
return PXENV_EXIT_SUCCESS;
|
||||||
|
|
|
@ -45,6 +45,45 @@ static struct net_protocol net_protocols_end[0]
|
||||||
/** List of network devices */
|
/** List of network devices */
|
||||||
struct list_head net_devices = LIST_HEAD_INIT ( net_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
|
* 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 ) {
|
struct io_buffer *iobuf, int rc ) {
|
||||||
|
|
||||||
/* Update statistics counter */
|
/* Update statistics counter */
|
||||||
|
netdev_record_stat ( &netdev->tx_stats, rc );
|
||||||
if ( rc == 0 ) {
|
if ( rc == 0 ) {
|
||||||
netdev->stats.tx_ok++;
|
|
||||||
DBGC ( netdev, "NETDEV %p transmission %p complete\n",
|
DBGC ( netdev, "NETDEV %p transmission %p complete\n",
|
||||||
netdev, iobuf );
|
netdev, iobuf );
|
||||||
} else {
|
} else {
|
||||||
netdev->stats.tx_err++;
|
|
||||||
DBGC ( netdev, "NETDEV %p transmission %p failed: %s\n",
|
DBGC ( netdev, "NETDEV %p transmission %p failed: %s\n",
|
||||||
netdev, iobuf, strerror ( rc ) );
|
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 );
|
list_add_tail ( &iobuf->list, &netdev->rx_queue );
|
||||||
|
|
||||||
/* Update statistics counter */
|
/* 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 );
|
free_iob ( iobuf );
|
||||||
|
|
||||||
/* Update statistics counter */
|
/* Update statistics counter */
|
||||||
netdev->stats.rx_err++;
|
netdev_record_stat ( &netdev->rx_stats, rc );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -58,6 +58,25 @@ void ifclose ( struct net_device *netdev ) {
|
||||||
netdev_close ( 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
|
* Print status of network device
|
||||||
*
|
*
|
||||||
|
@ -69,8 +88,10 @@ void ifstat ( struct net_device *netdev ) {
|
||||||
netdev->name, netdev_hwaddr ( netdev ), netdev->dev->name,
|
netdev->name, netdev_hwaddr ( netdev ), netdev->dev->name,
|
||||||
( ( netdev->state & NETDEV_OPEN ) ? "open" : "closed" ),
|
( ( netdev->state & NETDEV_OPEN ) ? "open" : "closed" ),
|
||||||
( netdev_link_ok ( netdev ) ? "up" : "down" ),
|
( netdev_link_ok ( netdev ) ? "up" : "down" ),
|
||||||
netdev->stats.tx_ok, netdev->stats.tx_err,
|
netdev->tx_stats.good, netdev->tx_stats.bad,
|
||||||
netdev->stats.rx_ok, netdev->stats.rx_err );
|
netdev->rx_stats.good, netdev->rx_stats.bad );
|
||||||
|
ifstat_errors ( &netdev->tx_stats, "TXE" );
|
||||||
|
ifstat_errors ( &netdev->rx_stats, "RXE" );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue