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/kernel.h>
31#include <sys/malloc.h>
32#include <sys/systm.h>
33#include <sys/lock.h>
34#include <sys/mutex.h>
35#include <machine/bus.h>
36#include <vm/vm.h>
37#include <vm/vm_param.h>
38#include <vm/pmap.h>
39
40#include "hv_vmbus_priv.h"
41
42/*
43 * Globals
44 */
45hv_vmbus_connection hv_vmbus_g_connection =
46 { .connect_state = HV_DISCONNECTED,
47 .next_gpadl_handle = 0xE1E10, };
48
49uint32_t hv_vmbus_protocal_version = HV_VMBUS_VERSION_WS2008;
50
51static uint32_t
52hv_vmbus_get_next_version(uint32_t current_ver)
53{
54 switch (current_ver) {
55 case (HV_VMBUS_VERSION_WIN7):
56 return(HV_VMBUS_VERSION_WS2008);
57
58 case (HV_VMBUS_VERSION_WIN8):
59 return(HV_VMBUS_VERSION_WIN7);
60
61 case (HV_VMBUS_VERSION_WIN8_1):
62 return(HV_VMBUS_VERSION_WIN8);
63
64 case (HV_VMBUS_VERSION_WS2008):
65 default:
66 return(HV_VMBUS_VERSION_INVALID);
67 }
68}
69
70/**
71 * Negotiate the highest supported hypervisor version.
72 */
73static int
74hv_vmbus_negotiate_version(hv_vmbus_channel_msg_info *msg_info,
75 uint32_t version)
76{
77 int ret = 0;
78 hv_vmbus_channel_initiate_contact *msg;
79
80 sema_init(&msg_info->wait_sema, 0, "Msg Info Sema");
81 msg = (hv_vmbus_channel_initiate_contact*) msg_info->msg;
82
83 msg->header.message_type = HV_CHANNEL_MESSAGE_INITIATED_CONTACT;
84 msg->vmbus_version_requested = version;
85
86 msg->interrupt_page = hv_get_phys_addr(
87 hv_vmbus_g_connection.interrupt_page);
88
89 msg->monitor_page_1 = hv_get_phys_addr(
90 hv_vmbus_g_connection.monitor_page_1);
91
92 msg->monitor_page_2 = hv_get_phys_addr(
93 hv_vmbus_g_connection.monitor_page_2);
94
95 /**
96 * Add to list before we send the request since we may receive the
97 * response before returning from this routine
98 */
99 mtx_lock(&hv_vmbus_g_connection.channel_msg_lock);
100
101 TAILQ_INSERT_TAIL(
102 &hv_vmbus_g_connection.channel_msg_anchor,
103 msg_info,
104 msg_list_entry);
105
106 mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock);
107
108 ret = hv_vmbus_post_message(
109 msg,
110 sizeof(hv_vmbus_channel_initiate_contact));
111
112 if (ret != 0) {
113 mtx_lock(&hv_vmbus_g_connection.channel_msg_lock);
114 TAILQ_REMOVE(
115 &hv_vmbus_g_connection.channel_msg_anchor,
116 msg_info,
117 msg_list_entry);
118 mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock);
119 return (ret);
120 }
121
122 /**
123 * Wait for the connection response
124 */
125 ret = sema_timedwait(&msg_info->wait_sema, 5 * hz); /* KYS 5 seconds */
126
127 mtx_lock(&hv_vmbus_g_connection.channel_msg_lock);
128 TAILQ_REMOVE(
129 &hv_vmbus_g_connection.channel_msg_anchor,
130 msg_info,
131 msg_list_entry);
132 mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock);
133
134 /**
135 * Check if successful
136 */
137 if (msg_info->response.version_response.version_supported) {
138 hv_vmbus_g_connection.connect_state = HV_CONNECTED;
139 } else {
140 ret = ECONNREFUSED;
141 }
142
143 return (ret);
144}
145
146/**
147 * Send a connect request on the partition service connection
148 */
149int
150hv_vmbus_connect(void) {
151 int ret = 0;
152 uint32_t version;
153 hv_vmbus_channel_msg_info* msg_info = NULL;
154
155 /**
156 * Make sure we are not connecting or connected
157 */
158 if (hv_vmbus_g_connection.connect_state != HV_DISCONNECTED) {
159 return (-1);
160 }
161
162 /**
163 * Initialize the vmbus connection
164 */
165 hv_vmbus_g_connection.connect_state = HV_CONNECTING;
166
167 TAILQ_INIT(&hv_vmbus_g_connection.channel_msg_anchor);
168 mtx_init(&hv_vmbus_g_connection.channel_msg_lock, "vmbus channel msg",
169 NULL, MTX_DEF);
170
171 TAILQ_INIT(&hv_vmbus_g_connection.channel_anchor);
172 mtx_init(&hv_vmbus_g_connection.channel_lock, "vmbus channel",
173 NULL, MTX_DEF);
174
175 /**
176 * Setup the vmbus event connection for channel interrupt abstraction
177 * stuff
178 */
179 hv_vmbus_g_connection.interrupt_page = malloc(
180 PAGE_SIZE, M_DEVBUF,
181 M_WAITOK | M_ZERO);
182
183 hv_vmbus_g_connection.recv_interrupt_page =
184 hv_vmbus_g_connection.interrupt_page;
185
186 hv_vmbus_g_connection.send_interrupt_page =
187 ((uint8_t *) hv_vmbus_g_connection.interrupt_page +
188 (PAGE_SIZE >> 1));
189
190 /**
191 * Set up the monitor notification facility. The 1st page for
192 * parent->child and the 2nd page for child->parent
193 */
194 hv_vmbus_g_connection.monitor_page_1 = malloc(
195 PAGE_SIZE,
196 M_DEVBUF,
197 M_WAITOK | M_ZERO);
198 hv_vmbus_g_connection.monitor_page_2 = malloc(
199 PAGE_SIZE,
200 M_DEVBUF,
201 M_WAITOK | M_ZERO);
202
203 msg_info = (hv_vmbus_channel_msg_info*)
204 malloc(sizeof(hv_vmbus_channel_msg_info) +
205 sizeof(hv_vmbus_channel_initiate_contact),
206 M_DEVBUF, M_WAITOK | M_ZERO);
207
208 hv_vmbus_g_connection.channels = malloc(sizeof(hv_vmbus_channel*) *
209 HV_CHANNEL_MAX_COUNT,
210 M_DEVBUF, M_WAITOK | M_ZERO);
211 /*
212 * Find the highest vmbus version number we can support.
213 */
214 version = HV_VMBUS_VERSION_CURRENT;
215
216 do {
217 ret = hv_vmbus_negotiate_version(msg_info, version);
218 if (ret == EWOULDBLOCK) {
219 /*
220 * We timed out.
221 */
222 goto cleanup;
223 }
224
225 if (hv_vmbus_g_connection.connect_state == HV_CONNECTED)
226 break;
227
228 version = hv_vmbus_get_next_version(version);
229 } while (version != HV_VMBUS_VERSION_INVALID);
230
231 hv_vmbus_protocal_version = version;
232 if (bootverbose)
233 printf("VMBUS: Protocol Version: %d.%d\n",
234 version >> 16, version & 0xFFFF);
235
236 sema_destroy(&msg_info->wait_sema);
237 free(msg_info, M_DEVBUF);
238
239 return (0);
240
241 /*
242 * Cleanup after failure!
243 */
244 cleanup:
245
246 hv_vmbus_g_connection.connect_state = HV_DISCONNECTED;
247
248 mtx_destroy(&hv_vmbus_g_connection.channel_lock);
249 mtx_destroy(&hv_vmbus_g_connection.channel_msg_lock);
250
251 if (hv_vmbus_g_connection.interrupt_page != NULL) {
252 free(hv_vmbus_g_connection.interrupt_page, M_DEVBUF);
253 hv_vmbus_g_connection.interrupt_page = NULL;
254 }
255
256 free(hv_vmbus_g_connection.monitor_page_1, M_DEVBUF);
257 free(hv_vmbus_g_connection.monitor_page_2, M_DEVBUF);
258
259 if (msg_info) {
260 sema_destroy(&msg_info->wait_sema);
261 free(msg_info, M_DEVBUF);
262 }
263
264 free(hv_vmbus_g_connection.channels, M_DEVBUF);
265 return (ret);
266}
267
268/**
269 * Send a disconnect request on the partition service connection
270 */
271int
272hv_vmbus_disconnect(void) {
273 int ret = 0;
274 hv_vmbus_channel_unload msg;
275
276 msg.message_type = HV_CHANNEL_MESSAGE_UNLOAD;
277
278 ret = hv_vmbus_post_message(&msg, sizeof(hv_vmbus_channel_unload));
279
280 free(hv_vmbus_g_connection.interrupt_page, M_DEVBUF);
281
282 mtx_destroy(&hv_vmbus_g_connection.channel_msg_lock);
283
284 free(hv_vmbus_g_connection.channels, M_DEVBUF);
285 hv_vmbus_g_connection.connect_state = HV_DISCONNECTED;
286
287 return (ret);
288}
289
290/**
291 * Handler for events
292 */
293void
294hv_vmbus_on_events(int cpu)
295{
296 int bit;
297 int dword;
298 void *page_addr;
299 uint32_t* recv_interrupt_page = NULL;
300 int rel_id;
301 int maxdword;
302 hv_vmbus_synic_event_flags *event;
303 /* int maxdword = PAGE_SIZE >> 3; */
304
305 KASSERT(cpu <= mp_maxid, ("VMBUS: hv_vmbus_on_events: "
306 "cpu out of range!"));
307
308 if ((hv_vmbus_protocal_version == HV_VMBUS_VERSION_WS2008) ||
309 (hv_vmbus_protocal_version == HV_VMBUS_VERSION_WIN7)) {
310 maxdword = HV_MAX_NUM_CHANNELS_SUPPORTED >> 5;
311 /*
312 * receive size is 1/2 page and divide that by 4 bytes
313 */
314 recv_interrupt_page =
315 hv_vmbus_g_connection.recv_interrupt_page;
316 } else {
317 /*
318 * On Host with Win8 or above, the event page can be
319 * checked directly to get the id of the channel
320 * that has the pending interrupt.
321 */
322 maxdword = HV_EVENT_FLAGS_DWORD_COUNT;
323 page_addr = hv_vmbus_g_context.syn_ic_event_page[cpu];
324 event = (hv_vmbus_synic_event_flags *)
325 page_addr + HV_VMBUS_MESSAGE_SINT;
326 recv_interrupt_page = event->flags32;
327 }
328
329 /*
330 * Check events
331 */
332 if (recv_interrupt_page != NULL) {
333 for (dword = 0; dword < maxdword; dword++) {
334 if (recv_interrupt_page[dword]) {
335 for (bit = 0; bit < HV_CHANNEL_DWORD_LEN; bit++) {
336 if (synch_test_and_clear_bit(bit,
337 (uint32_t *) &recv_interrupt_page[dword])) {
338 rel_id = (dword << 5) + bit;
339 if (rel_id == 0) {
340 /*
341 * Special case -
342 * vmbus channel protocol msg.
343 */
344 continue;
345 } else {
346 hv_vmbus_channel * channel = hv_vmbus_g_connection.channels[rel_id];
347 /* if channel is closed or closing */
348 if (channel == NULL || channel->rxq == NULL)
349 continue;
350
351 if (channel->batched_reading)
352 hv_ring_buffer_read_begin(&channel->inbound);
353 taskqueue_enqueue(channel->rxq, &channel->channel_task);
354 }
355 }
356 }
357 }
358 }
359 }
360
361 return;
362}
363
364/**
365 * Send a msg on the vmbus's message connection
366 */
367int hv_vmbus_post_message(void *buffer, size_t bufferLen)
368{
369 hv_vmbus_connection_id connId;
370 sbintime_t time = SBT_1MS;
371 int retries;
372 int ret;
373
374 connId.as_uint32_t = 0;
375 connId.u.id = HV_VMBUS_MESSAGE_CONNECTION_ID;
376
377 /*
378 * We retry to cope with transient failures caused by host side's
379 * insufficient resources. 20 times should suffice in practice.
380 */
381 for (retries = 0; retries < 20; retries++) {
382 ret = hv_vmbus_post_msg_via_msg_ipc(connId, 1, buffer,
383 bufferLen);
384 if (ret == HV_STATUS_SUCCESS)
385 return (0);
386
387 pause_sbt("pstmsg", time, 0, C_HARDCLOCK);
388 if (time < SBT_1S * 2)
389 time *= 2;
390 }
391
392 KASSERT(ret == HV_STATUS_SUCCESS,
393 ("Error VMBUS: Message Post Failed, ret=%d\n", ret));
394
395 return (EAGAIN);
396}
397
398/**
399 * Send an event notification to the parent
400 */
401int
402hv_vmbus_set_event(hv_vmbus_channel *channel) {
403 int ret = 0;
404 uint32_t child_rel_id = channel->offer_msg.child_rel_id;
405
406 /* Each uint32_t represents 32 channels */
407
408 synch_set_bit(child_rel_id & 31,
409 (((uint32_t *)hv_vmbus_g_connection.send_interrupt_page
410 + (child_rel_id >> 5))));
411 ret = hv_vmbus_signal_event(channel->signal_event_param);
412
413 return (ret);
414}
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/kernel.h>
31#include <sys/malloc.h>
32#include <sys/systm.h>
33#include <sys/lock.h>
34#include <sys/mutex.h>
35#include <machine/bus.h>
36#include <vm/vm.h>
37#include <vm/vm_param.h>
38#include <vm/pmap.h>
39
40#include "hv_vmbus_priv.h"
41
42/*
43 * Globals
44 */
45hv_vmbus_connection hv_vmbus_g_connection =
46 { .connect_state = HV_DISCONNECTED,
47 .next_gpadl_handle = 0xE1E10, };
48
49uint32_t hv_vmbus_protocal_version = HV_VMBUS_VERSION_WS2008;
50
51static uint32_t
52hv_vmbus_get_next_version(uint32_t current_ver)
53{
54 switch (current_ver) {
55 case (HV_VMBUS_VERSION_WIN7):
56 return(HV_VMBUS_VERSION_WS2008);
57
58 case (HV_VMBUS_VERSION_WIN8):
59 return(HV_VMBUS_VERSION_WIN7);
60
61 case (HV_VMBUS_VERSION_WIN8_1):
62 return(HV_VMBUS_VERSION_WIN8);
63
64 case (HV_VMBUS_VERSION_WS2008):
65 default:
66 return(HV_VMBUS_VERSION_INVALID);
67 }
68}
69
70/**
71 * Negotiate the highest supported hypervisor version.
72 */
73static int
74hv_vmbus_negotiate_version(hv_vmbus_channel_msg_info *msg_info,
75 uint32_t version)
76{
77 int ret = 0;
78 hv_vmbus_channel_initiate_contact *msg;
79
80 sema_init(&msg_info->wait_sema, 0, "Msg Info Sema");
81 msg = (hv_vmbus_channel_initiate_contact*) msg_info->msg;
82
83 msg->header.message_type = HV_CHANNEL_MESSAGE_INITIATED_CONTACT;
84 msg->vmbus_version_requested = version;
85
86 msg->interrupt_page = hv_get_phys_addr(
87 hv_vmbus_g_connection.interrupt_page);
88
89 msg->monitor_page_1 = hv_get_phys_addr(
90 hv_vmbus_g_connection.monitor_page_1);
91
92 msg->monitor_page_2 = hv_get_phys_addr(
93 hv_vmbus_g_connection.monitor_page_2);
94
95 /**
96 * Add to list before we send the request since we may receive the
97 * response before returning from this routine
98 */
99 mtx_lock(&hv_vmbus_g_connection.channel_msg_lock);
100
101 TAILQ_INSERT_TAIL(
102 &hv_vmbus_g_connection.channel_msg_anchor,
103 msg_info,
104 msg_list_entry);
105
106 mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock);
107
108 ret = hv_vmbus_post_message(
109 msg,
110 sizeof(hv_vmbus_channel_initiate_contact));
111
112 if (ret != 0) {
113 mtx_lock(&hv_vmbus_g_connection.channel_msg_lock);
114 TAILQ_REMOVE(
115 &hv_vmbus_g_connection.channel_msg_anchor,
116 msg_info,
117 msg_list_entry);
118 mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock);
119 return (ret);
120 }
121
122 /**
123 * Wait for the connection response
124 */
125 ret = sema_timedwait(&msg_info->wait_sema, 5 * hz); /* KYS 5 seconds */
126
127 mtx_lock(&hv_vmbus_g_connection.channel_msg_lock);
128 TAILQ_REMOVE(
129 &hv_vmbus_g_connection.channel_msg_anchor,
130 msg_info,
131 msg_list_entry);
132 mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock);
133
134 /**
135 * Check if successful
136 */
137 if (msg_info->response.version_response.version_supported) {
138 hv_vmbus_g_connection.connect_state = HV_CONNECTED;
139 } else {
140 ret = ECONNREFUSED;
141 }
142
143 return (ret);
144}
145
146/**
147 * Send a connect request on the partition service connection
148 */
149int
150hv_vmbus_connect(void) {
151 int ret = 0;
152 uint32_t version;
153 hv_vmbus_channel_msg_info* msg_info = NULL;
154
155 /**
156 * Make sure we are not connecting or connected
157 */
158 if (hv_vmbus_g_connection.connect_state != HV_DISCONNECTED) {
159 return (-1);
160 }
161
162 /**
163 * Initialize the vmbus connection
164 */
165 hv_vmbus_g_connection.connect_state = HV_CONNECTING;
166
167 TAILQ_INIT(&hv_vmbus_g_connection.channel_msg_anchor);
168 mtx_init(&hv_vmbus_g_connection.channel_msg_lock, "vmbus channel msg",
169 NULL, MTX_DEF);
170
171 TAILQ_INIT(&hv_vmbus_g_connection.channel_anchor);
172 mtx_init(&hv_vmbus_g_connection.channel_lock, "vmbus channel",
173 NULL, MTX_DEF);
174
175 /**
176 * Setup the vmbus event connection for channel interrupt abstraction
177 * stuff
178 */
179 hv_vmbus_g_connection.interrupt_page = malloc(
180 PAGE_SIZE, M_DEVBUF,
181 M_WAITOK | M_ZERO);
182
183 hv_vmbus_g_connection.recv_interrupt_page =
184 hv_vmbus_g_connection.interrupt_page;
185
186 hv_vmbus_g_connection.send_interrupt_page =
187 ((uint8_t *) hv_vmbus_g_connection.interrupt_page +
188 (PAGE_SIZE >> 1));
189
190 /**
191 * Set up the monitor notification facility. The 1st page for
192 * parent->child and the 2nd page for child->parent
193 */
194 hv_vmbus_g_connection.monitor_page_1 = malloc(
195 PAGE_SIZE,
196 M_DEVBUF,
197 M_WAITOK | M_ZERO);
198 hv_vmbus_g_connection.monitor_page_2 = malloc(
199 PAGE_SIZE,
200 M_DEVBUF,
201 M_WAITOK | M_ZERO);
202
203 msg_info = (hv_vmbus_channel_msg_info*)
204 malloc(sizeof(hv_vmbus_channel_msg_info) +
205 sizeof(hv_vmbus_channel_initiate_contact),
206 M_DEVBUF, M_WAITOK | M_ZERO);
207
208 hv_vmbus_g_connection.channels = malloc(sizeof(hv_vmbus_channel*) *
209 HV_CHANNEL_MAX_COUNT,
210 M_DEVBUF, M_WAITOK | M_ZERO);
211 /*
212 * Find the highest vmbus version number we can support.
213 */
214 version = HV_VMBUS_VERSION_CURRENT;
215
216 do {
217 ret = hv_vmbus_negotiate_version(msg_info, version);
218 if (ret == EWOULDBLOCK) {
219 /*
220 * We timed out.
221 */
222 goto cleanup;
223 }
224
225 if (hv_vmbus_g_connection.connect_state == HV_CONNECTED)
226 break;
227
228 version = hv_vmbus_get_next_version(version);
229 } while (version != HV_VMBUS_VERSION_INVALID);
230
231 hv_vmbus_protocal_version = version;
232 if (bootverbose)
233 printf("VMBUS: Protocol Version: %d.%d\n",
234 version >> 16, version & 0xFFFF);
235
236 sema_destroy(&msg_info->wait_sema);
237 free(msg_info, M_DEVBUF);
238
239 return (0);
240
241 /*
242 * Cleanup after failure!
243 */
244 cleanup:
245
246 hv_vmbus_g_connection.connect_state = HV_DISCONNECTED;
247
248 mtx_destroy(&hv_vmbus_g_connection.channel_lock);
249 mtx_destroy(&hv_vmbus_g_connection.channel_msg_lock);
250
251 if (hv_vmbus_g_connection.interrupt_page != NULL) {
252 free(hv_vmbus_g_connection.interrupt_page, M_DEVBUF);
253 hv_vmbus_g_connection.interrupt_page = NULL;
254 }
255
256 free(hv_vmbus_g_connection.monitor_page_1, M_DEVBUF);
257 free(hv_vmbus_g_connection.monitor_page_2, M_DEVBUF);
258
259 if (msg_info) {
260 sema_destroy(&msg_info->wait_sema);
261 free(msg_info, M_DEVBUF);
262 }
263
264 free(hv_vmbus_g_connection.channels, M_DEVBUF);
265 return (ret);
266}
267
268/**
269 * Send a disconnect request on the partition service connection
270 */
271int
272hv_vmbus_disconnect(void) {
273 int ret = 0;
274 hv_vmbus_channel_unload msg;
275
276 msg.message_type = HV_CHANNEL_MESSAGE_UNLOAD;
277
278 ret = hv_vmbus_post_message(&msg, sizeof(hv_vmbus_channel_unload));
279
280 free(hv_vmbus_g_connection.interrupt_page, M_DEVBUF);
281
282 mtx_destroy(&hv_vmbus_g_connection.channel_msg_lock);
283
284 free(hv_vmbus_g_connection.channels, M_DEVBUF);
285 hv_vmbus_g_connection.connect_state = HV_DISCONNECTED;
286
287 return (ret);
288}
289
290/**
291 * Handler for events
292 */
293void
294hv_vmbus_on_events(int cpu)
295{
296 int bit;
297 int dword;
298 void *page_addr;
299 uint32_t* recv_interrupt_page = NULL;
300 int rel_id;
301 int maxdword;
302 hv_vmbus_synic_event_flags *event;
303 /* int maxdword = PAGE_SIZE >> 3; */
304
305 KASSERT(cpu <= mp_maxid, ("VMBUS: hv_vmbus_on_events: "
306 "cpu out of range!"));
307
308 if ((hv_vmbus_protocal_version == HV_VMBUS_VERSION_WS2008) ||
309 (hv_vmbus_protocal_version == HV_VMBUS_VERSION_WIN7)) {
310 maxdword = HV_MAX_NUM_CHANNELS_SUPPORTED >> 5;
311 /*
312 * receive size is 1/2 page and divide that by 4 bytes
313 */
314 recv_interrupt_page =
315 hv_vmbus_g_connection.recv_interrupt_page;
316 } else {
317 /*
318 * On Host with Win8 or above, the event page can be
319 * checked directly to get the id of the channel
320 * that has the pending interrupt.
321 */
322 maxdword = HV_EVENT_FLAGS_DWORD_COUNT;
323 page_addr = hv_vmbus_g_context.syn_ic_event_page[cpu];
324 event = (hv_vmbus_synic_event_flags *)
325 page_addr + HV_VMBUS_MESSAGE_SINT;
326 recv_interrupt_page = event->flags32;
327 }
328
329 /*
330 * Check events
331 */
332 if (recv_interrupt_page != NULL) {
333 for (dword = 0; dword < maxdword; dword++) {
334 if (recv_interrupt_page[dword]) {
335 for (bit = 0; bit < HV_CHANNEL_DWORD_LEN; bit++) {
336 if (synch_test_and_clear_bit(bit,
337 (uint32_t *) &recv_interrupt_page[dword])) {
338 rel_id = (dword << 5) + bit;
339 if (rel_id == 0) {
340 /*
341 * Special case -
342 * vmbus channel protocol msg.
343 */
344 continue;
345 } else {
346 hv_vmbus_channel * channel = hv_vmbus_g_connection.channels[rel_id];
347 /* if channel is closed or closing */
348 if (channel == NULL || channel->rxq == NULL)
349 continue;
350
351 if (channel->batched_reading)
352 hv_ring_buffer_read_begin(&channel->inbound);
353 taskqueue_enqueue(channel->rxq, &channel->channel_task);
354 }
355 }
356 }
357 }
358 }
359 }
360
361 return;
362}
363
364/**
365 * Send a msg on the vmbus's message connection
366 */
367int hv_vmbus_post_message(void *buffer, size_t bufferLen)
368{
369 hv_vmbus_connection_id connId;
370 sbintime_t time = SBT_1MS;
371 int retries;
372 int ret;
373
374 connId.as_uint32_t = 0;
375 connId.u.id = HV_VMBUS_MESSAGE_CONNECTION_ID;
376
377 /*
378 * We retry to cope with transient failures caused by host side's
379 * insufficient resources. 20 times should suffice in practice.
380 */
381 for (retries = 0; retries < 20; retries++) {
382 ret = hv_vmbus_post_msg_via_msg_ipc(connId, 1, buffer,
383 bufferLen);
384 if (ret == HV_STATUS_SUCCESS)
385 return (0);
386
387 pause_sbt("pstmsg", time, 0, C_HARDCLOCK);
388 if (time < SBT_1S * 2)
389 time *= 2;
390 }
391
392 KASSERT(ret == HV_STATUS_SUCCESS,
393 ("Error VMBUS: Message Post Failed, ret=%d\n", ret));
394
395 return (EAGAIN);
396}
397
398/**
399 * Send an event notification to the parent
400 */
401int
402hv_vmbus_set_event(hv_vmbus_channel *channel) {
403 int ret = 0;
404 uint32_t child_rel_id = channel->offer_msg.child_rel_id;
405
406 /* Each uint32_t represents 32 channels */
407
408 synch_set_bit(child_rel_id & 31,
409 (((uint32_t *)hv_vmbus_g_connection.send_interrupt_page
410 + (child_rel_id >> 5))));
411 ret = hv_vmbus_signal_event(channel->signal_event_param);
412
413 return (ret);
414}