mirror of
https://github.com/ipxe/ipxe
synced 2025-12-13 23:41:45 +03:00
[netdevice] Add a generic concept of a "blocked link"
When Spanning Tree Protocol (STP) is used, there may be a substantial delay (tens of seconds) from the time that the link goes up to the time that the port starts forwarding packets. Add a generic concept of a "blocked link" (i.e. a link which is up but which is not expected to communicate successfully), and allow "ifstat" to indicate when a link is blocked. Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
@@ -15,6 +15,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
|||||||
#include <ipxe/refcnt.h>
|
#include <ipxe/refcnt.h>
|
||||||
#include <ipxe/settings.h>
|
#include <ipxe/settings.h>
|
||||||
#include <ipxe/interface.h>
|
#include <ipxe/interface.h>
|
||||||
|
#include <ipxe/retry.h>
|
||||||
|
|
||||||
struct io_buffer;
|
struct io_buffer;
|
||||||
struct net_device;
|
struct net_device;
|
||||||
@@ -392,6 +393,8 @@ struct net_device {
|
|||||||
* indicates the error preventing link-up.
|
* indicates the error preventing link-up.
|
||||||
*/
|
*/
|
||||||
int link_rc;
|
int link_rc;
|
||||||
|
/** Link block timer */
|
||||||
|
struct retry_timer link_block;
|
||||||
/** Maximum packet length
|
/** Maximum packet length
|
||||||
*
|
*
|
||||||
* This length includes any link-layer headers.
|
* This length includes any link-layer headers.
|
||||||
@@ -613,6 +616,17 @@ netdev_link_ok ( struct net_device *netdev ) {
|
|||||||
return ( netdev->link_rc == 0 );
|
return ( netdev->link_rc == 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check link block state of network device
|
||||||
|
*
|
||||||
|
* @v netdev Network device
|
||||||
|
* @ret link_blocked Link is blocked
|
||||||
|
*/
|
||||||
|
static inline __attribute__ (( always_inline )) int
|
||||||
|
netdev_link_blocked ( struct net_device *netdev ) {
|
||||||
|
return ( timer_running ( &netdev->link_block ) );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check whether or not network device is open
|
* Check whether or not network device is open
|
||||||
*
|
*
|
||||||
@@ -661,6 +675,9 @@ extern void netdev_rx_freeze ( struct net_device *netdev );
|
|||||||
extern void netdev_rx_unfreeze ( struct net_device *netdev );
|
extern void netdev_rx_unfreeze ( struct net_device *netdev );
|
||||||
extern void netdev_link_err ( struct net_device *netdev, int rc );
|
extern void netdev_link_err ( struct net_device *netdev, int rc );
|
||||||
extern void netdev_link_down ( struct net_device *netdev );
|
extern void netdev_link_down ( struct net_device *netdev );
|
||||||
|
extern void netdev_link_block ( struct net_device *netdev,
|
||||||
|
unsigned long timeout );
|
||||||
|
extern void netdev_link_unblock ( struct net_device *netdev );
|
||||||
extern int netdev_tx ( struct net_device *netdev, struct io_buffer *iobuf );
|
extern int netdev_tx ( struct net_device *netdev, struct io_buffer *iobuf );
|
||||||
extern void netdev_tx_defer ( struct net_device *netdev,
|
extern void netdev_tx_defer ( struct net_device *netdev,
|
||||||
struct io_buffer *iobuf );
|
struct io_buffer *iobuf );
|
||||||
|
|||||||
@@ -161,6 +161,9 @@ void netdev_rx_unfreeze ( struct net_device *netdev ) {
|
|||||||
*/
|
*/
|
||||||
void netdev_link_err ( struct net_device *netdev, int rc ) {
|
void netdev_link_err ( struct net_device *netdev, int rc ) {
|
||||||
|
|
||||||
|
/* Stop link block timer */
|
||||||
|
stop_timer ( &netdev->link_block );
|
||||||
|
|
||||||
/* Record link state */
|
/* Record link state */
|
||||||
netdev->link_rc = rc;
|
netdev->link_rc = rc;
|
||||||
if ( netdev->link_rc == 0 ) {
|
if ( netdev->link_rc == 0 ) {
|
||||||
@@ -190,6 +193,50 @@ void netdev_link_down ( struct net_device *netdev ) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mark network device link as being blocked
|
||||||
|
*
|
||||||
|
* @v netdev Network device
|
||||||
|
* @v timeout Timeout (in ticks)
|
||||||
|
*/
|
||||||
|
void netdev_link_block ( struct net_device *netdev, unsigned long timeout ) {
|
||||||
|
|
||||||
|
/* Start link block timer */
|
||||||
|
if ( ! netdev_link_blocked ( netdev ) ) {
|
||||||
|
DBGC ( netdev, "NETDEV %s link blocked for %ld ticks\n",
|
||||||
|
netdev->name, timeout );
|
||||||
|
}
|
||||||
|
start_timer_fixed ( &netdev->link_block, timeout );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mark network device link as being unblocked
|
||||||
|
*
|
||||||
|
* @v netdev Network device
|
||||||
|
*/
|
||||||
|
void netdev_link_unblock ( struct net_device *netdev ) {
|
||||||
|
|
||||||
|
/* Stop link block timer */
|
||||||
|
if ( netdev_link_blocked ( netdev ) )
|
||||||
|
DBGC ( netdev, "NETDEV %s link unblocked\n", netdev->name );
|
||||||
|
stop_timer ( &netdev->link_block );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle network device link block timer expiry
|
||||||
|
*
|
||||||
|
* @v timer Link block timer
|
||||||
|
* @v fail Failure indicator
|
||||||
|
*/
|
||||||
|
static void netdev_link_block_expired ( struct retry_timer *timer,
|
||||||
|
int fail __unused ) {
|
||||||
|
struct net_device *netdev =
|
||||||
|
container_of ( timer, struct net_device, link_block );
|
||||||
|
|
||||||
|
/* Assume link is no longer blocked */
|
||||||
|
DBGC ( netdev, "NETDEV %s link block expired\n", netdev->name );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Record network device statistic
|
* Record network device statistic
|
||||||
*
|
*
|
||||||
@@ -546,6 +593,7 @@ static void free_netdev ( struct refcnt *refcnt ) {
|
|||||||
struct net_device *netdev =
|
struct net_device *netdev =
|
||||||
container_of ( refcnt, struct net_device, refcnt );
|
container_of ( refcnt, struct net_device, refcnt );
|
||||||
|
|
||||||
|
stop_timer ( &netdev->link_block );
|
||||||
netdev_tx_flush ( netdev );
|
netdev_tx_flush ( netdev );
|
||||||
netdev_rx_flush ( netdev );
|
netdev_rx_flush ( netdev );
|
||||||
clear_settings ( netdev_settings ( netdev ) );
|
clear_settings ( netdev_settings ( netdev ) );
|
||||||
@@ -575,6 +623,8 @@ struct net_device * alloc_netdev ( size_t priv_len ) {
|
|||||||
if ( netdev ) {
|
if ( netdev ) {
|
||||||
ref_init ( &netdev->refcnt, free_netdev );
|
ref_init ( &netdev->refcnt, free_netdev );
|
||||||
netdev->link_rc = -EUNKNOWN_LINK_STATUS;
|
netdev->link_rc = -EUNKNOWN_LINK_STATUS;
|
||||||
|
timer_init ( &netdev->link_block, netdev_link_block_expired,
|
||||||
|
&netdev->refcnt );
|
||||||
INIT_LIST_HEAD ( &netdev->tx_queue );
|
INIT_LIST_HEAD ( &netdev->tx_queue );
|
||||||
INIT_LIST_HEAD ( &netdev->tx_deferred );
|
INIT_LIST_HEAD ( &netdev->tx_deferred );
|
||||||
INIT_LIST_HEAD ( &netdev->rx_queue );
|
INIT_LIST_HEAD ( &netdev->rx_queue );
|
||||||
|
|||||||
@@ -103,11 +103,12 @@ static void ifstat_errors ( struct net_device_stats *stats,
|
|||||||
*/
|
*/
|
||||||
void ifstat ( struct net_device *netdev ) {
|
void ifstat ( struct net_device *netdev ) {
|
||||||
printf ( "%s: %s using %s on %s (%s)\n"
|
printf ( "%s: %s using %s on %s (%s)\n"
|
||||||
" [Link:%s, TX:%d TXE:%d RX:%d RXE:%d]\n",
|
" [Link:%s%s, TX:%d TXE:%d RX:%d RXE:%d]\n",
|
||||||
netdev->name, netdev_addr ( netdev ),
|
netdev->name, netdev_addr ( netdev ),
|
||||||
netdev->dev->driver_name, netdev->dev->name,
|
netdev->dev->driver_name, netdev->dev->name,
|
||||||
( netdev_is_open ( netdev ) ? "open" : "closed" ),
|
( netdev_is_open ( netdev ) ? "open" : "closed" ),
|
||||||
( netdev_link_ok ( netdev ) ? "up" : "down" ),
|
( netdev_link_ok ( netdev ) ? "up" : "down" ),
|
||||||
|
( netdev_link_blocked ( netdev ) ? " (blocked)" : "" ),
|
||||||
netdev->tx_stats.good, netdev->tx_stats.bad,
|
netdev->tx_stats.good, netdev->tx_stats.bad,
|
||||||
netdev->rx_stats.good, netdev->rx_stats.bad );
|
netdev->rx_stats.good, netdev->rx_stats.bad );
|
||||||
if ( ! netdev_link_ok ( netdev ) ) {
|
if ( ! netdev_link_ok ( netdev ) ) {
|
||||||
|
|||||||
Reference in New Issue
Block a user