Deleted Added
sdiff udiff text old ( 295309 ) new ( 295964 )
full compact
1/*-
2 * Copyright (c) 2009-2012 Microsoft Corp.
3 * Copyright (c) 2012 NetApp Inc.
4 * Copyright (c) 2012 Citrix Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice unmodified, this list of conditions, and the following
12 * disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include <sys/param.h>
30#include <sys/malloc.h>
31#include <sys/systm.h>
32#include <sys/lock.h>
33#include <sys/mutex.h>
34#include <machine/bus.h>
35#include <vm/vm.h>
36#include <vm/vm_param.h>
37#include <vm/pmap.h>
38
39#include "hv_vmbus_priv.h"
40
41/*
42 * Globals
43 */
44hv_vmbus_connection hv_vmbus_g_connection =
45 { .connect_state = HV_DISCONNECTED,
46 .next_gpadl_handle = 0xE1E10, };
47
48uint32_t hv_vmbus_protocal_version = HV_VMBUS_VERSION_WS2008;
49
50static uint32_t
51hv_vmbus_get_next_version(uint32_t current_ver)
52{
53 switch (current_ver) {
54 case (HV_VMBUS_VERSION_WIN7):
55 return(HV_VMBUS_VERSION_WS2008);
56
57 case (HV_VMBUS_VERSION_WIN8):
58 return(HV_VMBUS_VERSION_WIN7);
59
60 case (HV_VMBUS_VERSION_WIN8_1):
61 return(HV_VMBUS_VERSION_WIN8);
62
63 case (HV_VMBUS_VERSION_WS2008):
64 default:
65 return(HV_VMBUS_VERSION_INVALID);
66 }
67}
68
69/**
70 * Negotiate the highest supported hypervisor version.
71 */
72static int
73hv_vmbus_negotiate_version(hv_vmbus_channel_msg_info *msg_info,
74 uint32_t version)
75{
76 int ret = 0;
77 hv_vmbus_channel_initiate_contact *msg;
78
79 sema_init(&msg_info->wait_sema, 0, "Msg Info Sema");
80 msg = (hv_vmbus_channel_initiate_contact*) msg_info->msg;
81
82 msg->header.message_type = HV_CHANNEL_MESSAGE_INITIATED_CONTACT;
83 msg->vmbus_version_requested = version;
84
85 msg->interrupt_page = hv_get_phys_addr(
86 hv_vmbus_g_connection.interrupt_page);
87
88 msg->monitor_page_1 = hv_get_phys_addr(
89 hv_vmbus_g_connection.monitor_page_1);
90
91 msg->monitor_page_2 = hv_get_phys_addr(
92 hv_vmbus_g_connection.monitor_page_2);
93
94 /**
95 * Add to list before we send the request since we may receive the
96 * response before returning from this routine
97 */
98 mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
99
100 TAILQ_INSERT_TAIL(
101 &hv_vmbus_g_connection.channel_msg_anchor,
102 msg_info,
103 msg_list_entry);
104
105 mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
106
107 ret = hv_vmbus_post_message(
108 msg,
109 sizeof(hv_vmbus_channel_initiate_contact));
110
111 if (ret != 0) {
112 mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
113 TAILQ_REMOVE(
114 &hv_vmbus_g_connection.channel_msg_anchor,
115 msg_info,
116 msg_list_entry);
117 mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
118 return (ret);
119 }
120
121 /**
122 * Wait for the connection response
123 */
124 ret = sema_timedwait(&msg_info->wait_sema, 500); /* KYS 5 seconds */
125
126 mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
127 TAILQ_REMOVE(
128 &hv_vmbus_g_connection.channel_msg_anchor,
129 msg_info,
130 msg_list_entry);
131 mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
132
133 /**
134 * Check if successful
135 */
136 if (msg_info->response.version_response.version_supported) {
137 hv_vmbus_g_connection.connect_state = HV_CONNECTED;
138 } else {
139 ret = ECONNREFUSED;
140 }
141
142 return (ret);
143}
144
145/**
146 * Send a connect request on the partition service connection
147 */
148int
149hv_vmbus_connect(void) {
150 int ret = 0;
151 uint32_t version;
152 hv_vmbus_channel_msg_info* msg_info = NULL;
153
154 /**
155 * Make sure we are not connecting or connected
156 */
157 if (hv_vmbus_g_connection.connect_state != HV_DISCONNECTED) {
158 return (-1);
159 }
160
161 /**
162 * Initialize the vmbus connection
163 */
164 hv_vmbus_g_connection.connect_state = HV_CONNECTING;
165
166 TAILQ_INIT(&hv_vmbus_g_connection.channel_msg_anchor);
167 mtx_init(&hv_vmbus_g_connection.channel_msg_lock, "vmbus channel msg",
168 NULL, MTX_SPIN);
169
170 TAILQ_INIT(&hv_vmbus_g_connection.channel_anchor);
171 mtx_init(&hv_vmbus_g_connection.channel_lock, "vmbus channel",
172 NULL, MTX_DEF);
173
174 /**
175 * Setup the vmbus event connection for channel interrupt abstraction
176 * stuff
177 */
178 hv_vmbus_g_connection.interrupt_page = malloc(
179 PAGE_SIZE, M_DEVBUF,
180 M_WAITOK | M_ZERO);
181
182 hv_vmbus_g_connection.recv_interrupt_page =
183 hv_vmbus_g_connection.interrupt_page;
184
185 hv_vmbus_g_connection.send_interrupt_page =
186 ((uint8_t *) hv_vmbus_g_connection.interrupt_page +
187 (PAGE_SIZE >> 1));
188
189 /**
190 * Set up the monitor notification facility. The 1st page for
191 * parent->child and the 2nd page for child->parent
192 */
193 hv_vmbus_g_connection.monitor_page_1 = malloc(
194 PAGE_SIZE,
195 M_DEVBUF,
196 M_WAITOK | M_ZERO);
197 hv_vmbus_g_connection.monitor_page_2 = malloc(
198 PAGE_SIZE,
199 M_DEVBUF,
200 M_WAITOK | M_ZERO);
201
202 msg_info = (hv_vmbus_channel_msg_info*)
203 malloc(sizeof(hv_vmbus_channel_msg_info) +
204 sizeof(hv_vmbus_channel_initiate_contact),
205 M_DEVBUF, M_WAITOK | M_ZERO);
206
207 hv_vmbus_g_connection.channels = malloc(sizeof(hv_vmbus_channel*) *
208 HV_CHANNEL_MAX_COUNT,
209 M_DEVBUF, M_WAITOK | M_ZERO);
210 /*
211 * Find the highest vmbus version number we can support.
212 */
213 version = HV_VMBUS_VERSION_CURRENT;
214
215 do {
216 ret = hv_vmbus_negotiate_version(msg_info, version);
217 if (ret == EWOULDBLOCK) {
218 /*
219 * We timed out.
220 */
221 goto cleanup;
222 }
223
224 if (hv_vmbus_g_connection.connect_state == HV_CONNECTED)
225 break;
226
227 version = hv_vmbus_get_next_version(version);
228 } while (version != HV_VMBUS_VERSION_INVALID);
229
230 hv_vmbus_protocal_version = version;
231 if (bootverbose)
232 printf("VMBUS: Protocol Version: %d.%d\n",
233 version >> 16, version & 0xFFFF);
234
235 sema_destroy(&msg_info->wait_sema);
236 free(msg_info, M_DEVBUF);
237
238 return (0);
239
240 /*
241 * Cleanup after failure!
242 */
243 cleanup:
244
245 hv_vmbus_g_connection.connect_state = HV_DISCONNECTED;
246
247 mtx_destroy(&hv_vmbus_g_connection.channel_lock);
248 mtx_destroy(&hv_vmbus_g_connection.channel_msg_lock);
249
250 if (hv_vmbus_g_connection.interrupt_page != NULL) {
251 contigfree(
252 hv_vmbus_g_connection.interrupt_page,
253 PAGE_SIZE,
254 M_DEVBUF);
255 hv_vmbus_g_connection.interrupt_page = NULL;
256 }
257
258 free(hv_vmbus_g_connection.monitor_page_1, M_DEVBUF);
259 free(hv_vmbus_g_connection.monitor_page_2, M_DEVBUF);
260
261 if (msg_info) {
262 sema_destroy(&msg_info->wait_sema);
263 free(msg_info, M_DEVBUF);
264 }
265
266 free(hv_vmbus_g_connection.channels, M_DEVBUF);
267 return (ret);
268}
269
270/**
271 * Send a disconnect request on the partition service connection
272 */
273int
274hv_vmbus_disconnect(void) {
275 int ret = 0;
276 hv_vmbus_channel_unload msg;
277
278 msg.message_type = HV_CHANNEL_MESSAGE_UNLOAD;
279
280 ret = hv_vmbus_post_message(&msg, sizeof(hv_vmbus_channel_unload));
281
282 contigfree(hv_vmbus_g_connection.interrupt_page, PAGE_SIZE, M_DEVBUF);
283
284 mtx_destroy(&hv_vmbus_g_connection.channel_msg_lock);
285
286 free(hv_vmbus_g_connection.channels, M_DEVBUF);
287 hv_vmbus_g_connection.connect_state = HV_DISCONNECTED;
288
289 return (ret);
290}
291
292/**
293 * Handler for events
294 */
295void
296hv_vmbus_on_events(int cpu)
297{
298 int bit;
299 int dword;
300 void *page_addr;
301 uint32_t* recv_interrupt_page = NULL;
302 int rel_id;
303 int maxdword;
304 hv_vmbus_synic_event_flags *event;
305 /* int maxdword = PAGE_SIZE >> 3; */
306
307 KASSERT(cpu <= mp_maxid, ("VMBUS: hv_vmbus_on_events: "
308 "cpu out of range!"));
309
310 if ((hv_vmbus_protocal_version == HV_VMBUS_VERSION_WS2008) ||
311 (hv_vmbus_protocal_version == HV_VMBUS_VERSION_WIN7)) {
312 maxdword = HV_MAX_NUM_CHANNELS_SUPPORTED >> 5;
313 /*
314 * receive size is 1/2 page and divide that by 4 bytes
315 */
316 recv_interrupt_page =
317 hv_vmbus_g_connection.recv_interrupt_page;
318 } else {
319 /*
320 * On Host with Win8 or above, the event page can be
321 * checked directly to get the id of the channel
322 * that has the pending interrupt.
323 */
324 maxdword = HV_EVENT_FLAGS_DWORD_COUNT;
325 page_addr = hv_vmbus_g_context.syn_ic_event_page[cpu];
326 event = (hv_vmbus_synic_event_flags *)
327 page_addr + HV_VMBUS_MESSAGE_SINT;
328 recv_interrupt_page = event->flags32;
329 }
330
331 /*
332 * Check events
333 */
334 if (recv_interrupt_page != NULL) {
335 for (dword = 0; dword < maxdword; dword++) {
336 if (recv_interrupt_page[dword]) {
337 for (bit = 0; bit < HV_CHANNEL_DWORD_LEN; bit++) {
338 if (synch_test_and_clear_bit(bit,
339 (uint32_t *) &recv_interrupt_page[dword])) {
340 rel_id = (dword << 5) + bit;
341 if (rel_id == 0) {
342 /*
343 * Special case -
344 * vmbus channel protocol msg.
345 */
346 continue;
347 } else {
348 hv_vmbus_channel * channel = hv_vmbus_g_connection.channels[rel_id];
349 /* if channel is closed or closing */
350 if (channel == NULL || channel->rxq == NULL)
351 continue;
352
353 if (channel->batched_reading)
354 hv_ring_buffer_read_begin(&channel->inbound);
355 taskqueue_enqueue_fast(channel->rxq, &channel->channel_task);
356 }
357 }
358 }
359 }
360 }
361 }
362
363 return;
364}
365
366/**
367 * Send a msg on the vmbus's message connection
368 */
369int hv_vmbus_post_message(void *buffer, size_t bufferLen) {
370 int ret = 0;
371 hv_vmbus_connection_id connId;
372 unsigned retries = 0;
373
374 /* NetScaler delays from previous code were consolidated here */
375 static int delayAmount[] = {100, 100, 100, 500, 500, 5000, 5000, 5000};
376
377 /* for(each entry in delayAmount) try to post message,
378 * delay a little bit before retrying
379 */
380 for (retries = 0;
381 retries < sizeof(delayAmount)/sizeof(delayAmount[0]); retries++) {
382 connId.as_uint32_t = 0;
383 connId.u.id = HV_VMBUS_MESSAGE_CONNECTION_ID;
384 ret = hv_vmbus_post_msg_via_msg_ipc(connId, 1, buffer, bufferLen);
385 if (ret != HV_STATUS_INSUFFICIENT_BUFFERS)
386 break;
387 /* TODO: KYS We should use a blocking wait call */
388 DELAY(delayAmount[retries]);
389 }
390
391 KASSERT(ret == 0, ("Error VMBUS: Message Post Failed\n"));
392
393 return (ret);
394}
395
396/**
397 * Send an event notification to the parent
398 */
399int
400hv_vmbus_set_event(hv_vmbus_channel *channel) {
401 int ret = 0;
402 uint32_t child_rel_id = channel->offer_msg.child_rel_id;
403
404 /* Each uint32_t represents 32 channels */
405
406 synch_set_bit(child_rel_id & 31,
407 (((uint32_t *)hv_vmbus_g_connection.send_interrupt_page
408 + (child_rel_id >> 5))));
409 ret = hv_vmbus_signal_event(channel->signal_event_param);
410
411 return (ret);
412}