summaryrefslogtreecommitdiffstats
path: root/src/drivers
diff options
context:
space:
mode:
authorMichael Brown2017-04-25 15:13:22 +0200
committerMichael Brown2017-04-28 17:20:47 +0200
commitb91cc983da48b2791a672431551f7859e33126ec (patch)
tree466bc46ef18d86a619769a49acf4c149193237b1 /src/drivers
parent[hyperv] Remove redundant return status code from mapping functions (diff)
downloadipxe-b91cc983da48b2791a672431551f7859e33126ec.tar.gz
ipxe-b91cc983da48b2791a672431551f7859e33126ec.tar.xz
ipxe-b91cc983da48b2791a672431551f7859e33126ec.zip
[hyperv] Cope with Windows Server 2016 enlightenments
An "enlightened" external bootloader (such as Windows Server 2016's winload.exe) may take ownership of the Hyper-V connection before all INT 13 operations have been completed. When this happens, all VMBus devices are implicitly closed and we are left with a non-functional network connection. Detect when our Hyper-V connection has been lost (by checking the SynIC message page MSR). Reclaim ownership of the Hyper-V connection and reestablish any VMBus devices, without disrupting any existing iPXE state (such as IPv4 settings attached to the network device). Windows Server 2016 will not cleanly take ownership of an active Hyper-V connection. Experimentation shows that we can quiesce by resetting only the SynIC message page MSR; this results in a successful SAN boot (on a Windows 2012 R2 physical host). Choose to quiesce by resetting (almost) all MSRs, in the hope that this will be more robust against corner cases such as a stray synthetic interrupt occurring during the handover. Signed-off-by: Michael Brown <mcb30@ipxe.org>
Diffstat (limited to 'src/drivers')
-rw-r--r--src/drivers/net/netvsc.c47
-rw-r--r--src/drivers/net/netvsc.h15
2 files changed, 62 insertions, 0 deletions
diff --git a/src/drivers/net/netvsc.c b/src/drivers/net/netvsc.c
index d269cd63..5be52fb8 100644
--- a/src/drivers/net/netvsc.c
+++ b/src/drivers/net/netvsc.c
@@ -259,6 +259,15 @@ static int netvsc_revoke_buffer ( struct netvsc_device *netvsc,
struct netvsc_revoke_buffer_message msg;
int rc;
+ /* If the buffer's GPADL is obsolete (i.e. was created before
+ * the most recent Hyper-V reset), then we will never receive
+ * a response to the revoke message. Since the GPADL is
+ * already destroyed as far as the hypervisor is concerned, no
+ * further action is required.
+ */
+ if ( netvsc_is_obsolete ( netvsc ) )
+ return 0;
+
/* Construct message */
memset ( &msg, 0, sizeof ( msg ) );
msg.header.type = cpu_to_le32 ( buffer->revoke_type );
@@ -474,6 +483,14 @@ static int netvsc_transmit ( struct rndis_device *rndis,
uint64_t xid;
int rc;
+ /* If the device is obsolete (i.e. was opened before the most
+ * recent Hyper-V reset), then we will never receive transmit
+ * completions. Fail transmissions immediately to minimise
+ * the delay in closing and reopening the device.
+ */
+ if ( netvsc_is_obsolete ( netvsc ) )
+ return -EPIPE;
+
/* Sanity check */
assert ( iob_len ( iobuf ) >= sizeof ( *header ) );
assert ( iob_len ( iobuf ) == le32_to_cpu ( header->len ) );
@@ -824,6 +841,35 @@ static int netvsc_probe ( struct vmbus_device *vmdev ) {
}
/**
+ * Reset device
+ *
+ * @v vmdev VMBus device
+ * @ret rc Return status code
+ */
+static int netvsc_reset ( struct vmbus_device *vmdev ) {
+ struct rndis_device *rndis = vmbus_get_drvdata ( vmdev );
+ struct netvsc_device *netvsc = rndis->priv;
+ struct net_device *netdev = rndis->netdev;
+ int rc;
+
+ /* A closed device holds no NetVSC (or RNDIS) state, so there
+ * is nothing to reset.
+ */
+ if ( ! netdev_is_open ( netdev ) )
+ return 0;
+
+ /* Close and reopen device to reset any stale state */
+ netdev_close ( netdev );
+ if ( ( rc = netdev_open ( netdev ) ) != 0 ) {
+ DBGC ( netvsc, "NETVSC %s could not reopen: %s\n",
+ netvsc->name, strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
* Remove device
*
* @v vmdev VMBus device
@@ -844,5 +890,6 @@ struct vmbus_driver netvsc_driver __vmbus_driver = {
.type = VMBUS_TYPE ( 0xf8615163, 0xdf3e, 0x46c5, 0x913f,
0xf2, 0xd2, 0xf9, 0x65, 0xed, 0x0e ),
.probe = netvsc_probe,
+ .reset = netvsc_reset,
.remove = netvsc_remove,
};
diff --git a/src/drivers/net/netvsc.h b/src/drivers/net/netvsc.h
index 39eeb891..93192357 100644
--- a/src/drivers/net/netvsc.h
+++ b/src/drivers/net/netvsc.h
@@ -362,4 +362,19 @@ struct netvsc_device {
int wait_rc;
};
+/**
+ * Check if NetVSC device is obsolete
+ *
+ * @v netvsc NetVSC device
+ * @v is_obsolete NetVSC device is obsolete
+ *
+ * Check if NetVSC device is obsolete (i.e. was opened before the most
+ * recent Hyper-V reset).
+ */
+static inline __attribute__ (( always_inline )) int
+netvsc_is_obsolete ( struct netvsc_device *netvsc ) {
+
+ return vmbus_gpadl_is_obsolete ( netvsc->rx.gpadl );
+}
+
#endif /* _NETVSC_H */