summaryrefslogtreecommitdiffstats
path: root/src/interface
diff options
context:
space:
mode:
Diffstat (limited to 'src/interface')
-rw-r--r--src/interface/hyperv/vmbus.c133
1 files changed, 129 insertions, 4 deletions
diff --git a/src/interface/hyperv/vmbus.c b/src/interface/hyperv/vmbus.c
index 7915ddfe..45a7caec 100644
--- a/src/interface/hyperv/vmbus.c
+++ b/src/interface/hyperv/vmbus.c
@@ -50,6 +50,16 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*/
#define VMBUS_GPADL_MAGIC 0x18ae0000
+/** Current (i.e. most recently issued) GPADL ID */
+static unsigned int vmbus_gpadl = VMBUS_GPADL_MAGIC;
+
+/** Obsolete GPADL ID threshold
+ *
+ * When the Hyper-V connection is reset, any previous GPADLs are
+ * automatically rendered obsolete.
+ */
+unsigned int vmbus_obsolete_gpadl;
+
/**
* Post message
*
@@ -281,12 +291,12 @@ int vmbus_establish_gpadl ( struct vmbus_device *vmdev, userptr_t data,
uint64_t pfn[pfn_count];
} __attribute__ (( packed )) gpadlhdr;
const struct vmbus_gpadl_created *created = &vmbus->message->created;
- static unsigned int gpadl = VMBUS_GPADL_MAGIC;
+ unsigned int gpadl;
unsigned int i;
int rc;
/* Allocate GPADL ID */
- gpadl++;
+ gpadl = ++vmbus_gpadl;
/* Construct message */
memset ( &gpadlhdr, 0, sizeof ( gpadlhdr ) );
@@ -347,6 +357,15 @@ int vmbus_gpadl_teardown ( struct vmbus_device *vmdev, unsigned int gpadl ) {
const struct vmbus_gpadl_torndown *torndown = &vmbus->message->torndown;
int rc;
+ /* If GPADL is obsolete (i.e. was created before the most
+ * recent Hyper-V reset), then we will never receive a
+ * response to the teardown message. Since the GPADL is
+ * already destroyed as far as the hypervisor is concerned, no
+ * further action is required.
+ */
+ if ( vmbus_gpadl_is_obsolete ( gpadl ) )
+ return 0;
+
/* Construct message */
memset ( &teardown, 0, sizeof ( teardown ) );
teardown.header.type = cpu_to_le32 ( VMBUS_GPADL_TEARDOWN );
@@ -530,8 +549,7 @@ void vmbus_close ( struct vmbus_device *vmdev ) {
}
/* Tear down GPADL */
- if ( ( rc = vmbus_gpadl_teardown ( vmdev,
- vmdev->gpadl ) ) != 0 ) {
+ if ( ( rc = vmbus_gpadl_teardown ( vmdev, vmdev->gpadl ) ) != 0 ) {
DBGC ( vmdev, "VMBUS %s failed to tear down channel GPADL: "
"%s\n", vmdev->dev.name, strerror ( rc ) );
/* We can't prevent the remote VM from continuing to
@@ -1187,6 +1205,8 @@ static int vmbus_probe_channels ( struct hv_hypervisor *hv,
&parent->children );
vmdev->dev.parent = parent;
vmdev->hv = hv;
+ memcpy ( &vmdev->instance, &offer->instance,
+ sizeof ( vmdev->instance ) );
vmdev->channel = channel;
vmdev->monitor = offer->monitor;
vmdev->signal = ( offer->monitored ?
@@ -1201,6 +1221,7 @@ static int vmbus_probe_channels ( struct hv_hypervisor *hv,
} else if ( header->type ==
cpu_to_le32 ( VMBUS_ALL_OFFERS_DELIVERED ) ) {
+ /* End of offer list */
break;
} else {
@@ -1244,6 +1265,77 @@ static int vmbus_probe_channels ( struct hv_hypervisor *hv,
return rc;
}
+
+/**
+ * Reset channels
+ *
+ * @v hv Hyper-V hypervisor
+ * @v parent Parent device
+ * @ret rc Return status code
+ */
+static int vmbus_reset_channels ( struct hv_hypervisor *hv,
+ struct device *parent ) {
+ struct vmbus *vmbus = hv->vmbus;
+ const struct vmbus_message_header *header = &vmbus->message->header;
+ const struct vmbus_offer_channel *offer = &vmbus->message->offer;
+ const union uuid *type;
+ struct vmbus_device *vmdev;
+ unsigned int channel;
+ int rc;
+
+ /* Post message */
+ if ( ( rc = vmbus_post_empty_message ( hv, VMBUS_REQUEST_OFFERS ) ) !=0)
+ return rc;
+
+ /* Collect responses */
+ while ( 1 ) {
+
+ /* Wait for response */
+ if ( ( rc = vmbus_wait_for_any_message ( hv ) ) != 0 )
+ return rc;
+
+ /* Handle response */
+ if ( header->type == cpu_to_le32 ( VMBUS_OFFER_CHANNEL ) ) {
+
+ /* Parse offer */
+ type = &offer->type;
+ channel = le32_to_cpu ( offer->channel );
+ DBGC2 ( vmbus, "VMBUS %p offer %d type %s",
+ vmbus, channel, uuid_ntoa ( type ) );
+ if ( offer->monitored )
+ DBGC2 ( vmbus, " monitor %d", offer->monitor );
+ DBGC2 ( vmbus, "\n" );
+
+ /* Do nothing with the offer; we already have all
+ * of the relevant state from the initial probe.
+ */
+
+ } else if ( header->type ==
+ cpu_to_le32 ( VMBUS_ALL_OFFERS_DELIVERED ) ) {
+
+ /* End of offer list */
+ break;
+
+ } else {
+ DBGC ( vmbus, "VMBUS %p unexpected offer response type "
+ "%d\n", vmbus, le32_to_cpu ( header->type ) );
+ return -EPROTO;
+ }
+ }
+
+ /* Reset all devices */
+ list_for_each_entry ( vmdev, &parent->children, dev.siblings ) {
+ if ( ( rc = vmdev->driver->reset ( vmdev ) ) != 0 ) {
+ DBGC ( vmdev, "VMBUS %s could not reset: %s\n",
+ vmdev->dev.name, strerror ( rc ) );
+ /* Continue attempting to reset other devices */
+ continue;
+ }
+ }
+
+ return 0;
+}
+
/**
* Remove channels
*
@@ -1331,6 +1423,39 @@ int vmbus_probe ( struct hv_hypervisor *hv, struct device *parent ) {
}
/**
+ * Reset Hyper-V virtual machine bus
+ *
+ * @v hv Hyper-V hypervisor
+ * @v parent Parent device
+ * @ret rc Return status code
+ */
+int vmbus_reset ( struct hv_hypervisor *hv, struct device *parent ) {
+ struct vmbus *vmbus = hv->vmbus;
+ int rc;
+
+ /* Mark all existent GPADLs as obsolete */
+ vmbus_obsolete_gpadl = vmbus_gpadl;
+
+ /* Clear interrupt and monitor pages */
+ memset ( vmbus->intr, 0, PAGE_SIZE );
+ memset ( vmbus->monitor_in, 0, PAGE_SIZE );
+ memset ( vmbus->monitor_out, 0, PAGE_SIZE );
+
+ /* Enable message interrupt */
+ hv_enable_sint ( hv, VMBUS_MESSAGE_SINT );
+
+ /* Renegotiate protocol version */
+ if ( ( rc = vmbus_negotiate_version ( hv ) ) != 0 )
+ return rc;
+
+ /* Reenumerate channels */
+ if ( ( rc = vmbus_reset_channels ( hv, parent ) ) != 0 )
+ return rc;
+
+ return 0;
+}
+
+/**
* Remove Hyper-V virtual machine bus
*
* @v hv Hyper-V hypervisor