aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hv/connection.c
diff options
context:
space:
mode:
authorK. Y. Srinivasan <kys@microsoft.com>2012-12-01 09:46:38 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-01-17 14:34:36 -0500
commit610071c38463998d5a66388ff9956aaeb24b49a8 (patch)
treea5ddb26924b8c328e016bd8004a0b33dfbd78c59 /drivers/hv/connection.c
parent4fa152ce24724a4a6b2edd26ca2eb7757ff5b2b8 (diff)
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: K. Y. Srinivasan <kys@microsoft.com> Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/hv/connection.c')
-rw-r--r--drivers/hv/connection.c165
1 files changed, 111 insertions, 54 deletions
diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c
index d1019a770ad7..2b56a3f47b30 100644
--- a/drivers/hv/connection.c
+++ b/drivers/hv/connection.c
@@ -40,15 +40,111 @@ struct vmbus_connection vmbus_connection = {
40}; 40};
41 41
42/* 42/*
43 * VMBUS version is 32 bit entity broken up into
44 * two 16 bit quantities: major_number. minor_number.
45 *
46 * 0 . 13 (Windows Server 2008)
47 * 1 . 1 (Windows 7)
48 * 2 . 4 (Windows 8)
49 */
50
51#define VERSION_WS2008 ((0 << 16) | (13))
52#define VERSION_WIN7 ((1 << 16) | (1))
53#define VERSION_WIN8 ((2 << 16) | (4))
54
55#define VERSION_INVAL -1
56
57static __u32 vmbus_get_next_version(__u32 current_version)
58{
59 switch (current_version) {
60 case (VERSION_WIN7):
61 return VERSION_WS2008;
62
63 case (VERSION_WIN8):
64 return VERSION_WIN7;
65
66 case (VERSION_WS2008):
67 default:
68 return VERSION_INVAL;
69 }
70}
71
72static int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo,
73 __u32 version)
74{
75 int ret = 0;
76 struct vmbus_channel_initiate_contact *msg;
77 unsigned long flags;
78 int t;
79
80 init_completion(&msginfo->waitevent);
81
82 msg = (struct vmbus_channel_initiate_contact *)msginfo->msg;
83
84 msg->header.msgtype = CHANNELMSG_INITIATE_CONTACT;
85 msg->vmbus_version_requested = version;
86 msg->interrupt_page = virt_to_phys(vmbus_connection.int_page);
87 msg->monitor_page1 = virt_to_phys(vmbus_connection.monitor_pages);
88 msg->monitor_page2 = virt_to_phys(
89 (void *)((unsigned long)vmbus_connection.monitor_pages +
90 PAGE_SIZE));
91
92 /*
93 * Add to list before we send the request since we may
94 * receive the response before returning from this routine
95 */
96 spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
97 list_add_tail(&msginfo->msglistentry,
98 &vmbus_connection.chn_msg_list);
99
100 spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
101
102 ret = vmbus_post_msg(msg,
103 sizeof(struct vmbus_channel_initiate_contact));
104 if (ret != 0) {
105 spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
106 list_del(&msginfo->msglistentry);
107 spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock,
108 flags);
109 return ret;
110 }
111
112 /* Wait for the connection response */
113 t = wait_for_completion_timeout(&msginfo->waitevent, 5*HZ);
114 if (t == 0) {
115 spin_lock_irqsave(&vmbus_connection.channelmsg_lock,
116 flags);
117 list_del(&msginfo->msglistentry);
118 spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock,
119 flags);
120 return -ETIMEDOUT;
121 }
122
123 spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
124 list_del(&msginfo->msglistentry);
125 spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
126
127 /* Check if successful */
128 if (msginfo->response.version_response.version_supported) {
129 vmbus_connection.conn_state = CONNECTED;
130 } else {
131 pr_err("Unable to connect, "
132 "Version %d not supported by Hyper-V\n",
133 version);
134 return -ECONNREFUSED;
135 }
136
137 return ret;
138}
139
140/*
43 * vmbus_connect - Sends a connect request on the partition service connection 141 * vmbus_connect - Sends a connect request on the partition service connection
44 */ 142 */
45int vmbus_connect(void) 143int vmbus_connect(void)
46{ 144{
47 int ret = 0; 145 int ret = 0;
48 int t;
49 struct vmbus_channel_msginfo *msginfo = NULL; 146 struct vmbus_channel_msginfo *msginfo = NULL;
50 struct vmbus_channel_initiate_contact *msg; 147 __u32 version;
51 unsigned long flags;
52 148
53 /* Initialize the vmbus connection */ 149 /* Initialize the vmbus connection */
54 vmbus_connection.conn_state = CONNECTING; 150 vmbus_connection.conn_state = CONNECTING;
@@ -99,64 +195,25 @@ int vmbus_connect(void)
99 goto cleanup; 195 goto cleanup;
100 } 196 }
101 197
102 init_completion(&msginfo->waitevent);
103
104 msg = (struct vmbus_channel_initiate_contact *)msginfo->msg;
105
106 msg->header.msgtype = CHANNELMSG_INITIATE_CONTACT;
107 msg->vmbus_version_requested = VMBUS_REVISION_NUMBER;
108 msg->interrupt_page = virt_to_phys(vmbus_connection.int_page);
109 msg->monitor_page1 = virt_to_phys(vmbus_connection.monitor_pages);
110 msg->monitor_page2 = virt_to_phys(
111 (void *)((unsigned long)vmbus_connection.monitor_pages +
112 PAGE_SIZE));
113
114 /* 198 /*
115 * Add to list before we send the request since we may 199 * Negotiate a compatible VMBUS version number with the
116 * receive the response before returning from this routine 200 * host. We start with the highest number we can support
201 * and work our way down until we negotiate a compatible
202 * version.
117 */ 203 */
118 spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
119 list_add_tail(&msginfo->msglistentry,
120 &vmbus_connection.chn_msg_list);
121
122 spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
123 204
124 ret = vmbus_post_msg(msg, 205 version = VERSION_WS2008;
125 sizeof(struct vmbus_channel_initiate_contact));
126 if (ret != 0) {
127 spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
128 list_del(&msginfo->msglistentry);
129 spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock,
130 flags);
131 goto cleanup;
132 }
133 206
134 /* Wait for the connection response */ 207 do {
135 t = wait_for_completion_timeout(&msginfo->waitevent, 5*HZ); 208 ret = vmbus_negotiate_version(msginfo, version);
136 if (t == 0) { 209 if (ret == 0)
137 spin_lock_irqsave(&vmbus_connection.channelmsg_lock, 210 break;
138 flags);
139 list_del(&msginfo->msglistentry);
140 spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock,
141 flags);
142 ret = -ETIMEDOUT;
143 goto cleanup;
144 }
145 211
146 spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); 212 version = vmbus_get_next_version(version);
147 list_del(&msginfo->msglistentry); 213 } while (version != VERSION_INVAL);
148 spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
149 214
150 /* Check if successful */ 215 if (version == VERSION_INVAL)
151 if (msginfo->response.version_response.version_supported) {
152 vmbus_connection.conn_state = CONNECTED;
153 } else {
154 pr_err("Unable to connect, "
155 "Version %d not supported by Hyper-V\n",
156 VMBUS_REVISION_NUMBER);
157 ret = -ECONNREFUSED;
158 goto cleanup; 216 goto cleanup;
159 }
160 217
161 kfree(msginfo); 218 kfree(msginfo);
162 return 0; 219 return 0;