Commit 610071c3 authored by K. Y. Srinivasan's avatar K. Y. Srinivasan Committed by Greg Kroah-Hartman

Drivers: hv: Support handling multiple VMBUS versions

The current code hard coded the vmbus version independent of the host
it was running on. Add code to dynamically negotiate the most appropriate
version.
Signed-off-by: default avatarK. Y. Srinivasan <kys@microsoft.com>
Reviewed-by: default avatarHaiyang Zhang <haiyangz@microsoft.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 4fa152ce
...@@ -39,16 +39,112 @@ struct vmbus_connection vmbus_connection = { ...@@ -39,16 +39,112 @@ struct vmbus_connection vmbus_connection = {
.next_gpadl_handle = ATOMIC_INIT(0xE1E10), .next_gpadl_handle = ATOMIC_INIT(0xE1E10),
}; };
/*
* VMBUS version is 32 bit entity broken up into
* two 16 bit quantities: major_number. minor_number.
*
* 0 . 13 (Windows Server 2008)
* 1 . 1 (Windows 7)
* 2 . 4 (Windows 8)
*/
#define VERSION_WS2008 ((0 << 16) | (13))
#define VERSION_WIN7 ((1 << 16) | (1))
#define VERSION_WIN8 ((2 << 16) | (4))
#define VERSION_INVAL -1
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_WS2008):
default:
return VERSION_INVAL;
}
}
static int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo,
__u32 version)
{
int ret = 0;
struct vmbus_channel_initiate_contact *msg;
unsigned long flags;
int t;
init_completion(&msginfo->waitevent);
msg = (struct vmbus_channel_initiate_contact *)msginfo->msg;
msg->header.msgtype = CHANNELMSG_INITIATE_CONTACT;
msg->vmbus_version_requested = version;
msg->interrupt_page = virt_to_phys(vmbus_connection.int_page);
msg->monitor_page1 = virt_to_phys(vmbus_connection.monitor_pages);
msg->monitor_page2 = virt_to_phys(
(void *)((unsigned long)vmbus_connection.monitor_pages +
PAGE_SIZE));
/*
* Add to list before we send the request since we may
* receive the response before returning from this routine
*/
spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
list_add_tail(&msginfo->msglistentry,
&vmbus_connection.chn_msg_list);
spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
ret = vmbus_post_msg(msg,
sizeof(struct vmbus_channel_initiate_contact));
if (ret != 0) {
spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
list_del(&msginfo->msglistentry);
spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock,
flags);
return ret;
}
/* Wait for the connection response */
t = wait_for_completion_timeout(&msginfo->waitevent, 5*HZ);
if (t == 0) {
spin_lock_irqsave(&vmbus_connection.channelmsg_lock,
flags);
list_del(&msginfo->msglistentry);
spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock,
flags);
return -ETIMEDOUT;
}
spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
list_del(&msginfo->msglistentry);
spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
/* Check if successful */
if (msginfo->response.version_response.version_supported) {
vmbus_connection.conn_state = CONNECTED;
} else {
pr_err("Unable to connect, "
"Version %d not supported by Hyper-V\n",
version);
return -ECONNREFUSED;
}
return ret;
}
/* /*
* vmbus_connect - Sends a connect request on the partition service connection * vmbus_connect - Sends a connect request on the partition service connection
*/ */
int vmbus_connect(void) int vmbus_connect(void)
{ {
int ret = 0; int ret = 0;
int t;
struct vmbus_channel_msginfo *msginfo = NULL; struct vmbus_channel_msginfo *msginfo = NULL;
struct vmbus_channel_initiate_contact *msg; __u32 version;
unsigned long flags;
/* Initialize the vmbus connection */ /* Initialize the vmbus connection */
vmbus_connection.conn_state = CONNECTING; vmbus_connection.conn_state = CONNECTING;
...@@ -99,64 +195,25 @@ int vmbus_connect(void) ...@@ -99,64 +195,25 @@ int vmbus_connect(void)
goto cleanup; goto cleanup;
} }
init_completion(&msginfo->waitevent);
msg = (struct vmbus_channel_initiate_contact *)msginfo->msg;
msg->header.msgtype = CHANNELMSG_INITIATE_CONTACT;
msg->vmbus_version_requested = VMBUS_REVISION_NUMBER;
msg->interrupt_page = virt_to_phys(vmbus_connection.int_page);
msg->monitor_page1 = virt_to_phys(vmbus_connection.monitor_pages);
msg->monitor_page2 = virt_to_phys(
(void *)((unsigned long)vmbus_connection.monitor_pages +
PAGE_SIZE));
/* /*
* Add to list before we send the request since we may * Negotiate a compatible VMBUS version number with the
* receive the response before returning from this routine * host. We start with the highest number we can support
* and work our way down until we negotiate a compatible
* version.
*/ */
spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
list_add_tail(&msginfo->msglistentry,
&vmbus_connection.chn_msg_list);
spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
ret = vmbus_post_msg(msg, version = VERSION_WS2008;
sizeof(struct vmbus_channel_initiate_contact));
if (ret != 0) {
spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
list_del(&msginfo->msglistentry);
spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock,
flags);
goto cleanup;
}
/* Wait for the connection response */ do {
t = wait_for_completion_timeout(&msginfo->waitevent, 5*HZ); ret = vmbus_negotiate_version(msginfo, version);
if (t == 0) { if (ret == 0)
spin_lock_irqsave(&vmbus_connection.channelmsg_lock, break;
flags);
list_del(&msginfo->msglistentry);
spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock,
flags);
ret = -ETIMEDOUT;
goto cleanup;
}
spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); version = vmbus_get_next_version(version);
list_del(&msginfo->msglistentry); } while (version != VERSION_INVAL);
spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
/* Check if successful */ if (version == VERSION_INVAL)
if (msginfo->response.version_response.version_supported) {
vmbus_connection.conn_state = CONNECTED;
} else {
pr_err("Unable to connect, "
"Version %d not supported by Hyper-V\n",
VMBUS_REVISION_NUMBER);
ret = -ECONNREFUSED;
goto cleanup; goto cleanup;
}
kfree(msginfo); kfree(msginfo);
return 0; return 0;
......
...@@ -406,12 +406,6 @@ hv_get_ringbuffer_availbytes(struct hv_ring_buffer_info *rbi, ...@@ -406,12 +406,6 @@ hv_get_ringbuffer_availbytes(struct hv_ring_buffer_info *rbi,
#define HV_DRV_VERSION "3.1" #define HV_DRV_VERSION "3.1"
/*
* A revision number of vmbus that is used for ensuring both ends on a
* partition are using compatible versions.
*/
#define VMBUS_REVISION_NUMBER 13
/* Make maximum size of pipe payload of 16K */ /* Make maximum size of pipe payload of 16K */
#define MAX_PIPE_DATA_PAYLOAD (sizeof(u8) * 16384) #define MAX_PIPE_DATA_PAYLOAD (sizeof(u8) * 16384)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment