hv_connection.c (255524) | hv_connection.c (282212) |
---|---|
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 --- 31 unchanged lines hidden (view full) --- 40 41/* 42 * Globals 43 */ 44hv_vmbus_connection hv_vmbus_g_connection = 45 { .connect_state = HV_DISCONNECTED, 46 .next_gpadl_handle = 0xE1E10, }; 47 | 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 --- 31 unchanged lines hidden (view full) --- 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 |
|
48/** | 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_pages); 90 91 msg->monitor_page_2 = 92 hv_get_phys_addr( 93 ((uint8_t *) hv_vmbus_g_connection.monitor_pages 94 + PAGE_SIZE)); 95 96 /** 97 * Add to list before we send the request since we may receive the 98 * response before returning from this routine 99 */ 100 mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock); 101 102 TAILQ_INSERT_TAIL( 103 &hv_vmbus_g_connection.channel_msg_anchor, 104 msg_info, 105 msg_list_entry); 106 107 mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock); 108 109 ret = hv_vmbus_post_message( 110 msg, 111 sizeof(hv_vmbus_channel_initiate_contact)); 112 113 if (ret != 0) { 114 mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock); 115 TAILQ_REMOVE( 116 &hv_vmbus_g_connection.channel_msg_anchor, 117 msg_info, 118 msg_list_entry); 119 mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock); 120 return (ret); 121 } 122 123 /** 124 * Wait for the connection response 125 */ 126 ret = sema_timedwait(&msg_info->wait_sema, 500); /* KYS 5 seconds */ 127 128 mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock); 129 TAILQ_REMOVE( 130 &hv_vmbus_g_connection.channel_msg_anchor, 131 msg_info, 132 msg_list_entry); 133 mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock); 134 135 /** 136 * Check if successful 137 */ 138 if (msg_info->response.version_response.version_supported) { 139 hv_vmbus_g_connection.connect_state = HV_CONNECTED; 140 } else { 141 ret = ECONNREFUSED; 142 } 143 144 return (ret); 145} 146 147/** |
|
49 * Send a connect request on the partition service connection 50 */ 51int 52hv_vmbus_connect(void) { 53 int ret = 0; | 148 * Send a connect request on the partition service connection 149 */ 150int 151hv_vmbus_connect(void) { 152 int ret = 0; |
153 uint32_t version; |
|
54 hv_vmbus_channel_msg_info* msg_info = NULL; | 154 hv_vmbus_channel_msg_info* msg_info = NULL; |
55 hv_vmbus_channel_initiate_contact* msg; | |
56 57 /** 58 * Make sure we are not connecting or connected 59 */ 60 if (hv_vmbus_g_connection.connect_state != HV_DISCONNECTED) { 61 return (-1); 62 } 63 --- 5 unchanged lines hidden (view full) --- 69 sema_init(&hv_vmbus_g_connection.control_sema, 1, "control_sema"); 70 71 TAILQ_INIT(&hv_vmbus_g_connection.channel_msg_anchor); 72 mtx_init(&hv_vmbus_g_connection.channel_msg_lock, "vmbus channel msg", 73 NULL, MTX_SPIN); 74 75 TAILQ_INIT(&hv_vmbus_g_connection.channel_anchor); 76 mtx_init(&hv_vmbus_g_connection.channel_lock, "vmbus channel", | 155 156 /** 157 * Make sure we are not connecting or connected 158 */ 159 if (hv_vmbus_g_connection.connect_state != HV_DISCONNECTED) { 160 return (-1); 161 } 162 --- 5 unchanged lines hidden (view full) --- 168 sema_init(&hv_vmbus_g_connection.control_sema, 1, "control_sema"); 169 170 TAILQ_INIT(&hv_vmbus_g_connection.channel_msg_anchor); 171 mtx_init(&hv_vmbus_g_connection.channel_msg_lock, "vmbus channel msg", 172 NULL, MTX_SPIN); 173 174 TAILQ_INIT(&hv_vmbus_g_connection.channel_anchor); 175 mtx_init(&hv_vmbus_g_connection.channel_lock, "vmbus channel", |
77 NULL, MTX_SPIN); | 176 NULL, MTX_DEF); |
78 79 /** 80 * Setup the vmbus event connection for channel interrupt abstraction 81 * stuff 82 */ 83 hv_vmbus_g_connection.interrupt_page = contigmalloc( 84 PAGE_SIZE, M_DEVBUF, 85 M_NOWAIT | M_ZERO, 0UL, --- 39 unchanged lines hidden (view full) --- 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) { 129 ret = ENOMEM; 130 goto cleanup; 131 } 132 | 177 178 /** 179 * Setup the vmbus event connection for channel interrupt abstraction 180 * stuff 181 */ 182 hv_vmbus_g_connection.interrupt_page = contigmalloc( 183 PAGE_SIZE, M_DEVBUF, 184 M_NOWAIT | M_ZERO, 0UL, --- 39 unchanged lines hidden (view full) --- 224 M_DEVBUF, M_NOWAIT | M_ZERO); 225 KASSERT(msg_info != NULL, 226 ("Error VMBUS: malloc failed for Initiate Contact message!")); 227 if (msg_info == NULL) { 228 ret = ENOMEM; 229 goto cleanup; 230 } 231 |
133 sema_init(&msg_info->wait_sema, 0, "Msg Info Sema"); 134 msg = (hv_vmbus_channel_initiate_contact*) msg_info->msg; 135 136 msg->header.message_type = HV_CHANNEL_MESSAGE_INITIATED_CONTACT; 137 msg->vmbus_version_requested = HV_VMBUS_REVISION_NUMBER; 138 139 msg->interrupt_page = hv_get_phys_addr( 140 hv_vmbus_g_connection.interrupt_page); 141 142 msg->monitor_page_1 = hv_get_phys_addr( 143 hv_vmbus_g_connection.monitor_pages); 144 145 msg->monitor_page_2 = 146 hv_get_phys_addr( 147 ((uint8_t *) hv_vmbus_g_connection.monitor_pages 148 + PAGE_SIZE)); 149 150 /** 151 * Add to list before we send the request since we may receive the 152 * response before returning from this routine | 232 /* 233 * Find the highest vmbus version number we can support. |
153 */ | 234 */ |
154 mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock); | 235 version = HV_VMBUS_VERSION_CURRENT; |
155 | 236 |
156 TAILQ_INSERT_TAIL( 157 &hv_vmbus_g_connection.channel_msg_anchor, 158 msg_info, 159 msg_list_entry); | 237 do { 238 ret = hv_vmbus_negotiate_version(msg_info, version); 239 if (ret == EWOULDBLOCK) { 240 /* 241 * We timed out. 242 */ 243 goto cleanup; 244 } |
160 | 245 |
161 mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock); | 246 if (hv_vmbus_g_connection.connect_state == HV_CONNECTED) 247 break; |
162 | 248 |
163 ret = hv_vmbus_post_message( 164 msg, 165 sizeof(hv_vmbus_channel_initiate_contact)); | 249 version = hv_vmbus_get_next_version(version); 250 } while (version != HV_VMBUS_VERSION_INVALID); |
166 | 251 |
167 if (ret != 0) { 168 mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock); 169 TAILQ_REMOVE( 170 &hv_vmbus_g_connection.channel_msg_anchor, 171 msg_info, 172 msg_list_entry); 173 mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock); 174 goto cleanup; 175 } | 252 hv_vmbus_protocal_version = version; 253 if (bootverbose) 254 printf("VMBUS: Portocal Version: %d.%d\n", 255 version >> 16, version & 0xFFFF); |
176 | 256 |
177 /** 178 * Wait for the connection response 179 */ 180 ret = sema_timedwait(&msg_info->wait_sema, 500); /* KYS 5 seconds */ 181 182 mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock); 183 TAILQ_REMOVE( 184 &hv_vmbus_g_connection.channel_msg_anchor, 185 msg_info, 186 msg_list_entry); 187 mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock); 188 189 /** 190 * Check if successful 191 */ 192 if (msg_info->response.version_response.version_supported) { 193 hv_vmbus_g_connection.connect_state = HV_CONNECTED; 194 } else { 195 ret = ECONNREFUSED; 196 goto cleanup; 197 } 198 | |
199 sema_destroy(&msg_info->wait_sema); 200 free(msg_info, M_DEVBUF); 201 202 return (0); 203 204 /* 205 * Cleanup after failure! 206 */ --- 74 unchanged lines hidden (view full) --- 281 hv_vmbus_channel* foundChannel = NULL; 282 283 /* 284 * TODO: 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 287 * the list. 288 */ | 257 sema_destroy(&msg_info->wait_sema); 258 free(msg_info, M_DEVBUF); 259 260 return (0); 261 262 /* 263 * Cleanup after failure! 264 */ --- 74 unchanged lines hidden (view full) --- 339 hv_vmbus_channel* foundChannel = NULL; 340 341 /* 342 * TODO: 343 * Consider optimization where relids are stored in a fixed size array 344 * and channels are accessed without the need to take this lock or search 345 * the list. 346 */ |
289 mtx_lock_spin(&hv_vmbus_g_connection.channel_lock); | 347 mtx_lock(&hv_vmbus_g_connection.channel_lock); |
290 TAILQ_FOREACH(channel, 291 &hv_vmbus_g_connection.channel_anchor, list_entry) { 292 293 if (channel->offer_msg.child_rel_id == rel_id) { 294 foundChannel = channel; 295 break; 296 } 297 } | 348 TAILQ_FOREACH(channel, 349 &hv_vmbus_g_connection.channel_anchor, list_entry) { 350 351 if (channel->offer_msg.child_rel_id == rel_id) { 352 foundChannel = channel; 353 break; 354 } 355 } |
298 mtx_unlock_spin(&hv_vmbus_g_connection.channel_lock); | 356 mtx_unlock(&hv_vmbus_g_connection.channel_lock); |
299 300 return (foundChannel); 301} 302 303/** 304 * Process a channel event notification 305 */ 306static void 307VmbusProcessChannelEvent(uint32_t relid) 308{ | 357 358 return (foundChannel); 359} 360 361/** 362 * Process a channel event notification 363 */ 364static void 365VmbusProcessChannelEvent(uint32_t relid) 366{ |
367 void* arg; 368 uint32_t bytes_to_read; |
|
309 hv_vmbus_channel* channel; | 369 hv_vmbus_channel* channel; |
370 boolean_t is_batched_reading; |
|
310 311 /** 312 * Find the channel based on this relid and invokes 313 * the channel callback to process the event 314 */ 315 316 channel = hv_vmbus_get_channel_from_rel_id(relid); 317 --- 4 unchanged lines hidden (view full) --- 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. 328 */ 329 | 371 372 /** 373 * Find the channel based on this relid and invokes 374 * the channel callback to process the event 375 */ 376 377 channel = hv_vmbus_get_channel_from_rel_id(relid); 378 --- 4 unchanged lines hidden (view full) --- 383 * To deal with the race condition where we might 384 * receive a packet while the relevant driver is 385 * being unloaded, dispatch the callback while 386 * holding the channel lock. The unloading driver 387 * will acquire the same channel lock to set the 388 * callback to NULL. This closes the window. 389 */ 390 |
330 mtx_lock(&channel->inbound_lock); | 391 /* 392 * Disable the lock due to newly added WITNESS check in r277723. 393 * Will seek other way to avoid race condition. 394 * -- whu 395 */ 396 // mtx_lock(&channel->inbound_lock); |
331 if (channel->on_channel_callback != NULL) { | 397 if (channel->on_channel_callback != NULL) { |
332 channel->on_channel_callback(channel->channel_callback_context); | 398 arg = channel->channel_callback_context; 399 is_batched_reading = channel->batched_reading; 400 /* 401 * Optimize host to guest signaling by ensuring: 402 * 1. While reading the channel, we disable interrupts from 403 * host. 404 * 2. Ensure that we process all posted messages from the host 405 * before returning from this callback. 406 * 3. Once we return, enable signaling from the host. Once this 407 * state is set we check to see if additional packets are 408 * available to read. In this case we repeat the process. 409 */ 410 do { 411 if (is_batched_reading) 412 hv_ring_buffer_read_begin(&channel->inbound); 413 414 channel->on_channel_callback(arg); 415 416 if (is_batched_reading) 417 bytes_to_read = 418 hv_ring_buffer_read_end(&channel->inbound); 419 else 420 bytes_to_read = 0; 421 } while (is_batched_reading && (bytes_to_read != 0)); |
333 } | 422 } |
334 mtx_unlock(&channel->inbound_lock); | 423 // mtx_unlock(&channel->inbound_lock); |
335} 336 | 424} 425 |
426#ifdef HV_DEBUG_INTR 427extern uint32_t hv_intr_count; 428extern uint32_t hv_vmbus_swintr_event_cpu[MAXCPU]; 429extern uint32_t hv_vmbus_intr_cpu[MAXCPU]; 430#endif 431 |
|
337/** 338 * Handler for events 339 */ 340void 341hv_vmbus_on_events(void *arg) 342{ | 432/** 433 * Handler for events 434 */ 435void 436hv_vmbus_on_events(void *arg) 437{ |
343 int dword; | |
344 int bit; | 438 int bit; |
439 int cpu; 440 int dword; 441 void *page_addr; 442 uint32_t* recv_interrupt_page = NULL; |
|
345 int rel_id; | 443 int rel_id; |
346 int maxdword = HV_MAX_NUM_CHANNELS_SUPPORTED >> 5; | 444 int maxdword; 445 hv_vmbus_synic_event_flags *event; |
347 /* int maxdword = PAGE_SIZE >> 3; */ 348 | 446 /* int maxdword = PAGE_SIZE >> 3; */ 447 |
349 /* 350 * receive size is 1/2 page and divide that by 4 bytes 351 */ | 448 cpu = (int)(long)arg; 449 KASSERT(cpu <= mp_maxid, ("VMBUS: hv_vmbus_on_events: " 450 "cpu out of range!")); |
352 | 451 |
353 uint32_t* recv_interrupt_page = 354 hv_vmbus_g_connection.recv_interrupt_page; | 452#ifdef HV_DEBUG_INTR 453 int i; 454 hv_vmbus_swintr_event_cpu[cpu]++; 455 if (hv_intr_count % 10000 == 0) { 456 printf("VMBUS: Total interrupt %d\n", hv_intr_count); 457 for (i = 0; i < mp_ncpus; i++) 458 printf("VMBUS: hw cpu[%d]: %d, event sw intr cpu[%d]: %d\n", 459 i, hv_vmbus_intr_cpu[i], i, hv_vmbus_swintr_event_cpu[i]); 460 } 461#endif |
355 | 462 |
463 if ((hv_vmbus_protocal_version == HV_VMBUS_VERSION_WS2008) || 464 (hv_vmbus_protocal_version == HV_VMBUS_VERSION_WIN7)) { 465 maxdword = HV_MAX_NUM_CHANNELS_SUPPORTED >> 5; 466 /* 467 * receive size is 1/2 page and divide that by 4 bytes 468 */ 469 recv_interrupt_page = 470 hv_vmbus_g_connection.recv_interrupt_page; 471 } else { 472 /* 473 * On Host with Win8 or above, the event page can be 474 * checked directly to get the id of the channel 475 * that has the pending interrupt. 476 */ 477 maxdword = HV_EVENT_FLAGS_DWORD_COUNT; 478 page_addr = hv_vmbus_g_context.syn_ic_event_page[cpu]; 479 event = (hv_vmbus_synic_event_flags *) 480 page_addr + HV_VMBUS_MESSAGE_SINT; 481 recv_interrupt_page = event->flags32; 482 } 483 |
|
356 /* 357 * Check events 358 */ 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, --- 47 unchanged lines hidden (view full) --- 411 412 return (ret); 413} 414 415/** 416 * Send an event notification to the parent 417 */ 418int | 484 /* 485 * Check events 486 */ 487 if (recv_interrupt_page != NULL) { 488 for (dword = 0; dword < maxdword; dword++) { 489 if (recv_interrupt_page[dword]) { 490 for (bit = 0; bit < 32; bit++) { 491 if (synch_test_and_clear_bit(bit, --- 47 unchanged lines hidden (view full) --- 539 540 return (ret); 541} 542 543/** 544 * Send an event notification to the parent 545 */ 546int |
419hv_vmbus_set_event(uint32_t child_rel_id) { | 547hv_vmbus_set_event(hv_vmbus_channel *channel) { |
420 int ret = 0; | 548 int ret = 0; |
549 uint32_t child_rel_id = channel->offer_msg.child_rel_id; |
|
421 422 /* Each uint32_t represents 32 channels */ 423 424 synch_set_bit(child_rel_id & 31, 425 (((uint32_t *)hv_vmbus_g_connection.send_interrupt_page 426 + (child_rel_id >> 5)))); | 550 551 /* Each uint32_t represents 32 channels */ 552 553 synch_set_bit(child_rel_id & 31, 554 (((uint32_t *)hv_vmbus_g_connection.send_interrupt_page 555 + (child_rel_id >> 5)))); |
427 ret = hv_vmbus_signal_event(); | 556 ret = hv_vmbus_signal_event(channel->signal_event_param); |
428 429 return (ret); 430} | 557 558 return (ret); 559} |
431 | |