]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[xen] Cope with unexpected initial backend states
authorMichael Brown <mcb30@ipxe.org>
Wed, 13 Aug 2014 23:03:43 +0000 (00:03 +0100)
committerMichael Brown <mcb30@ipxe.org>
Wed, 13 Aug 2014 23:14:51 +0000 (00:14 +0100)
Under some circumstances (e.g. if iPXE itself is booted via iSCSI, or
after an unclean reboot), the backend may not be in the expected
InitWait state when iPXE starts up.

There is no generic reset mechanism for Xenbus devices.  Recent
versions of xen-netback will gracefully perform all of the required
steps if the frontend sets its state to Initialising.  Older versions
(such as that found in XenServer 6.2.0) require the frontend to
transition through Closed before reaching Initialising.

Add a reset mechanism for netfront devices which does the following:

 - read current backend state

 - if backend state is anything other than InitWait, then set the
   frontend state to Closed and wait for the backend to also reach
   Closed

 - set the frontend state to Initialising and wait for the backend to
   reach InitWait.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/drivers/net/netfront.c
src/include/ipxe/xenbus.h
src/interface/xen/xenbus.c

index ef04f5c5c7833bb91ce845bdaaa94fb4249f8732..4b816329edb5cbaff34aff701ba6c901de1e9143 100644 (file)
@@ -62,6 +62,55 @@ FILE_LICENCE ( GPL2_OR_LATER );
  ******************************************************************************
  */
 
+/**
+ * Reset device
+ *
+ * @v netfront         Netfront device
+ * @ret rc             Return status code
+ */
+static int netfront_reset ( struct netfront_nic *netfront ) {
+       struct xen_device *xendev = netfront->xendev;
+       int state;
+       int rc;
+
+       /* Get current backend state */
+       if ( ( state = xenbus_backend_state ( xendev ) ) < 0 ) {
+               rc = state;
+               DBGC ( netfront, "NETFRONT %s could not read backend state: "
+                      "%s\n", xendev->key, strerror ( rc ) );
+               return rc;
+       }
+
+       /* If the backend is not already in InitWait, then mark
+        * frontend as Closed to shut down the backend.
+        */
+       if ( state != XenbusStateInitWait ) {
+
+               /* Set state to Closed */
+               xenbus_set_state ( xendev, XenbusStateClosed );
+
+               /* Wait for backend to reach Closed */
+               if ( ( rc = xenbus_backend_wait ( xendev,
+                                                 XenbusStateClosed ) ) != 0 ) {
+                       DBGC ( netfront, "NETFRONT %s backend did not reach "
+                              "Closed: %s\n", xendev->key, strerror ( rc ) );
+                       return rc;
+               }
+       }
+
+       /* Reset state to Initialising */
+       xenbus_set_state ( xendev, XenbusStateInitialising );
+
+       /* Wait for backend to reach InitWait */
+       if ( ( rc = xenbus_backend_wait ( xendev, XenbusStateInitWait ) ) != 0){
+               DBGC ( netfront, "NETFRONT %s backend did not reach InitWait: "
+                      "%s\n", xendev->key, strerror ( rc ) );
+               return rc;
+       }
+
+       return 0;
+}
+
 /**
  * Fetch MAC address
  *
@@ -510,6 +559,10 @@ static int netfront_open ( struct net_device *netdev ) {
        struct xen_device *xendev = netfront->xendev;
        int rc;
 
+       /* Ensure device is in a suitable initial state */
+       if ( ( rc = netfront_reset ( netfront ) ) != 0 )
+               goto err_reset;
+
        /* Create transmit descriptor ring */
        if ( ( rc = netfront_create_ring ( netfront, &netfront->tx ) ) != 0 )
                goto err_create_tx;
@@ -563,7 +616,7 @@ static int netfront_open ( struct net_device *netdev ) {
        return 0;
 
  err_backend_wait:
-       xenbus_set_state ( xendev, XenbusStateInitialising );
+       netfront_reset ( netfront );
  err_set_state:
        netfront_rm ( netfront, "feature-no-csum-offload" );
  err_feature_no_csum_offload:
@@ -575,6 +628,7 @@ static int netfront_open ( struct net_device *netdev ) {
  err_create_rx:
        netfront_destroy_ring ( netfront, &netfront->tx, NULL );
  err_create_tx:
+ err_reset:
        return rc;
 }
 
@@ -588,13 +642,10 @@ static void netfront_close ( struct net_device *netdev ) {
        struct xen_device *xendev = netfront->xendev;
        int rc;
 
-       /* Set state to Closed */
-       xenbus_set_state ( xendev, XenbusStateClosed );
-
-       /* Wait for backend to close, thereby ensuring that grant
-        * references are no longer in use, etc.
+       /* Reset devic, thereby ensuring that grant references are no
+        * longer in use, etc.
         */
-       if ( ( rc = xenbus_backend_wait ( xendev, XenbusStateClosed ) ) != 0 ) {
+       if ( ( rc = netfront_reset ( netfront ) ) != 0 ) {
                DBGC ( netfront, "NETFRONT %s could not disconnect from "
                       "backend: %s\n", xendev->key, strerror ( rc ) );
                /* Things will probably go _very_ badly wrong if this
@@ -609,9 +660,6 @@ static void netfront_close ( struct net_device *netdev ) {
                netdev_link_down ( netdev );
        }
 
-       /* Reset state to Initialising */
-       xenbus_set_state ( xendev, XenbusStateInitialising );
-
        /* Delete flags */
        netfront_rm ( netfront, "feature-no-csum-offload" );
        netfront_rm ( netfront, "request-rx-copy" );
@@ -835,6 +883,12 @@ static int netfront_probe ( struct xen_device *xendev ) {
        if ( ( rc = netfront_read_mac ( netfront, netdev->hw_addr ) ) != 0 )
                goto err_read_mac;
 
+       /* Reset device.  Ignore failures; allow the device to be
+        * registered so that reset errors can be observed by the user
+        * when attempting to open the device.
+        */
+       netfront_reset ( netfront );
+
        /* Register network device */
        if ( ( rc = register_netdev ( netdev ) ) != 0 )
                goto err_register_netdev;
index 2777226fd5d8f8a0e2c153d0e416ca60eb8e4ba2..ef2b5496f8f09c01a2ee256d29b395177ec14db5 100644 (file)
@@ -78,6 +78,7 @@ static inline void * xen_get_drvdata ( struct xen_device *xendev ) {
 }
 
 extern int xenbus_set_state ( struct xen_device *xendev, int state );
+extern int xenbus_backend_state ( struct xen_device *xendev );
 extern int xenbus_backend_wait ( struct xen_device *xendev, int state );
 extern int xenbus_probe ( struct xen_hypervisor *xen, struct device *parent );
 extern void xenbus_remove ( struct xen_hypervisor *xen, struct device *parent );
index 7ac418993dfefff0d57768efdfc959b23322bb31..ffc8aba3e804836c46a0f97897d5a18aaa2e584f 100644 (file)
@@ -118,7 +118,7 @@ int xenbus_set_state ( struct xen_device *xendev, int state ) {
  * @v xendev           Xen device
  * @ret state          Backend state, or negative error
  */
-static int xenbus_backend_state ( struct xen_device *xendev ) {
+int xenbus_backend_state ( struct xen_device *xendev ) {
        unsigned long state;
        int rc;