2 * Copyright (c) 2009-2012 Microsoft Corp.
3 * Copyright (c) 2012 NetApp Inc.
4 * Copyright (c) 2012 Citrix Inc.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice unmodified, this list of conditions, and the following
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.
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.
29 #include <sys/param.h>
30 #include <sys/malloc.h>
31 #include <sys/systm.h>
33 #include <sys/mutex.h>
34 #include <machine/bus.h>
36 #include <vm/vm_param.h>
39 #include "hv_vmbus_priv.h"
44 hv_vmbus_connection hv_vmbus_g_connection =
45 { .connect_state = HV_DISCONNECTED,
46 .next_gpadl_handle = 0xE1E10, };
49 * Send a connect request on the partition service connection
52 hv_vmbus_connect(void) {
54 hv_vmbus_channel_msg_info* msg_info = NULL;
55 hv_vmbus_channel_initiate_contact* msg;
58 * Make sure we are not connecting or connected
60 if (hv_vmbus_g_connection.connect_state != HV_DISCONNECTED) {
65 * Initialize the vmbus connection
67 hv_vmbus_g_connection.connect_state = HV_CONNECTING;
68 hv_vmbus_g_connection.work_queue = hv_work_queue_create("vmbusQ");
69 sema_init(&hv_vmbus_g_connection.control_sema, 1, "control_sema");
71 TAILQ_INIT(&hv_vmbus_g_connection.channel_msg_anchor);
72 mtx_init(&hv_vmbus_g_connection.channel_msg_lock, "vmbus channel msg",
75 TAILQ_INIT(&hv_vmbus_g_connection.channel_anchor);
76 mtx_init(&hv_vmbus_g_connection.channel_lock, "vmbus channel",
80 * Setup the vmbus event connection for channel interrupt abstraction
83 hv_vmbus_g_connection.interrupt_page = contigmalloc(
85 M_NOWAIT | M_ZERO, 0UL,
88 KASSERT(hv_vmbus_g_connection.interrupt_page != NULL,
89 ("Error VMBUS: malloc failed to allocate Channel"
90 " Request Event message!"));
91 if (hv_vmbus_g_connection.interrupt_page == NULL) {
96 hv_vmbus_g_connection.recv_interrupt_page =
97 hv_vmbus_g_connection.interrupt_page;
99 hv_vmbus_g_connection.send_interrupt_page =
100 ((uint8_t *) hv_vmbus_g_connection.interrupt_page +
104 * Set up the monitor notification facility. The 1st page for
105 * parent->child and the 2nd page for child->parent
107 hv_vmbus_g_connection.monitor_pages = contigmalloc(
115 KASSERT(hv_vmbus_g_connection.monitor_pages != NULL,
116 ("Error VMBUS: malloc failed to allocate Monitor Pages!"));
117 if (hv_vmbus_g_connection.monitor_pages == NULL) {
122 msg_info = (hv_vmbus_channel_msg_info*)
123 malloc(sizeof(hv_vmbus_channel_msg_info) +
124 sizeof(hv_vmbus_channel_initiate_contact),
125 M_DEVBUF, M_NOWAIT | M_ZERO);
126 KASSERT(msg_info != NULL,
127 ("Error VMBUS: malloc failed for Initiate Contact message!"));
128 if (msg_info == NULL) {
133 sema_init(&msg_info->wait_sema, 0, "Msg Info Sema");
134 msg = (hv_vmbus_channel_initiate_contact*) msg_info->msg;
136 msg->header.message_type = HV_CHANNEL_MESSAGE_INITIATED_CONTACT;
137 msg->vmbus_version_requested = HV_VMBUS_REVISION_NUMBER;
139 msg->interrupt_page = hv_get_phys_addr(
140 hv_vmbus_g_connection.interrupt_page);
142 msg->monitor_page_1 = hv_get_phys_addr(
143 hv_vmbus_g_connection.monitor_pages);
145 msg->monitor_page_2 =
147 ((uint8_t *) hv_vmbus_g_connection.monitor_pages
151 * Add to list before we send the request since we may receive the
152 * response before returning from this routine
154 mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
157 &hv_vmbus_g_connection.channel_msg_anchor,
161 mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
163 ret = hv_vmbus_post_message(
165 sizeof(hv_vmbus_channel_initiate_contact));
168 mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
170 &hv_vmbus_g_connection.channel_msg_anchor,
173 mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
178 * Wait for the connection response
180 ret = sema_timedwait(&msg_info->wait_sema, 500); /* KYS 5 seconds */
182 mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
184 &hv_vmbus_g_connection.channel_msg_anchor,
187 mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
190 * Check if successful
192 if (msg_info->response.version_response.version_supported) {
193 hv_vmbus_g_connection.connect_state = HV_CONNECTED;
199 sema_destroy(&msg_info->wait_sema);
200 free(msg_info, M_DEVBUF);
205 * Cleanup after failure!
209 hv_vmbus_g_connection.connect_state = HV_DISCONNECTED;
211 hv_work_queue_close(hv_vmbus_g_connection.work_queue);
212 sema_destroy(&hv_vmbus_g_connection.control_sema);
213 mtx_destroy(&hv_vmbus_g_connection.channel_lock);
214 mtx_destroy(&hv_vmbus_g_connection.channel_msg_lock);
216 if (hv_vmbus_g_connection.interrupt_page != NULL) {
218 hv_vmbus_g_connection.interrupt_page,
221 hv_vmbus_g_connection.interrupt_page = NULL;
224 if (hv_vmbus_g_connection.monitor_pages != NULL) {
226 hv_vmbus_g_connection.monitor_pages,
229 hv_vmbus_g_connection.monitor_pages = NULL;
233 sema_destroy(&msg_info->wait_sema);
234 free(msg_info, M_DEVBUF);
241 * Send a disconnect request on the partition service connection
244 hv_vmbus_disconnect(void) {
246 hv_vmbus_channel_unload* msg;
248 msg = malloc(sizeof(hv_vmbus_channel_unload),
249 M_DEVBUF, M_NOWAIT | M_ZERO);
251 ("Error VMBUS: malloc failed to allocate Channel Unload Msg!"));
255 msg->message_type = HV_CHANNEL_MESSAGE_UNLOAD;
257 ret = hv_vmbus_post_message(msg, sizeof(hv_vmbus_channel_unload));
260 contigfree(hv_vmbus_g_connection.interrupt_page, PAGE_SIZE, M_DEVBUF);
262 mtx_destroy(&hv_vmbus_g_connection.channel_msg_lock);
264 hv_work_queue_close(hv_vmbus_g_connection.work_queue);
265 sema_destroy(&hv_vmbus_g_connection.control_sema);
267 hv_vmbus_g_connection.connect_state = HV_DISCONNECTED;
275 * Get the channel object given its child relative id (ie channel id)
278 hv_vmbus_get_channel_from_rel_id(uint32_t rel_id) {
280 hv_vmbus_channel* channel;
281 hv_vmbus_channel* foundChannel = NULL;
285 * Consider optimization where relids are stored in a fixed size array
286 * and channels are accessed without the need to take this lock or search
289 mtx_lock_spin(&hv_vmbus_g_connection.channel_lock);
290 TAILQ_FOREACH(channel,
291 &hv_vmbus_g_connection.channel_anchor, list_entry) {
293 if (channel->offer_msg.child_rel_id == rel_id) {
294 foundChannel = channel;
298 mtx_unlock_spin(&hv_vmbus_g_connection.channel_lock);
300 return (foundChannel);
304 * Process a channel event notification
307 VmbusProcessChannelEvent(uint32_t relid)
309 hv_vmbus_channel* channel;
312 * Find the channel based on this relid and invokes
313 * the channel callback to process the event
316 channel = hv_vmbus_get_channel_from_rel_id(relid);
318 if (channel == NULL) {
322 * To deal with the race condition where we might
323 * receive a packet while the relevant driver is
324 * being unloaded, dispatch the callback while
325 * holding the channel lock. The unloading driver
326 * will acquire the same channel lock to set the
327 * callback to NULL. This closes the window.
330 mtx_lock(&channel->inbound_lock);
331 if (channel->on_channel_callback != NULL) {
332 channel->on_channel_callback(channel->channel_callback_context);
334 mtx_unlock(&channel->inbound_lock);
341 hv_vmbus_on_events(void *arg)
346 int maxdword = HV_MAX_NUM_CHANNELS_SUPPORTED >> 5;
347 /* int maxdword = PAGE_SIZE >> 3; */
350 * receive size is 1/2 page and divide that by 4 bytes
353 uint32_t* recv_interrupt_page =
354 hv_vmbus_g_connection.recv_interrupt_page;
359 if (recv_interrupt_page != NULL) {
360 for (dword = 0; dword < maxdword; dword++) {
361 if (recv_interrupt_page[dword]) {
362 for (bit = 0; bit < 32; bit++) {
363 if (synch_test_and_clear_bit(bit,
364 (uint32_t *) &recv_interrupt_page[dword])) {
365 rel_id = (dword << 5) + bit;
369 * vmbus channel protocol msg.
373 VmbusProcessChannelEvent(rel_id);
386 * Send a msg on the vmbus's message connection
388 int hv_vmbus_post_message(void *buffer, size_t bufferLen) {
390 hv_vmbus_connection_id connId;
391 unsigned retries = 0;
393 /* NetScaler delays from previous code were consolidated here */
394 static int delayAmount[] = {100, 100, 100, 500, 500, 5000, 5000, 5000};
396 /* for(each entry in delayAmount) try to post message,
397 * delay a little bit before retrying
400 retries < sizeof(delayAmount)/sizeof(delayAmount[0]); retries++) {
401 connId.as_uint32_t = 0;
402 connId.u.id = HV_VMBUS_MESSAGE_CONNECTION_ID;
403 ret = hv_vmbus_post_msg_via_msg_ipc(connId, 1, buffer, bufferLen);
404 if (ret != HV_STATUS_INSUFFICIENT_BUFFERS)
406 /* TODO: KYS We should use a blocking wait call */
407 DELAY(delayAmount[retries]);
410 KASSERT(ret == 0, ("Error VMBUS: Message Post Failed\n"));
416 * Send an event notification to the parent
419 hv_vmbus_set_event(uint32_t child_rel_id) {
422 /* Each uint32_t represents 32 channels */
424 synch_set_bit(child_rel_id & 31,
425 (((uint32_t *)hv_vmbus_g_connection.send_interrupt_page
426 + (child_rel_id >> 5))));
427 ret = hv_vmbus_signal_event();