]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[netdevice] Add a generic concept of a "blocked link"
authorMichael Brown <mcb30@ipxe.org>
Thu, 25 Jun 2015 15:41:39 +0000 (16:41 +0100)
committerMichael Brown <mcb30@ipxe.org>
Thu, 25 Jun 2015 15:46:47 +0000 (16:46 +0100)
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>
src/include/ipxe/netdevice.h
src/net/netdevice.c
src/usr/ifmgmt.c

index 8e615c941d40aeb4c57c625be8f62e8952b77107..f20a81f79c1196377aa31827621b043eb5e33720 100644 (file)
@@ -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.
@@ -613,6 +616,17 @@ netdev_link_ok ( struct net_device *netdev ) {
        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
  *
@@ -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 );
index 3c4b4cd07876c2c1e55ce1b59be32a122715dd22..f2821efe1ca2f7c08bf05adbd3ef711f51035bc6 100644 (file)
@@ -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 ) {
@@ -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
  *
@@ -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 );
index 3228a4874f79e101d176bb05071ab1ee283dedff..aefdaa45dbd30dd9d07bd19e52026f4f17f66c74 100644 (file)
@@ -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 ) ) {