Deleted Added
full compact
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}