diff options
Diffstat (limited to 'src/interface')
-rw-r--r-- | src/interface/hyperv/vmbus.c | 133 |
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 |