summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/include/ipxe/netdevice.h17
-rw-r--r--src/net/netdevice.c52
-rw-r--r--src/usr/ifmgmt.c3
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 ) ) {