diff options
author | Michael Brown <mcb30@ipxe.org> | 2015-06-25 16:41:39 +0100 |
---|---|---|
committer | Michael Brown <mcb30@ipxe.org> | 2015-06-25 16:46:47 +0100 |
commit | f3812395a261b80fe77d19ebb9045e790c434773 (patch) | |
tree | d68ece89cfa39a4976b5dad99d2cb466dee704ca | |
parent | 7e7870984b4e63eeaf8416289c11bc386a77d5ba (diff) | |
download | ipxe-f3812395a261b80fe77d19ebb9045e790c434773.tar.gz |
[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>
-rw-r--r-- | src/include/ipxe/netdevice.h | 17 | ||||
-rw-r--r-- | src/net/netdevice.c | 52 | ||||
-rw-r--r-- | src/usr/ifmgmt.c | 3 |
3 files changed, 70 insertions, 2 deletions
diff --git a/src/include/ipxe/netdevice.h b/src/include/ipxe/netdevice.h index 8e615c94..f20a81f7 100644 --- a/src/include/ipxe/netdevice.h +++ b/src/include/ipxe/netdevice.h @@ -15,6 +15,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/refcnt.h> #include <ipxe/settings.h> #include <ipxe/interface.h> +#include <ipxe/retry.h> struct io_buffer; struct net_device; @@ -392,6 +393,8 @@ struct net_device { * indicates the error preventing link-up. */ int link_rc; + /** Link block timer */ + struct retry_timer link_block; /** Maximum packet length * * This length includes any link-layer headers. @@ -614,6 +617,17 @@ netdev_link_ok ( struct net_device *netdev ) { } /** + * 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 * * @v netdev Network device @@ -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_link_err ( struct net_device *netdev, int rc ); 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 void netdev_tx_defer ( struct net_device *netdev, struct io_buffer *iobuf ); diff --git a/src/net/netdevice.c b/src/net/netdevice.c index 3c4b4cd0..f2821efe 100644 --- a/src/net/netdevice.c +++ b/src/net/netdevice.c @@ -161,6 +161,9 @@ void netdev_rx_unfreeze ( struct net_device *netdev ) { */ void netdev_link_err ( struct net_device *netdev, int rc ) { + /* Stop link block timer */ + stop_timer ( &netdev->link_block ); + /* Record link state */ netdev->link_rc = rc; if ( netdev->link_rc == 0 ) { @@ -191,6 +194,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 * * @v stats Network device statistics @@ -545,7 +592,8 @@ static struct interface_descriptor netdev_config_desc = static void free_netdev ( struct refcnt *refcnt ) { struct net_device *netdev = container_of ( refcnt, struct net_device, refcnt ); - + + stop_timer ( &netdev->link_block ); netdev_tx_flush ( netdev ); netdev_rx_flush ( netdev ); clear_settings ( netdev_settings ( netdev ) ); @@ -575,6 +623,8 @@ struct net_device * alloc_netdev ( size_t priv_len ) { if ( netdev ) { ref_init ( &netdev->refcnt, free_netdev ); 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_deferred ); INIT_LIST_HEAD ( &netdev->rx_queue ); diff --git a/src/usr/ifmgmt.c b/src/usr/ifmgmt.c index 3228a487..aefdaa45 100644 --- a/src/usr/ifmgmt.c +++ b/src/usr/ifmgmt.c @@ -103,11 +103,12 @@ static void ifstat_errors ( struct net_device_stats *stats, */ void ifstat ( struct net_device *netdev ) { 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->dev->driver_name, netdev->dev->name, ( netdev_is_open ( netdev ) ? "open" : "closed" ), ( netdev_link_ok ( netdev ) ? "up" : "down" ), + ( netdev_link_blocked ( netdev ) ? " (blocked)" : "" ), netdev->tx_stats.good, netdev->tx_stats.bad, netdev->rx_stats.good, netdev->rx_stats.bad ); if ( ! netdev_link_ok ( netdev ) ) { |