hv_vmbus_drv_freebsd.c revision 293820
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 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/* 30 * VM Bus Driver Implementation 31 */ 32#include <sys/cdefs.h> 33__FBSDID("$FreeBSD: stable/10/sys/dev/hyperv/vmbus/hv_vmbus_drv_freebsd.c 293820 2016-01-13 08:22:53Z delphij $"); 34 35#include <sys/param.h> 36#include <sys/bus.h> 37#include <sys/kernel.h> 38#include <sys/lock.h> 39#include <sys/malloc.h> 40#include <sys/module.h> 41#include <sys/sysctl.h> 42#include <sys/syslog.h> 43#include <sys/systm.h> 44#include <sys/rtprio.h> 45#include <sys/interrupt.h> 46#include <sys/sx.h> 47#include <sys/taskqueue.h> 48#include <sys/mutex.h> 49#include <sys/smp.h> 50 51#include <machine/resource.h> 52#include <sys/rman.h> 53 54#include <machine/stdarg.h> 55#include <machine/intr_machdep.h> 56#include <machine/md_var.h> 57#include <machine/segments.h> 58#include <sys/pcpu.h> 59#include <machine/apicvar.h> 60 61#include "hv_vmbus_priv.h" 62 63 64#define VMBUS_IRQ 0x5 65 66static device_t vmbus_devp; 67static int vmbus_inited; 68static hv_setup_args setup_args; /* only CPU 0 supported at this time */ 69 70/** 71 * @brief Software interrupt thread routine to handle channel messages from 72 * the hypervisor. 73 */ 74static void 75vmbus_msg_swintr(void *arg) 76{ 77 int cpu; 78 void* page_addr; 79 hv_vmbus_channel_msg_header *hdr; 80 hv_vmbus_channel_msg_table_entry *entry; 81 hv_vmbus_channel_msg_type msg_type; 82 hv_vmbus_message* msg; 83 hv_vmbus_message* copied; 84 static bool warned = false; 85 86 cpu = (int)(long)arg; 87 KASSERT(cpu <= mp_maxid, ("VMBUS: vmbus_msg_swintr: " 88 "cpu out of range!")); 89 90 page_addr = hv_vmbus_g_context.syn_ic_msg_page[cpu]; 91 msg = (hv_vmbus_message*) page_addr + HV_VMBUS_MESSAGE_SINT; 92 93 for (;;) { 94 if (msg->header.message_type == HV_MESSAGE_TYPE_NONE) 95 break; /* no message */ 96 97 hdr = (hv_vmbus_channel_msg_header *)msg->u.payload; 98 msg_type = hdr->message_type; 99 100 if (msg_type >= HV_CHANNEL_MESSAGE_COUNT && !warned) { 101 warned = true; 102 printf("VMBUS: unknown message type = %d\n", msg_type); 103 goto handled; 104 } 105 106 entry = &g_channel_message_table[msg_type]; 107 108 if (entry->handler_no_sleep) 109 entry->messageHandler(hdr); 110 else { 111 112 copied = malloc(sizeof(hv_vmbus_message), 113 M_DEVBUF, M_NOWAIT); 114 KASSERT(copied != NULL, 115 ("Error VMBUS: malloc failed to allocate" 116 " hv_vmbus_message!")); 117 if (copied == NULL) 118 continue; 119 120 memcpy(copied, msg, sizeof(hv_vmbus_message)); 121 hv_queue_work_item(hv_vmbus_g_connection.work_queue, 122 hv_vmbus_on_channel_message, 123 copied); 124 } 125handled: 126 msg->header.message_type = HV_MESSAGE_TYPE_NONE; 127 128 /* 129 * Make sure the write to message_type (ie set to 130 * HV_MESSAGE_TYPE_NONE) happens before we read the 131 * message_pending and EOMing. Otherwise, the EOMing will 132 * not deliver any more messages 133 * since there is no empty slot 134 */ 135 wmb(); 136 137 if (msg->header.message_flags.u.message_pending) { 138 /* 139 * This will cause message queue rescan to possibly 140 * deliver another msg from the hypervisor 141 */ 142 wrmsr(HV_X64_MSR_EOM, 0); 143 } 144 } 145} 146 147/** 148 * @brief Interrupt filter routine for VMBUS. 149 * 150 * The purpose of this routine is to determine the type of VMBUS protocol 151 * message to process - an event or a channel message. 152 */ 153static inline int 154hv_vmbus_isr(void *unused) 155{ 156 int cpu; 157 hv_vmbus_message* msg; 158 hv_vmbus_synic_event_flags* event; 159 void* page_addr; 160 161 cpu = PCPU_GET(cpuid); 162 163 /* 164 * The Windows team has advised that we check for events 165 * before checking for messages. This is the way they do it 166 * in Windows when running as a guest in Hyper-V 167 */ 168 169 page_addr = hv_vmbus_g_context.syn_ic_event_page[cpu]; 170 event = (hv_vmbus_synic_event_flags*) 171 page_addr + HV_VMBUS_MESSAGE_SINT; 172 173 if ((hv_vmbus_protocal_version == HV_VMBUS_VERSION_WS2008) || 174 (hv_vmbus_protocal_version == HV_VMBUS_VERSION_WIN7)) { 175 /* Since we are a child, we only need to check bit 0 */ 176 if (synch_test_and_clear_bit(0, &event->flags32[0])) { 177 swi_sched(hv_vmbus_g_context.event_swintr[cpu], 0); 178 } 179 } else { 180 /* 181 * On host with Win8 or above, we can directly look at 182 * the event page. If bit n is set, we have an interrupt 183 * on the channel with id n. 184 * Directly schedule the event software interrupt on 185 * current cpu. 186 */ 187 swi_sched(hv_vmbus_g_context.event_swintr[cpu], 0); 188 } 189 190 /* Check if there are actual msgs to be process */ 191 page_addr = hv_vmbus_g_context.syn_ic_msg_page[cpu]; 192 msg = (hv_vmbus_message*) page_addr + HV_VMBUS_MESSAGE_SINT; 193 194 if (msg->header.message_type != HV_MESSAGE_TYPE_NONE) { 195 swi_sched(hv_vmbus_g_context.msg_swintr[cpu], 0); 196 } 197 198 return FILTER_HANDLED; 199} 200 201#ifdef HV_DEBUG_INTR 202uint32_t hv_intr_count = 0; 203#endif 204uint32_t hv_vmbus_swintr_event_cpu[MAXCPU]; 205uint32_t hv_vmbus_intr_cpu[MAXCPU]; 206 207void 208hv_vector_handler(struct trapframe *trap_frame) 209{ 210#ifdef HV_DEBUG_INTR 211 int cpu; 212#endif 213 214 /* 215 * Disable preemption. 216 */ 217 critical_enter(); 218 219#ifdef HV_DEBUG_INTR 220 /* 221 * Do a little interrupt counting. 222 */ 223 cpu = PCPU_GET(cpuid); 224 hv_vmbus_intr_cpu[cpu]++; 225 hv_intr_count++; 226#endif 227 228 hv_vmbus_isr(NULL); 229 230 /* 231 * Enable preemption. 232 */ 233 critical_exit(); 234} 235 236static int 237vmbus_read_ivar( 238 device_t dev, 239 device_t child, 240 int index, 241 uintptr_t* result) 242{ 243 struct hv_device *child_dev_ctx = device_get_ivars(child); 244 245 switch (index) { 246 247 case HV_VMBUS_IVAR_TYPE: 248 *result = (uintptr_t) &child_dev_ctx->class_id; 249 return (0); 250 case HV_VMBUS_IVAR_INSTANCE: 251 *result = (uintptr_t) &child_dev_ctx->device_id; 252 return (0); 253 case HV_VMBUS_IVAR_DEVCTX: 254 *result = (uintptr_t) child_dev_ctx; 255 return (0); 256 case HV_VMBUS_IVAR_NODE: 257 *result = (uintptr_t) child_dev_ctx->device; 258 return (0); 259 } 260 return (ENOENT); 261} 262 263static int 264vmbus_write_ivar( 265 device_t dev, 266 device_t child, 267 int index, 268 uintptr_t value) 269{ 270 switch (index) { 271 272 case HV_VMBUS_IVAR_TYPE: 273 case HV_VMBUS_IVAR_INSTANCE: 274 case HV_VMBUS_IVAR_DEVCTX: 275 case HV_VMBUS_IVAR_NODE: 276 /* read-only */ 277 return (EINVAL); 278 } 279 return (ENOENT); 280} 281 282struct hv_device* 283hv_vmbus_child_device_create( 284 hv_guid type, 285 hv_guid instance, 286 hv_vmbus_channel* channel) 287{ 288 hv_device* child_dev; 289 290 /* 291 * Allocate the new child device 292 */ 293 child_dev = malloc(sizeof(hv_device), M_DEVBUF, 294 M_NOWAIT | M_ZERO); 295 KASSERT(child_dev != NULL, 296 ("Error VMBUS: malloc failed to allocate hv_device!")); 297 298 if (child_dev == NULL) 299 return (NULL); 300 301 child_dev->channel = channel; 302 memcpy(&child_dev->class_id, &type, sizeof(hv_guid)); 303 memcpy(&child_dev->device_id, &instance, sizeof(hv_guid)); 304 305 return (child_dev); 306} 307 308static void 309print_dev_guid(struct hv_device *dev) 310{ 311 int i; 312 unsigned char guid_name[100]; 313 for (i = 0; i < 32; i += 2) 314 sprintf(&guid_name[i], "%02x", dev->class_id.data[i / 2]); 315 if(bootverbose) 316 printf("VMBUS: Class ID: %s\n", guid_name); 317} 318 319int 320hv_vmbus_child_device_register(struct hv_device *child_dev) 321{ 322 device_t child; 323 int ret = 0; 324 325 print_dev_guid(child_dev); 326 327 328 child = device_add_child(vmbus_devp, NULL, -1); 329 child_dev->device = child; 330 device_set_ivars(child, child_dev); 331 332 mtx_lock(&Giant); 333 ret = device_probe_and_attach(child); 334 mtx_unlock(&Giant); 335 336 return (0); 337} 338 339int 340hv_vmbus_child_device_unregister(struct hv_device *child_dev) 341{ 342 int ret = 0; 343 /* 344 * XXXKYS: Ensure that this is the opposite of 345 * device_add_child() 346 */ 347 mtx_lock(&Giant); 348 ret = device_delete_child(vmbus_devp, child_dev->device); 349 mtx_unlock(&Giant); 350 return(ret); 351} 352 353static void 354vmbus_identify(driver_t *driver, device_t parent) 355{ 356 if (!hv_vmbus_query_hypervisor_presence()) 357 return; 358 359 vm_guest = VM_GUEST_HV; 360 361 BUS_ADD_CHILD(parent, 0, "vmbus", 0); 362} 363 364static int 365vmbus_probe(device_t dev) { 366 if(bootverbose) 367 device_printf(dev, "VMBUS: probe\n"); 368 369 device_set_desc(dev, "Vmbus Devices"); 370 371 return (BUS_PROBE_NOWILDCARD); 372} 373 374#ifdef HYPERV 375extern inthand_t IDTVEC(rsvd), IDTVEC(hv_vmbus_callback); 376 377/** 378 * @brief Find a free IDT slot and setup the interrupt handler. 379 */ 380static int 381vmbus_vector_alloc(void) 382{ 383 int vector; 384 uintptr_t func; 385 struct gate_descriptor *ip; 386 387 /* 388 * Search backwards form the highest IDT vector available for use 389 * as vmbus channel callback vector. We install 'hv_vmbus_callback' 390 * handler at that vector and use it to interrupt vcpus. 391 */ 392 vector = APIC_SPURIOUS_INT; 393 while (--vector >= APIC_IPI_INTS) { 394 ip = &idt[vector]; 395 func = ((long)ip->gd_hioffset << 16 | ip->gd_looffset); 396 if (func == (uintptr_t)&IDTVEC(rsvd)) { 397#ifdef __i386__ 398 setidt(vector , IDTVEC(hv_vmbus_callback), SDT_SYS386IGT, 399 SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); 400#else 401 setidt(vector , IDTVEC(hv_vmbus_callback), SDT_SYSIGT, 402 SEL_KPL, 0); 403#endif 404 405 return (vector); 406 } 407 } 408 return (0); 409} 410 411/** 412 * @brief Restore the IDT slot to rsvd. 413 */ 414static void 415vmbus_vector_free(int vector) 416{ 417 uintptr_t func; 418 struct gate_descriptor *ip; 419 420 if (vector == 0) 421 return; 422 423 KASSERT(vector >= APIC_IPI_INTS && vector < APIC_SPURIOUS_INT, 424 ("invalid vector %d", vector)); 425 426 ip = &idt[vector]; 427 func = ((long)ip->gd_hioffset << 16 | ip->gd_looffset); 428 KASSERT(func == (uintptr_t)&IDTVEC(hv_vmbus_callback), 429 ("invalid vector %d", vector)); 430 431 setidt(vector, IDTVEC(rsvd), SDT_SYSIGT, SEL_KPL, 0); 432} 433 434#else /* HYPERV */ 435 436static int 437vmbus_vector_alloc(void) 438{ 439 return(0); 440} 441 442static void 443vmbus_vector_free(int vector) 444{ 445} 446 447#endif /* HYPERV */ 448 449/** 450 * @brief Main vmbus driver initialization routine. 451 * 452 * Here, we 453 * - initialize the vmbus driver context 454 * - setup various driver entry points 455 * - invoke the vmbus hv main init routine 456 * - get the irq resource 457 * - invoke the vmbus to add the vmbus root device 458 * - setup the vmbus root device 459 * - retrieve the channel offers 460 */ 461static int 462vmbus_bus_init(void) 463{ 464 int i, j, n, ret; 465 466 if (vmbus_inited) 467 return (0); 468 469 vmbus_inited = 1; 470 471 ret = hv_vmbus_init(); 472 473 if (ret) { 474 if(bootverbose) 475 printf("Error VMBUS: Hypervisor Initialization Failed!\n"); 476 return (ret); 477 } 478 479 /* 480 * Find a free IDT slot for vmbus callback. 481 */ 482 hv_vmbus_g_context.hv_cb_vector = vmbus_vector_alloc(); 483 484 if (hv_vmbus_g_context.hv_cb_vector == 0) { 485 if(bootverbose) 486 printf("Error VMBUS: Cannot find free IDT slot for " 487 "vmbus callback!\n"); 488 goto cleanup; 489 } 490 491 if(bootverbose) 492 printf("VMBUS: vmbus callback vector %d\n", 493 hv_vmbus_g_context.hv_cb_vector); 494 495 /* 496 * Notify the hypervisor of our vector. 497 */ 498 setup_args.vector = hv_vmbus_g_context.hv_cb_vector; 499 500 CPU_FOREACH(j) { 501 hv_vmbus_intr_cpu[j] = 0; 502 hv_vmbus_swintr_event_cpu[j] = 0; 503 hv_vmbus_g_context.hv_event_intr_event[j] = NULL; 504 hv_vmbus_g_context.hv_msg_intr_event[j] = NULL; 505 hv_vmbus_g_context.event_swintr[j] = NULL; 506 hv_vmbus_g_context.msg_swintr[j] = NULL; 507 508 for (i = 0; i < 2; i++) 509 setup_args.page_buffers[2 * j + i] = NULL; 510 } 511 512 /* 513 * Per cpu setup. 514 */ 515 CPU_FOREACH(j) { 516 /* 517 * Setup software interrupt thread and handler for msg handling. 518 */ 519 ret = swi_add(&hv_vmbus_g_context.hv_msg_intr_event[j], 520 "hv_msg", vmbus_msg_swintr, (void *)(long)j, SWI_CLOCK, 0, 521 &hv_vmbus_g_context.msg_swintr[j]); 522 if (ret) { 523 if(bootverbose) 524 printf("VMBUS: failed to setup msg swi for " 525 "cpu %d\n", j); 526 goto cleanup1; 527 } 528 529 /* 530 * Bind the swi thread to the cpu. 531 */ 532 ret = intr_event_bind(hv_vmbus_g_context.hv_msg_intr_event[j], 533 j); 534 if (ret) { 535 if(bootverbose) 536 printf("VMBUS: failed to bind msg swi thread " 537 "to cpu %d\n", j); 538 goto cleanup1; 539 } 540 541 /* 542 * Setup software interrupt thread and handler for 543 * event handling. 544 */ 545 ret = swi_add(&hv_vmbus_g_context.hv_event_intr_event[j], 546 "hv_event", hv_vmbus_on_events, (void *)(long)j, 547 SWI_CLOCK, 0, &hv_vmbus_g_context.event_swintr[j]); 548 if (ret) { 549 if(bootverbose) 550 printf("VMBUS: failed to setup event swi for " 551 "cpu %d\n", j); 552 goto cleanup1; 553 } 554 555 /* 556 * Prepare the per cpu msg and event pages to be called on each cpu. 557 */ 558 for(i = 0; i < 2; i++) { 559 setup_args.page_buffers[2 * j + i] = 560 malloc(PAGE_SIZE, M_DEVBUF, M_NOWAIT | M_ZERO); 561 if (setup_args.page_buffers[2 * j + i] == NULL) { 562 KASSERT(setup_args.page_buffers[2 * j + i] != NULL, 563 ("Error VMBUS: malloc failed!")); 564 goto cleanup1; 565 } 566 } 567 } 568 569 if (bootverbose) 570 printf("VMBUS: Calling smp_rendezvous, smp_started = %d\n", 571 smp_started); 572 573 smp_rendezvous(NULL, hv_vmbus_synic_init, NULL, &setup_args); 574 575 /* 576 * Connect to VMBus in the root partition 577 */ 578 ret = hv_vmbus_connect(); 579 580 if (ret != 0) 581 goto cleanup1; 582 583 hv_vmbus_request_channel_offers(); 584 return (ret); 585 586 cleanup1: 587 /* 588 * Free pages alloc'ed 589 */ 590 for (n = 0; n < 2 * MAXCPU; n++) 591 if (setup_args.page_buffers[n] != NULL) 592 free(setup_args.page_buffers[n], M_DEVBUF); 593 594 /* 595 * remove swi and vmbus callback vector; 596 */ 597 CPU_FOREACH(j) { 598 if (hv_vmbus_g_context.msg_swintr[j] != NULL) 599 swi_remove(hv_vmbus_g_context.msg_swintr[j]); 600 if (hv_vmbus_g_context.event_swintr[j] != NULL) 601 swi_remove(hv_vmbus_g_context.event_swintr[j]); 602 hv_vmbus_g_context.hv_msg_intr_event[j] = NULL; 603 hv_vmbus_g_context.hv_event_intr_event[j] = NULL; 604 } 605 606 vmbus_vector_free(hv_vmbus_g_context.hv_cb_vector); 607 608 cleanup: 609 hv_vmbus_cleanup(); 610 611 return (ret); 612} 613 614static int 615vmbus_attach(device_t dev) 616{ 617 if(bootverbose) 618 device_printf(dev, "VMBUS: attach dev: %p\n", dev); 619 vmbus_devp = dev; 620 621 /* 622 * If the system has already booted and thread 623 * scheduling is possible indicated by the global 624 * cold set to zero, we just call the driver 625 * initialization directly. 626 */ 627 if (!cold) 628 vmbus_bus_init(); 629 630 return (0); 631} 632 633static void 634vmbus_init(void) 635{ 636 if (vm_guest != VM_GUEST_HV) 637 return; 638 639 /* 640 * If the system has already booted and thread 641 * scheduling is possible, as indicated by the 642 * global cold set to zero, we just call the driver 643 * initialization directly. 644 */ 645 if (!cold) 646 vmbus_bus_init(); 647} 648 649static void 650vmbus_bus_exit(void) 651{ 652 int i; 653 654 hv_vmbus_release_unattached_channels(); 655 hv_vmbus_disconnect(); 656 657 smp_rendezvous(NULL, hv_vmbus_synic_cleanup, NULL, NULL); 658 659 for(i = 0; i < 2 * MAXCPU; i++) { 660 if (setup_args.page_buffers[i] != 0) 661 free(setup_args.page_buffers[i], M_DEVBUF); 662 } 663 664 hv_vmbus_cleanup(); 665 666 /* remove swi */ 667 CPU_FOREACH(i) { 668 if (hv_vmbus_g_context.msg_swintr[i] != NULL) 669 swi_remove(hv_vmbus_g_context.msg_swintr[i]); 670 if (hv_vmbus_g_context.event_swintr[i] != NULL) 671 swi_remove(hv_vmbus_g_context.event_swintr[i]); 672 hv_vmbus_g_context.hv_msg_intr_event[i] = NULL; 673 hv_vmbus_g_context.hv_event_intr_event[i] = NULL; 674 } 675 676 vmbus_vector_free(hv_vmbus_g_context.hv_cb_vector); 677 678 return; 679} 680 681static void 682vmbus_exit(void) 683{ 684 vmbus_bus_exit(); 685} 686 687static int 688vmbus_detach(device_t dev) 689{ 690 vmbus_exit(); 691 return (0); 692} 693 694static void 695vmbus_mod_load(void) 696{ 697 if(bootverbose) 698 printf("VMBUS: load\n"); 699} 700 701static void 702vmbus_mod_unload(void) 703{ 704 if(bootverbose) 705 printf("VMBUS: unload\n"); 706} 707 708static int 709vmbus_modevent(module_t mod, int what, void *arg) 710{ 711 switch (what) { 712 713 case MOD_LOAD: 714 vmbus_mod_load(); 715 break; 716 case MOD_UNLOAD: 717 vmbus_mod_unload(); 718 break; 719 } 720 721 return (0); 722} 723 724static device_method_t vmbus_methods[] = { 725 /** Device interface */ 726 DEVMETHOD(device_identify, vmbus_identify), 727 DEVMETHOD(device_probe, vmbus_probe), 728 DEVMETHOD(device_attach, vmbus_attach), 729 DEVMETHOD(device_detach, vmbus_detach), 730 DEVMETHOD(device_shutdown, bus_generic_shutdown), 731 DEVMETHOD(device_suspend, bus_generic_suspend), 732 DEVMETHOD(device_resume, bus_generic_resume), 733 734 /** Bus interface */ 735 DEVMETHOD(bus_add_child, bus_generic_add_child), 736 DEVMETHOD(bus_print_child, bus_generic_print_child), 737 DEVMETHOD(bus_read_ivar, vmbus_read_ivar), 738 DEVMETHOD(bus_write_ivar, vmbus_write_ivar), 739 740 { 0, 0 } }; 741 742static char driver_name[] = "vmbus"; 743static driver_t vmbus_driver = { driver_name, vmbus_methods,0, }; 744 745 746devclass_t vmbus_devclass; 747 748DRIVER_MODULE(vmbus, nexus, vmbus_driver, vmbus_devclass, vmbus_modevent, 0); 749MODULE_VERSION(vmbus,1); 750 751/* We want to be started after SMP is initialized */ 752SYSINIT(vmb_init, SI_SUB_SMP + 1, SI_ORDER_FIRST, vmbus_init, NULL); 753 754