hv_channel_mgmt.c (255524) | hv_channel_mgmt.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 --- 36 unchanged lines hidden (view full) --- 45static void vmbus_channel_on_offer(hv_vmbus_channel_msg_header* hdr); 46static void vmbus_channel_on_open_result(hv_vmbus_channel_msg_header* hdr); 47static void vmbus_channel_on_offer_rescind(hv_vmbus_channel_msg_header* hdr); 48static void vmbus_channel_on_gpadl_created(hv_vmbus_channel_msg_header* hdr); 49static void vmbus_channel_on_gpadl_torndown(hv_vmbus_channel_msg_header* hdr); 50static void vmbus_channel_on_offers_delivered(hv_vmbus_channel_msg_header* hdr); 51static void vmbus_channel_on_version_response(hv_vmbus_channel_msg_header* hdr); 52static void vmbus_channel_process_offer(void *context); | 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 --- 36 unchanged lines hidden (view full) --- 45static void vmbus_channel_on_offer(hv_vmbus_channel_msg_header* hdr); 46static void vmbus_channel_on_open_result(hv_vmbus_channel_msg_header* hdr); 47static void vmbus_channel_on_offer_rescind(hv_vmbus_channel_msg_header* hdr); 48static void vmbus_channel_on_gpadl_created(hv_vmbus_channel_msg_header* hdr); 49static void vmbus_channel_on_gpadl_torndown(hv_vmbus_channel_msg_header* hdr); 50static void vmbus_channel_on_offers_delivered(hv_vmbus_channel_msg_header* hdr); 51static void vmbus_channel_on_version_response(hv_vmbus_channel_msg_header* hdr); 52static void vmbus_channel_process_offer(void *context); |
53struct hv_vmbus_channel* 54 vmbus_select_outgoing_channel(struct hv_vmbus_channel *promary); |
|
53 54/** 55 * Channel message dispatch table 56 */ 57hv_vmbus_channel_msg_table_entry 58 g_channel_message_table[HV_CHANNEL_MESSAGE_COUNT] = { 59 { HV_CHANNEL_MESSAGE_INVALID, NULL }, 60 { HV_CHANNEL_MESSAGE_OFFER_CHANNEL, vmbus_channel_on_offer }, --- 167 unchanged lines hidden (view full) --- 228 sizeof(hv_vmbus_channel), 229 M_DEVBUF, 230 M_NOWAIT | M_ZERO); 231 KASSERT(channel != NULL, ("Error VMBUS: Failed to allocate channel!")); 232 if (channel == NULL) 233 return (NULL); 234 235 mtx_init(&channel->inbound_lock, "channel inbound", NULL, MTX_DEF); | 55 56/** 57 * Channel message dispatch table 58 */ 59hv_vmbus_channel_msg_table_entry 60 g_channel_message_table[HV_CHANNEL_MESSAGE_COUNT] = { 61 { HV_CHANNEL_MESSAGE_INVALID, NULL }, 62 { HV_CHANNEL_MESSAGE_OFFER_CHANNEL, vmbus_channel_on_offer }, --- 167 unchanged lines hidden (view full) --- 230 sizeof(hv_vmbus_channel), 231 M_DEVBUF, 232 M_NOWAIT | M_ZERO); 233 KASSERT(channel != NULL, ("Error VMBUS: Failed to allocate channel!")); 234 if (channel == NULL) 235 return (NULL); 236 237 mtx_init(&channel->inbound_lock, "channel inbound", NULL, MTX_DEF); |
238 mtx_init(&channel->sc_lock, "vmbus multi channel", NULL, MTX_DEF); |
|
236 | 239 |
240 TAILQ_INIT(&channel->sc_list_anchor); 241 |
|
237 channel->control_work_queue = hv_work_queue_create("control"); 238 239 if (channel->control_work_queue == NULL) { 240 mtx_destroy(&channel->inbound_lock); 241 free(channel, M_DEVBUF); 242 return (NULL); 243 } 244 --- 12 unchanged lines hidden (view full) --- 257} 258 259/** 260 * @brief Release the resources used by the vmbus channel object 261 */ 262void 263hv_vmbus_free_vmbus_channel(hv_vmbus_channel* channel) 264{ | 242 channel->control_work_queue = hv_work_queue_create("control"); 243 244 if (channel->control_work_queue == NULL) { 245 mtx_destroy(&channel->inbound_lock); 246 free(channel, M_DEVBUF); 247 return (NULL); 248 } 249 --- 12 unchanged lines hidden (view full) --- 262} 263 264/** 265 * @brief Release the resources used by the vmbus channel object 266 */ 267void 268hv_vmbus_free_vmbus_channel(hv_vmbus_channel* channel) 269{ |
270 mtx_destroy(&channel->sc_lock); |
|
265 mtx_destroy(&channel->inbound_lock); 266 /* 267 * We have to release the channel's workqueue/thread in 268 * the vmbus's workqueue/thread context 269 * ie we can't destroy ourselves 270 */ 271 hv_queue_work_item(hv_vmbus_g_connection.work_queue, 272 ReleaseVmbusChannel, (void *) channel); 273} 274 275/** 276 * @brief Process the offer by creating a channel/device 277 * associated with this offer 278 */ 279static void 280vmbus_channel_process_offer(void *context) 281{ | 271 mtx_destroy(&channel->inbound_lock); 272 /* 273 * We have to release the channel's workqueue/thread in 274 * the vmbus's workqueue/thread context 275 * ie we can't destroy ourselves 276 */ 277 hv_queue_work_item(hv_vmbus_g_connection.work_queue, 278 ReleaseVmbusChannel, (void *) channel); 279} 280 281/** 282 * @brief Process the offer by creating a channel/device 283 * associated with this offer 284 */ 285static void 286vmbus_channel_process_offer(void *context) 287{ |
282 int ret; | |
283 hv_vmbus_channel* new_channel; 284 boolean_t f_new; 285 hv_vmbus_channel* channel; | 288 hv_vmbus_channel* new_channel; 289 boolean_t f_new; 290 hv_vmbus_channel* channel; |
291 int ret; |
|
286 287 new_channel = (hv_vmbus_channel*) context; 288 f_new = TRUE; 289 channel = NULL; 290 291 /* 292 * Make sure this is a new offer 293 */ | 292 293 new_channel = (hv_vmbus_channel*) context; 294 f_new = TRUE; 295 channel = NULL; 296 297 /* 298 * Make sure this is a new offer 299 */ |
294 mtx_lock_spin(&hv_vmbus_g_connection.channel_lock); | 300 mtx_lock(&hv_vmbus_g_connection.channel_lock); |
295 296 TAILQ_FOREACH(channel, &hv_vmbus_g_connection.channel_anchor, 297 list_entry) 298 { | 301 302 TAILQ_FOREACH(channel, &hv_vmbus_g_connection.channel_anchor, 303 list_entry) 304 { |
299 if (!memcmp( 300 &channel->offer_msg.offer.interface_type, 301 &new_channel->offer_msg.offer.interface_type, 302 sizeof(hv_guid)) 303 && !memcmp( 304 &channel->offer_msg.offer.interface_instance, | 305 if (memcmp(&channel->offer_msg.offer.interface_type, 306 &new_channel->offer_msg.offer.interface_type, 307 sizeof(hv_guid)) == 0 && 308 memcmp(&channel->offer_msg.offer.interface_instance, |
305 &new_channel->offer_msg.offer.interface_instance, | 309 &new_channel->offer_msg.offer.interface_instance, |
306 sizeof(hv_guid))) { 307 f_new = FALSE; 308 break; 309 } | 310 sizeof(hv_guid)) == 0) { 311 f_new = FALSE; 312 break; 313 } |
310 } 311 312 if (f_new) { | 314 } 315 316 if (f_new) { |
313 /* Insert at tail */ 314 TAILQ_INSERT_TAIL( 315 &hv_vmbus_g_connection.channel_anchor, 316 new_channel, 317 list_entry); | 317 /* Insert at tail */ 318 TAILQ_INSERT_TAIL( 319 &hv_vmbus_g_connection.channel_anchor, 320 new_channel, 321 list_entry); |
318 } | 322 } |
319 mtx_unlock_spin(&hv_vmbus_g_connection.channel_lock); | 323 mtx_unlock(&hv_vmbus_g_connection.channel_lock); |
320 | 324 |
325 /*XXX add new channel to percpu_list */ 326 |
|
321 if (!f_new) { | 327 if (!f_new) { |
328 /* 329 * Check if this is a sub channel. 330 */ 331 if (new_channel->offer_msg.offer.sub_channel_index != 0) { 332 /* 333 * It is a sub channel offer, process it. 334 */ 335 new_channel->primary_channel = channel; 336 mtx_lock(&channel->sc_lock); 337 TAILQ_INSERT_TAIL( 338 &channel->sc_list_anchor, 339 new_channel, 340 sc_list_entry); 341 mtx_unlock(&channel->sc_lock); 342 343 /* Insert new channel into channel_anchor. */ 344 printf("Storvsc get multi-channel offer, rel=%u.\n", 345 new_channel->offer_msg.child_rel_id); 346 mtx_lock(&hv_vmbus_g_connection.channel_lock); 347 TAILQ_INSERT_TAIL(&hv_vmbus_g_connection.channel_anchor, 348 new_channel, list_entry); 349 mtx_unlock(&hv_vmbus_g_connection.channel_lock); 350 351 if(bootverbose) 352 printf("VMBUS: new multi-channel offer <%p>.\n", 353 new_channel); 354 355 /*XXX add it to percpu_list */ 356 357 new_channel->state = HV_CHANNEL_OPEN_STATE; 358 if (channel->sc_creation_callback != NULL) { 359 channel->sc_creation_callback(new_channel); 360 } 361 return; 362 } 363 |
|
322 hv_vmbus_free_vmbus_channel(new_channel); 323 return; 324 } 325 | 364 hv_vmbus_free_vmbus_channel(new_channel); 365 return; 366 } 367 |
368 new_channel->state = HV_CHANNEL_OPEN_STATE; 369 |
|
326 /* 327 * Start the process of binding this offer to the driver 328 * (We need to set the device field before calling 329 * hv_vmbus_child_device_add()) 330 */ 331 new_channel->device = hv_vmbus_child_device_create( 332 new_channel->offer_msg.offer.interface_type, 333 new_channel->offer_msg.offer.interface_instance, new_channel); 334 335 /* | 370 /* 371 * Start the process of binding this offer to the driver 372 * (We need to set the device field before calling 373 * hv_vmbus_child_device_add()) 374 */ 375 new_channel->device = hv_vmbus_child_device_create( 376 new_channel->offer_msg.offer.interface_type, 377 new_channel->offer_msg.offer.interface_instance, new_channel); 378 379 /* |
336 * TODO - the HV_CHANNEL_OPEN_STATE flag should not be set below 337 * but in the "open" channel request. The ret != 0 logic below 338 * doesn't take into account that a channel 339 * may have been opened successfully 340 */ 341 342 /* | |
343 * Add the new device to the bus. This will kick off device-driver 344 * binding which eventually invokes the device driver's AddDevice() 345 * method. 346 */ 347 ret = hv_vmbus_child_device_register(new_channel->device); 348 if (ret != 0) { | 380 * Add the new device to the bus. This will kick off device-driver 381 * binding which eventually invokes the device driver's AddDevice() 382 * method. 383 */ 384 ret = hv_vmbus_child_device_register(new_channel->device); 385 if (ret != 0) { |
349 mtx_lock_spin(&hv_vmbus_g_connection.channel_lock); 350 TAILQ_REMOVE( 351 &hv_vmbus_g_connection.channel_anchor, 352 new_channel, 353 list_entry); 354 mtx_unlock_spin(&hv_vmbus_g_connection.channel_lock); 355 hv_vmbus_free_vmbus_channel(new_channel); 356 } else { 357 /* 358 * This state is used to indicate a successful open 359 * so that when we do close the channel normally, 360 * we can clean up properly 361 */ 362 new_channel->state = HV_CHANNEL_OPEN_STATE; | 386 mtx_lock(&hv_vmbus_g_connection.channel_lock); 387 TAILQ_REMOVE( 388 &hv_vmbus_g_connection.channel_anchor, 389 new_channel, 390 list_entry); 391 mtx_unlock(&hv_vmbus_g_connection.channel_lock); 392 hv_vmbus_free_vmbus_channel(new_channel); 393 } 394} |
363 | 395 |
396/** 397 * Array of device guids that are performance critical. We try to distribute 398 * the interrupt load for these devices across all online cpus. 399 */ 400static const hv_guid high_perf_devices[] = { 401 {HV_NIC_GUID, }, 402 {HV_IDE_GUID, }, 403 {HV_SCSI_GUID, }, 404}; 405 406enum { 407 PERF_CHN_NIC = 0, 408 PERF_CHN_IDE, 409 PERF_CHN_SCSI, 410 MAX_PERF_CHN, 411}; 412 413/* 414 * We use this static number to distribute the channel interrupt load. 415 */ 416static uint32_t next_vcpu; 417 418/** 419 * Starting with Win8, we can statically distribute the incoming 420 * channel interrupt load by binding a channel to VCPU. We 421 * implement here a simple round robin scheme for distributing 422 * the interrupt load. 423 * We will bind channels that are not performance critical to cpu 0 and 424 * performance critical channels (IDE, SCSI and Network) will be uniformly 425 * distributed across all available CPUs. 426 */ 427static void 428vmbus_channel_select_cpu(hv_vmbus_channel *channel, hv_guid *guid) 429{ 430 uint32_t current_cpu; 431 int i; 432 boolean_t is_perf_channel = FALSE; 433 434 for (i = PERF_CHN_NIC; i < MAX_PERF_CHN; i++) { 435 if (memcmp(guid->data, high_perf_devices[i].data, 436 sizeof(hv_guid)) == 0) { 437 is_perf_channel = TRUE; 438 break; 439 } |
|
364 } | 440 } |
441 442 if ((hv_vmbus_protocal_version == HV_VMBUS_VERSION_WS2008) || 443 (hv_vmbus_protocal_version == HV_VMBUS_VERSION_WIN7) || 444 (!is_perf_channel)) { 445 /* Host's view of guest cpu */ 446 channel->target_vcpu = 0; 447 /* Guest's own view of cpu */ 448 channel->target_cpu = 0; 449 return; 450 } 451 /* mp_ncpus should have the number cpus currently online */ 452 current_cpu = (++next_vcpu % mp_ncpus); 453 channel->target_cpu = current_cpu; 454 channel->target_vcpu = 455 hv_vmbus_g_context.hv_vcpu_index[current_cpu]; 456 if (bootverbose) 457 printf("VMBUS: Total online cpus %d, assign perf channel %d " 458 "to vcpu %d, cpu %d\n", mp_ncpus, i, channel->target_vcpu, 459 current_cpu); |
|
365} 366 367/** 368 * @brief Handler for channel offers from Hyper-V/Azure 369 * 370 * Handler for channel offers from vmbus in parent partition. We ignore 371 * all offers except network and storage offers. For each network and storage 372 * offers, we create a channel object and queue a work item to the channel --- 13 unchanged lines hidden (view full) --- 386 guidType = &offer->offer.interface_type; 387 guidInstance = &offer->offer.interface_instance; 388 389 /* Allocate the channel object and save this offer */ 390 new_channel = hv_vmbus_allocate_channel(); 391 if (new_channel == NULL) 392 return; 393 | 460} 461 462/** 463 * @brief Handler for channel offers from Hyper-V/Azure 464 * 465 * Handler for channel offers from vmbus in parent partition. We ignore 466 * all offers except network and storage offers. For each network and storage 467 * offers, we create a channel object and queue a work item to the channel --- 13 unchanged lines hidden (view full) --- 481 guidType = &offer->offer.interface_type; 482 guidInstance = &offer->offer.interface_instance; 483 484 /* Allocate the channel object and save this offer */ 485 new_channel = hv_vmbus_allocate_channel(); 486 if (new_channel == NULL) 487 return; 488 |
489 /* 490 * By default we setup state to enable batched 491 * reading. A specific service can choose to 492 * disable this prior to opening the channel. 493 */ 494 new_channel->batched_reading = TRUE; 495 496 new_channel->signal_event_param = 497 (hv_vmbus_input_signal_event *) 498 (HV_ALIGN_UP((unsigned long) 499 &new_channel->signal_event_buffer, 500 HV_HYPERCALL_PARAM_ALIGN)); 501 502 new_channel->signal_event_param->connection_id.as_uint32_t = 0; 503 new_channel->signal_event_param->connection_id.u.id = 504 HV_VMBUS_EVENT_CONNECTION_ID; 505 new_channel->signal_event_param->flag_number = 0; 506 new_channel->signal_event_param->rsvd_z = 0; 507 508 if (hv_vmbus_protocal_version != HV_VMBUS_VERSION_WS2008) { 509 new_channel->is_dedicated_interrupt = 510 (offer->is_dedicated_interrupt != 0); 511 new_channel->signal_event_param->connection_id.u.id = 512 offer->connection_id; 513 } 514 515 /* 516 * Bind the channel to a chosen cpu. 517 */ 518 vmbus_channel_select_cpu(new_channel, 519 &offer->offer.interface_type); 520 |
|
394 memcpy(&new_channel->offer_msg, offer, 395 sizeof(hv_vmbus_channel_offer_channel)); 396 new_channel->monitor_group = (uint8_t) offer->monitor_id / 32; 397 new_channel->monitor_bit = (uint8_t) offer->monitor_id % 32; 398 399 /* TODO: Make sure the offer comes from our parent partition */ 400 hv_queue_work_item( 401 new_channel->control_work_queue, --- 259 unchanged lines hidden (view full) --- 661/** 662 * @brief Release channels that are unattached/unconnected (i.e., no drivers associated) 663 */ 664void 665hv_vmbus_release_unattached_channels(void) 666{ 667 hv_vmbus_channel *channel; 668 | 521 memcpy(&new_channel->offer_msg, offer, 522 sizeof(hv_vmbus_channel_offer_channel)); 523 new_channel->monitor_group = (uint8_t) offer->monitor_id / 32; 524 new_channel->monitor_bit = (uint8_t) offer->monitor_id % 32; 525 526 /* TODO: Make sure the offer comes from our parent partition */ 527 hv_queue_work_item( 528 new_channel->control_work_queue, --- 259 unchanged lines hidden (view full) --- 788/** 789 * @brief Release channels that are unattached/unconnected (i.e., no drivers associated) 790 */ 791void 792hv_vmbus_release_unattached_channels(void) 793{ 794 hv_vmbus_channel *channel; 795 |
669 mtx_lock_spin(&hv_vmbus_g_connection.channel_lock); | 796 mtx_lock(&hv_vmbus_g_connection.channel_lock); |
670 671 while (!TAILQ_EMPTY(&hv_vmbus_g_connection.channel_anchor)) { 672 channel = TAILQ_FIRST(&hv_vmbus_g_connection.channel_anchor); 673 TAILQ_REMOVE(&hv_vmbus_g_connection.channel_anchor, 674 channel, list_entry); 675 676 hv_vmbus_child_device_unregister(channel->device); 677 hv_vmbus_free_vmbus_channel(channel); 678 } | 797 798 while (!TAILQ_EMPTY(&hv_vmbus_g_connection.channel_anchor)) { 799 channel = TAILQ_FIRST(&hv_vmbus_g_connection.channel_anchor); 800 TAILQ_REMOVE(&hv_vmbus_g_connection.channel_anchor, 801 channel, list_entry); 802 803 hv_vmbus_child_device_unregister(channel->device); 804 hv_vmbus_free_vmbus_channel(channel); 805 } |
679 mtx_unlock_spin(&hv_vmbus_g_connection.channel_lock); | 806 mtx_unlock(&hv_vmbus_g_connection.channel_lock); |
680} | 807} |
808 809/** 810 * @brief Select the best outgoing channel 811 * 812 * The channel whose vcpu binding is closest to the currect vcpu will 813 * be selected. 814 * If no multi-channel, always select primary channel 815 * 816 * @param primary - primary channel 817 */ 818struct hv_vmbus_channel * 819vmbus_select_outgoing_channel(struct hv_vmbus_channel *primary) 820{ 821 hv_vmbus_channel *new_channel = NULL; 822 hv_vmbus_channel *outgoing_channel = primary; 823 int old_cpu_distance = 0; 824 int new_cpu_distance = 0; 825 int cur_vcpu = 0; 826 int smp_pro_id = PCPU_GET(cpuid); 827 828 if (TAILQ_EMPTY(&primary->sc_list_anchor)) { 829 return outgoing_channel; 830 } 831 832 if (smp_pro_id >= MAXCPU) { 833 return outgoing_channel; 834 } 835 836 cur_vcpu = hv_vmbus_g_context.hv_vcpu_index[smp_pro_id]; 837 838 TAILQ_FOREACH(new_channel, &primary->sc_list_anchor, sc_list_entry) { 839 if (new_channel->state != HV_CHANNEL_OPENED_STATE){ 840 continue; 841 } 842 843 if (new_channel->target_vcpu == cur_vcpu){ 844 return new_channel; 845 } 846 847 old_cpu_distance = ((outgoing_channel->target_vcpu > cur_vcpu) ? 848 (outgoing_channel->target_vcpu - cur_vcpu) : 849 (cur_vcpu - outgoing_channel->target_vcpu)); 850 851 new_cpu_distance = ((new_channel->target_vcpu > cur_vcpu) ? 852 (new_channel->target_vcpu - cur_vcpu) : 853 (cur_vcpu - new_channel->target_vcpu)); 854 855 if (old_cpu_distance < new_cpu_distance) { 856 continue; 857 } 858 859 outgoing_channel = new_channel; 860 } 861 862 return(outgoing_channel); 863} |
|