From 539ff45fd0e8a4d4a979c28d9e5be8526a0eccaf Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 5 Jul 2007 17:18:27 +0100 Subject: [PATCH] Allow recording of TX and RX errors to aid in end-user debugging. --- src/include/gpxe/netdevice.h | 43 ++++++++++++++++++++---- src/interface/pxe/pxe_undi.c | 8 ++--- src/net/netdevice.c | 64 +++++++++++++++++++++++++++--------- src/usr/ifmgmt.c | 5 +-- 4 files changed, 92 insertions(+), 28 deletions(-) diff --git a/src/include/gpxe/netdevice.h b/src/include/gpxe/netdevice.h index 0bc5311c..d6b9a1be 100644 --- a/src/include/gpxe/netdevice.h +++ b/src/include/gpxe/netdevice.h @@ -134,9 +134,13 @@ struct ll_protocol { */ struct net_device_stats { /** Count of successfully completed transmissions */ - unsigned int tx_count; + unsigned int tx_ok; + /** Count of transmission errors */ + unsigned int tx_err; /** Count of successfully received packets */ - unsigned int rx_count; + unsigned int rx_ok; + /** Count of reception errors */ + unsigned int rx_err; }; /** @@ -189,7 +193,8 @@ struct net_device { * owned by the net device's TX queue, and the net device must * eventually call netdev_tx_complete() to free the buffer. * If this method returns failure, the I/O buffer is - * immediately released. + * immediately released; the failure is interpreted as + * "failure to enqueue buffer". * * This method is guaranteed to be called only when the device * is open. @@ -289,10 +294,12 @@ netdev_put ( struct net_device *netdev ) { } extern int netdev_tx ( struct net_device *netdev, struct io_buffer *iobuf ); -extern void netdev_tx_complete ( struct net_device *netdev, - struct io_buffer *iobuf ); -extern void netdev_tx_complete_next ( struct net_device *netdev ); +extern void netdev_tx_complete_err ( struct net_device *netdev, + struct io_buffer *iobuf, int rc ); +extern void netdev_tx_complete_next_err ( struct net_device *netdev, int rc ); extern void netdev_rx ( struct net_device *netdev, struct io_buffer *iobuf ); +extern void netdev_rx_err ( struct net_device *netdev, + struct io_buffer *iobuf, int rc ); extern int netdev_poll ( struct net_device *netdev, unsigned int rx_quota ); extern struct io_buffer * netdev_rx_dequeue ( struct net_device *netdev ); extern struct net_device * alloc_netdev ( size_t priv_size ); @@ -308,4 +315,28 @@ extern int net_tx ( struct io_buffer *iobuf, struct net_device *netdev, extern int net_rx ( struct io_buffer *iobuf, struct net_device *netdev, uint16_t net_proto, const void *ll_source ); +/** + * Complete network transmission + * + * @v netdev Network device + * @v iobuf I/O buffer + * + * The packet must currently be in the network device's TX queue. + */ +static inline void netdev_tx_complete ( struct net_device *netdev, + struct io_buffer *iobuf ) { + netdev_tx_complete_err ( netdev, iobuf, 0 ); +} + +/** + * Complete network transmission + * + * @v netdev Network device + * + * Completes the oldest outstanding packet in the TX queue. + */ +static inline void netdev_tx_complete_next ( struct net_device *netdev ) { + netdev_tx_complete_next_err ( netdev, 0 ); +} + #endif /* _GPXE_NETDEVICE_H */ diff --git a/src/interface/pxe/pxe_undi.c b/src/interface/pxe/pxe_undi.c index 98c4cb10..cc23de70 100644 --- a/src/interface/pxe/pxe_undi.c +++ b/src/interface/pxe/pxe_undi.c @@ -371,10 +371,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_count; - undi_get_statistics->RcvGoodFrames = pxe_netdev->stats.rx_count; - undi_get_statistics->RcvCRCErrors = 0; - undi_get_statistics->RcvResourceErrors = 0; + 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->Status = PXENV_STATUS_SUCCESS; return PXENV_EXIT_SUCCESS; diff --git a/src/net/netdevice.c b/src/net/netdevice.c index fb4612bc..a5c8890e 100644 --- a/src/net/netdevice.c +++ b/src/net/netdevice.c @@ -74,9 +74,7 @@ int netdev_tx ( struct net_device *netdev, struct io_buffer *iobuf ) { return 0; err: - DBGC ( netdev, "NETDEV %p transmission %p failed: %s\n", - netdev, iobuf, strerror ( rc ) ); - netdev_tx_complete ( netdev, iobuf ); + netdev_tx_complete_err ( netdev, iobuf, rc ); return rc; } @@ -85,11 +83,23 @@ int netdev_tx ( struct net_device *netdev, struct io_buffer *iobuf ) { * * @v netdev Network device * @v iobuf I/O buffer + * @v rc Packet status code * * The packet must currently be in the network device's TX queue. */ -void netdev_tx_complete ( struct net_device *netdev, struct io_buffer *iobuf ) { - DBGC ( netdev, "NETDEV %p transmission %p complete\n", netdev, iobuf ); +void netdev_tx_complete_err ( struct net_device *netdev, + struct io_buffer *iobuf, int rc ) { + + /* Update statistics counter */ + 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 ) ); + } /* Catch data corruption as early as possible */ assert ( iobuf->list.next != NULL ); @@ -98,23 +108,21 @@ void netdev_tx_complete ( struct net_device *netdev, struct io_buffer *iobuf ) { /* Dequeue and free I/O buffer */ list_del ( &iobuf->list ); free_iob ( iobuf ); - - /* Update statistics counter */ - netdev->stats.tx_count++; } /** * Complete network transmission * * @v netdev Network device + * @v rc Packet status code * * Completes the oldest outstanding packet in the TX queue. */ -void netdev_tx_complete_next ( struct net_device *netdev ) { +void netdev_tx_complete_next_err ( struct net_device *netdev, int rc ) { struct io_buffer *iobuf; list_for_each_entry ( iobuf, &netdev->tx_queue, list ) { - netdev_tx_complete ( netdev, iobuf ); + netdev_tx_complete_err ( netdev, iobuf, rc ); return; } } @@ -128,7 +136,7 @@ static void netdev_tx_flush ( struct net_device *netdev ) { /* Discard any packets in the TX queue */ while ( ! list_empty ( &netdev->tx_queue ) ) { - netdev_tx_complete_next ( netdev ); + netdev_tx_complete_next_err ( netdev, -ECANCELED ); } } @@ -136,12 +144,13 @@ static void netdev_tx_flush ( struct net_device *netdev ) { * Add packet to receive queue * * @v netdev Network device - * @v iobuf I/O buffer + * @v iobuf I/O buffer, or NULL * * The packet is added to the network device's RX queue. This * function takes ownership of the I/O buffer. */ void netdev_rx ( struct net_device *netdev, struct io_buffer *iobuf ) { + DBGC ( netdev, "NETDEV %p received %p (%p+%zx)\n", netdev, iobuf, iobuf->data, iob_len ( iobuf ) ); @@ -149,7 +158,32 @@ 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_count++; + netdev->stats.rx_ok++; +} + +/** + * Discard received packet + * + * @v netdev Network device + * @v iobuf I/O buffer, or NULL + * @v rc Packet status code + * + * The packet is discarded and an RX error is recorded. This function + * takes ownership of the I/O buffer. @c iobuf may be NULL if, for + * example, the net device wishes to report an error due to being + * unable to allocate an I/O buffer. + */ +void netdev_rx_err ( struct net_device *netdev, + struct io_buffer *iobuf, int rc ) { + + DBGC ( netdev, "NETDEV %p failed to receive %p: %s\n", + netdev, iobuf, strerror ( rc ) ); + + /* Discard packet */ + free_iob ( iobuf ); + + /* Update statistics counter */ + netdev->stats.rx_err++; } /** @@ -200,9 +234,7 @@ static void netdev_rx_flush ( struct net_device *netdev ) { /* Discard any packets in the RX queue */ while ( ( iobuf = netdev_rx_dequeue ( netdev ) ) ) { - DBGC ( netdev, "NETDEV %p discarding received %p\n", - netdev, iobuf ); - free_iob ( iobuf ); + netdev_rx_err ( netdev, iobuf, -ECANCELED ); } } diff --git a/src/usr/ifmgmt.c b/src/usr/ifmgmt.c index a43c4ca0..5f4323de 100644 --- a/src/usr/ifmgmt.c +++ b/src/usr/ifmgmt.c @@ -61,8 +61,9 @@ void ifclose ( struct net_device *netdev ) { * @v netdev Network device */ void ifstat ( struct net_device *netdev ) { - printf ( "%s: %s on %s (%s) TX:%d RX:%d\n", + printf ( "%s: %s on %s (%s) TX:%d TXE:%d RX:%d RXE:%d\n", netdev->name, netdev_hwaddr ( netdev ), netdev->dev->name, ( ( netdev->state & NETDEV_OPEN ) ? "open" : "closed" ), - netdev->stats.tx_count, netdev->stats.rx_count ); + netdev->stats.tx_ok, netdev->stats.tx_err, + netdev->stats.rx_ok, netdev->stats.rx_err ); }