hv_connection.c revision 299892
1/*- 2 * Copyright (c) 2009-2012,2016 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 304 KASSERT(cpu <= mp_maxid, ("VMBUS: hv_vmbus_on_events: " 305 "cpu out of range!")); 306 307 page_addr = hv_vmbus_g_context.syn_ic_event_page[cpu]; 308 event = (hv_vmbus_synic_event_flags *) 309 page_addr + HV_VMBUS_MESSAGE_SINT; 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 if (synch_test_and_clear_bit(0, &event->flags32[0])) { 317 recv_interrupt_page = 318 hv_vmbus_g_connection.recv_interrupt_page; 319 } else { 320 return; 321 } 322 } else { 323 /* 324 * On Host with Win8 or above, the event page can be 325 * checked directly to get the id of the channel 326 * that has the pending interrupt. 327 */ 328 maxdword = HV_EVENT_FLAGS_DWORD_COUNT; 329 recv_interrupt_page = event->flags32; 330 } 331 332 /* 333 * Check events 334 */ 335 for (dword = 0; dword < maxdword; dword++) { 336 if (recv_interrupt_page[dword] == 0) 337 continue; 338 339 for (bit = 0; bit < HV_CHANNEL_DWORD_LEN; bit++) { 340 if (synch_test_and_clear_bit(bit, 341 (uint32_t *)&recv_interrupt_page[dword])) { 342 struct hv_vmbus_channel *channel; 343 344 rel_id = (dword << 5) + bit; 345 channel = 346 hv_vmbus_g_connection.channels[rel_id]; 347 348 /* if channel is closed or closing */ 349 if (channel == NULL || channel->rxq == NULL) 350 continue; 351 352 if (channel->batched_reading) { 353 hv_ring_buffer_read_begin( 354 &channel->inbound); 355 } 356 taskqueue_enqueue(channel->rxq, 357 &channel->channel_task); 358 } 359 } 360 } 361} 362 363/** 364 * Send a msg on the vmbus's message connection 365 */ 366int hv_vmbus_post_message(void *buffer, size_t bufferLen) 367{ 368 hv_vmbus_connection_id connId; 369 sbintime_t time = SBT_1MS; 370 int retries; 371 int ret; 372 373 connId.as_uint32_t = 0; 374 connId.u.id = HV_VMBUS_MESSAGE_CONNECTION_ID; 375 376 /* 377 * We retry to cope with transient failures caused by host side's 378 * insufficient resources. 20 times should suffice in practice. 379 */ 380 for (retries = 0; retries < 20; retries++) { 381 ret = hv_vmbus_post_msg_via_msg_ipc(connId, 1, buffer, 382 bufferLen); 383 if (ret == HV_STATUS_SUCCESS) 384 return (0); 385 386 pause_sbt("pstmsg", time, 0, C_HARDCLOCK); 387 if (time < SBT_1S * 2) 388 time *= 2; 389 } 390 391 KASSERT(ret == HV_STATUS_SUCCESS, 392 ("Error VMBUS: Message Post Failed, ret=%d\n", ret)); 393 394 return (EAGAIN); 395} 396 397/** 398 * Send an event notification to the parent 399 */ 400int 401hv_vmbus_set_event(hv_vmbus_channel *channel) { 402 int ret = 0; 403 uint32_t child_rel_id = channel->offer_msg.child_rel_id; 404 405 /* Each uint32_t represents 32 channels */ 406 407 synch_set_bit(child_rel_id & 31, 408 (((uint32_t *)hv_vmbus_g_connection.send_interrupt_page 409 + (child_rel_id >> 5)))); 410 ret = hv_vmbus_signal_event(channel->signal_event_param); 411 412 return (ret); 413} 414