diff options
Diffstat (limited to 'drivers/hv/connection.c')
| -rw-r--r-- | drivers/hv/connection.c | 87 | 
1 files changed, 45 insertions, 42 deletions
diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c index 6e4c015783ff..74e77de89b4f 100644 --- a/drivers/hv/connection.c +++ b/drivers/hv/connection.c @@ -14,6 +14,7 @@  #include <linux/wait.h>  #include <linux/delay.h>  #include <linux/mm.h> +#include <linux/module.h>  #include <linux/slab.h>  #include <linux/vmalloc.h>  #include <linux/hyperv.h> @@ -40,29 +41,30 @@ EXPORT_SYMBOL_GPL(vmbus_connection);  __u32 vmbus_proto_version;  EXPORT_SYMBOL_GPL(vmbus_proto_version); -static __u32 vmbus_get_next_version(__u32 current_version) -{ -	switch (current_version) { -	case (VERSION_WIN7): -		return VERSION_WS2008; - -	case (VERSION_WIN8): -		return VERSION_WIN7; - -	case (VERSION_WIN8_1): -		return VERSION_WIN8; +/* + * Table of VMBus versions listed from newest to oldest. + */ +static __u32 vmbus_versions[] = { +	VERSION_WIN10_V5_2, +	VERSION_WIN10_V5_1, +	VERSION_WIN10_V5, +	VERSION_WIN10_V4_1, +	VERSION_WIN10, +	VERSION_WIN8_1, +	VERSION_WIN8, +	VERSION_WIN7, +	VERSION_WS2008 +}; -	case (VERSION_WIN10): -		return VERSION_WIN8_1; +/* + * Maximal VMBus protocol version guests can negotiate.  Useful to cap the + * VMBus version for testing and debugging purpose. + */ +static uint max_version = VERSION_WIN10_V5_2; -	case (VERSION_WIN10_V5): -		return VERSION_WIN10; - -	case (VERSION_WS2008): -	default: -		return VERSION_INVAL; -	} -} +module_param(max_version, uint, S_IRUGO); +MODULE_PARM_DESC(max_version, +		 "Maximal VMBus protocol version which can be negotiated");  int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version)  { @@ -80,12 +82,12 @@ int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version)  	msg->vmbus_version_requested = version;  	/* -	 * VMBus protocol 5.0 (VERSION_WIN10_V5) requires that we must use -	 * VMBUS_MESSAGE_CONNECTION_ID_4 for the Initiate Contact Message, +	 * VMBus protocol 5.0 (VERSION_WIN10_V5) and higher require that we must +	 * use VMBUS_MESSAGE_CONNECTION_ID_4 for the Initiate Contact Message,  	 * and for subsequent messages, we must use the Message Connection ID  	 * field in the host-returned Version Response Message. And, with -	 * VERSION_WIN10_V5, we don't use msg->interrupt_page, but we tell -	 * the host explicitly that we still use VMBUS_MESSAGE_SINT(2) for +	 * VERSION_WIN10_V5 and higher, we don't use msg->interrupt_page, but we +	 * tell the host explicitly that we still use VMBUS_MESSAGE_SINT(2) for  	 * compatibility.  	 *  	 * On old hosts, we should always use VMBUS_MESSAGE_CONNECTION_ID (1). @@ -169,8 +171,8 @@ int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version)   */  int vmbus_connect(void)  { -	int ret = 0;  	struct vmbus_channel_msginfo *msginfo = NULL; +	int i, ret = 0;  	__u32 version;  	/* Initialize the vmbus connection */ @@ -206,7 +208,7 @@ int vmbus_connect(void)  	 * abstraction stuff  	 */  	vmbus_connection.int_page = -	(void *)__get_free_pages(GFP_KERNEL|__GFP_ZERO, 0); +	(void *)hv_alloc_hyperv_zeroed_page();  	if (vmbus_connection.int_page == NULL) {  		ret = -ENOMEM;  		goto cleanup; @@ -215,14 +217,14 @@ int vmbus_connect(void)  	vmbus_connection.recv_int_page = vmbus_connection.int_page;  	vmbus_connection.send_int_page =  		(void *)((unsigned long)vmbus_connection.int_page + -			(PAGE_SIZE >> 1)); +			(HV_HYP_PAGE_SIZE >> 1));  	/*  	 * Setup the monitor notification facility. The 1st page for  	 * parent->child and the 2nd page for child->parent  	 */ -	vmbus_connection.monitor_pages[0] = (void *)__get_free_pages((GFP_KERNEL|__GFP_ZERO), 0); -	vmbus_connection.monitor_pages[1] = (void *)__get_free_pages((GFP_KERNEL|__GFP_ZERO), 0); +	vmbus_connection.monitor_pages[0] = (void *)hv_alloc_hyperv_zeroed_page(); +	vmbus_connection.monitor_pages[1] = (void *)hv_alloc_hyperv_zeroed_page();  	if ((vmbus_connection.monitor_pages[0] == NULL) ||  	    (vmbus_connection.monitor_pages[1] == NULL)) {  		ret = -ENOMEM; @@ -244,21 +246,21 @@ int vmbus_connect(void)  	 * version.  	 */ -	version = VERSION_CURRENT; +	for (i = 0; ; i++) { +		if (i == ARRAY_SIZE(vmbus_versions)) +			goto cleanup; + +		version = vmbus_versions[i]; +		if (version > max_version) +			continue; -	do {  		ret = vmbus_negotiate_version(msginfo, version);  		if (ret == -ETIMEDOUT)  			goto cleanup;  		if (vmbus_connection.conn_state == CONNECTED)  			break; - -		version = vmbus_get_next_version(version); -	} while (version != VERSION_INVAL); - -	if (version == VERSION_INVAL) -		goto cleanup; +	}  	vmbus_proto_version = version;  	pr_info("Vmbus version:%d.%d\n", @@ -295,12 +297,12 @@ void vmbus_disconnect(void)  		destroy_workqueue(vmbus_connection.work_queue);  	if (vmbus_connection.int_page) { -		free_pages((unsigned long)vmbus_connection.int_page, 0); +		hv_free_hyperv_page((unsigned long)vmbus_connection.int_page);  		vmbus_connection.int_page = NULL;  	} -	free_pages((unsigned long)vmbus_connection.monitor_pages[0], 0); -	free_pages((unsigned long)vmbus_connection.monitor_pages[1], 0); +	hv_free_hyperv_page((unsigned long)vmbus_connection.monitor_pages[0]); +	hv_free_hyperv_page((unsigned long)vmbus_connection.monitor_pages[1]);  	vmbus_connection.monitor_pages[0] = NULL;  	vmbus_connection.monitor_pages[1] = NULL;  } @@ -361,6 +363,7 @@ void vmbus_on_event(unsigned long data)  	trace_vmbus_on_event(channel); +	hv_debug_delay_test(channel, INTERRUPT_DELAY);  	do {  		void (*callback_fn)(void *); @@ -413,7 +416,7 @@ int vmbus_post_msg(void *buffer, size_t buflen, bool can_sleep)  		case HV_STATUS_INVALID_CONNECTION_ID:  			/*  			 * See vmbus_negotiate_version(): VMBus protocol 5.0 -			 * requires that we must use +			 * and higher require that we must use  			 * VMBUS_MESSAGE_CONNECTION_ID_4 for the Initiate  			 * Contact message, but on old hosts that only  			 * support VMBus protocol 4.0 or lower, here we get  |