1 /*- 2 * Copyright (c) 2018 VMware, Inc. 3 * 4 * SPDX-License-Identifier: (BSD-2-Clause OR GPL-2.0) 5 */ 6 7 /* This file implements the VMCI doorbell API. */ 8 9 #include <sys/cdefs.h> 10 __FBSDID("$FreeBSD$"); 11 12 #include <sys/types.h> 13 14 #include "vmci_doorbell.h" 15 #include "vmci_driver.h" 16 #include "vmci_kernel_api.h" 17 #include "vmci_kernel_defs.h" 18 #include "vmci_resource.h" 19 #include "vmci_utils.h" 20 21 #define LGPFX "vmci_doorbell: " 22 23 #define VMCI_DOORBELL_INDEX_TABLE_SIZE 64 24 #define VMCI_DOORBELL_HASH(_idx) \ 25 vmci_hash_id((_idx), VMCI_DOORBELL_INDEX_TABLE_SIZE) 26 27 /* Describes a doorbell notification handle allocated by the host. */ 28 struct vmci_doorbell_entry { 29 struct vmci_resource resource; 30 uint32_t idx; 31 vmci_list_item(vmci_doorbell_entry) idx_list_item; 32 vmci_privilege_flags priv_flags; 33 bool is_doorbell; 34 bool run_delayed; 35 vmci_callback notify_cb; 36 void *client_data; 37 vmci_event destroy_event; 38 volatile int active; 39 }; 40 41 struct vmci_doorbell_index_table { 42 vmci_lock lock; 43 vmci_list(vmci_doorbell_entry) entries[VMCI_DOORBELL_INDEX_TABLE_SIZE]; 44 }; 45 46 /* The VMCI index table keeps track of currently registered doorbells. */ 47 static struct vmci_doorbell_index_table vmci_doorbell_it; 48 49 /* 50 * The max_notify_idx is one larger than the currently known bitmap index in 51 * use, and is used to determine how much of the bitmap needs to be scanned. 52 */ 53 static uint32_t max_notify_idx; 54 55 /* 56 * The notify_idx_count is used for determining whether there are free entries 57 * within the bitmap (if notify_idx_count + 1 < max_notify_idx). 58 */ 59 static uint32_t notify_idx_count; 60 61 /* 62 * The last_notify_idx_reserved is used to track the last index handed out - in 63 * the case where multiple handles share a notification index, we hand out 64 * indexes round robin based on last_notify_idx_reserved. 65 */ 66 static uint32_t last_notify_idx_reserved; 67 68 /* This is a one entry cache used to by the index allocation. */ 69 static uint32_t last_notify_idx_released = PAGE_SIZE; 70 71 static void vmci_doorbell_free_cb(void *client_data); 72 static int vmci_doorbell_release_cb(void *client_data); 73 static void vmci_doorbell_delayed_dispatch_cb(void *data); 74 75 /* 76 *------------------------------------------------------------------------------ 77 * 78 * vmci_doorbell_init -- 79 * 80 * General init code. 81 * 82 * Result: 83 * VMCI_SUCCESS on success, lock allocation error otherwise. 84 * 85 * Side effects: 86 * None. 87 * 88 *------------------------------------------------------------------------------ 89 */ 90 91 int 92 vmci_doorbell_init(void) 93 { 94 uint32_t bucket; 95 96 for (bucket = 0; bucket < ARRAYSIZE(vmci_doorbell_it.entries); 97 ++bucket) 98 vmci_list_init(&vmci_doorbell_it.entries[bucket]); 99 100 return (vmci_init_lock(&vmci_doorbell_it.lock, 101 "VMCI Doorbell index table lock")); 102 } 103 104 /* 105 *------------------------------------------------------------------------------ 106 * 107 * vmci_doorbell_exit -- 108 * 109 * General exit code. 110 * 111 * Result: 112 * None. 113 * 114 * Side effects: 115 * None. 116 * 117 *------------------------------------------------------------------------------ 118 */ 119 120 void 121 vmci_doorbell_exit(void) 122 { 123 124 vmci_cleanup_lock(&vmci_doorbell_it.lock); 125 } 126 127 /* 128 *------------------------------------------------------------------------------ 129 * 130 * vmci_doorbell_free_cb -- 131 * 132 * Callback to free doorbell entry structure when resource is no longer used, 133 * i.e. the reference count reached 0. The entry is freed in 134 * vmci_doorbell_destroy(), which is waiting on the signal that gets fired 135 * here. 136 * 137 * Result: 138 * None. 139 * 140 * Side effects: 141 * Signals VMCI event. 142 * 143 *------------------------------------------------------------------------------ 144 */ 145 146 static void 147 vmci_doorbell_free_cb(void *client_data) 148 { 149 struct vmci_doorbell_entry *entry; 150 151 entry = (struct vmci_doorbell_entry *)client_data; 152 ASSERT(entry); 153 vmci_signal_event(&entry->destroy_event); 154 } 155 156 /* 157 *------------------------------------------------------------------------------ 158 * 159 * vmci_doorbell_release_cb -- 160 * 161 * Callback to release the resource reference. It is called by the 162 * vmci_wait_on_event function before it blocks. 163 * 164 * Result: 165 * Always 0. 166 * 167 * Side effects: 168 * None. 169 * 170 *------------------------------------------------------------------------------ 171 */ 172 173 static int 174 vmci_doorbell_release_cb(void *client_data) 175 { 176 struct vmci_doorbell_entry *entry; 177 178 entry = (struct vmci_doorbell_entry *)client_data; 179 ASSERT(entry); 180 vmci_resource_release(&entry->resource); 181 return (0); 182 } 183 184 /* 185 *------------------------------------------------------------------------------ 186 * 187 * vmci_doorbell_get_priv_flags -- 188 * 189 * Utility function that retrieves the privilege flags associated with a 190 * given doorbell handle. For guest endpoints, the privileges are determined 191 * by the context ID, but for host endpoints privileges are associated with 192 * the complete handle. Hypervisor endpoints are not yet supported. 193 * 194 * Result: 195 * VMCI_SUCCESS on success, 196 * VMCI_ERROR_NOT_FOUND if handle isn't found, 197 * VMCI_ERROR_INVALID_ARGS if handle is invalid. 198 * 199 * Side effects: 200 * None. 201 * 202 *------------------------------------------------------------------------------ 203 */ 204 205 int 206 vmci_doorbell_get_priv_flags(struct vmci_handle handle, 207 vmci_privilege_flags *priv_flags) 208 { 209 210 if (priv_flags == NULL || handle.context == VMCI_INVALID_ID) 211 return (VMCI_ERROR_INVALID_ARGS); 212 213 if (handle.context == VMCI_HOST_CONTEXT_ID) { 214 struct vmci_doorbell_entry *entry; 215 struct vmci_resource *resource; 216 217 resource = vmci_resource_get(handle, 218 VMCI_RESOURCE_TYPE_DOORBELL); 219 if (resource == NULL) 220 return (VMCI_ERROR_NOT_FOUND); 221 entry = RESOURCE_CONTAINER( 222 resource, struct vmci_doorbell_entry, resource); 223 *priv_flags = entry->priv_flags; 224 vmci_resource_release(resource); 225 } else if (handle.context == VMCI_HYPERVISOR_CONTEXT_ID) { 226 /* Hypervisor endpoints for notifications are not supported. */ 227 return (VMCI_ERROR_INVALID_ARGS); 228 } else 229 *priv_flags = VMCI_NO_PRIVILEGE_FLAGS; 230 231 return (VMCI_SUCCESS); 232 } 233 234 /* 235 *------------------------------------------------------------------------------ 236 * 237 * vmci_doorbell_index_table_find -- 238 * 239 * Find doorbell entry by bitmap index. 240 * 241 * Results: 242 * Entry if found, NULL if not. 243 * 244 * Side effects: 245 * None. 246 * 247 *------------------------------------------------------------------------------ 248 */ 249 250 static struct vmci_doorbell_entry * 251 vmci_doorbell_index_table_find(uint32_t idx) 252 { 253 struct vmci_doorbell_entry *iter; 254 uint32_t bucket; 255 256 bucket = VMCI_DOORBELL_HASH(idx); 257 258 vmci_list_scan(iter, &vmci_doorbell_it.entries[bucket], idx_list_item) { 259 if (idx == iter->idx) 260 return (iter); 261 } 262 263 return (NULL); 264 } 265 266 /* 267 *------------------------------------------------------------------------------ 268 * 269 * vmci_doorbell_index_table_add -- 270 * 271 * Add the given entry to the index table. This will hold() the entry's 272 * resource so that the entry is not deleted before it is removed from the 273 * table. 274 * 275 * Results: 276 * None. 277 * 278 * Side effects: 279 * None. 280 * 281 *------------------------------------------------------------------------------ 282 */ 283 284 static void 285 vmci_doorbell_index_table_add(struct vmci_doorbell_entry *entry) 286 { 287 uint32_t bucket; 288 uint32_t new_notify_idx; 289 290 ASSERT(entry); 291 292 vmci_resource_hold(&entry->resource); 293 294 vmci_grab_lock_bh(&vmci_doorbell_it.lock); 295 296 /* 297 * Below we try to allocate an index in the notification bitmap with 298 * "not too much" sharing between resources. If we use less that the 299 * full bitmap, we either add to the end if there are no unused flags 300 * within the currently used area, or we search for unused ones. If we 301 * use the full bitmap, we allocate the index round robin. 302 */ 303 304 if (max_notify_idx < PAGE_SIZE || notify_idx_count < PAGE_SIZE) { 305 if (last_notify_idx_released < max_notify_idx && 306 !vmci_doorbell_index_table_find(last_notify_idx_released)) { 307 new_notify_idx = last_notify_idx_released; 308 last_notify_idx_released = PAGE_SIZE; 309 } else { 310 bool reused = false; 311 new_notify_idx = last_notify_idx_reserved; 312 if (notify_idx_count + 1 < max_notify_idx) { 313 do { 314 if (!vmci_doorbell_index_table_find( 315 new_notify_idx)) { 316 reused = true; 317 break; 318 } 319 new_notify_idx = (new_notify_idx + 1) % 320 max_notify_idx; 321 } while (new_notify_idx != 322 last_notify_idx_released); 323 } 324 if (!reused) { 325 new_notify_idx = max_notify_idx; 326 max_notify_idx++; 327 } 328 } 329 } else { 330 new_notify_idx = (last_notify_idx_reserved + 1) % PAGE_SIZE; 331 } 332 last_notify_idx_reserved = new_notify_idx; 333 notify_idx_count++; 334 335 entry->idx = new_notify_idx; 336 bucket = VMCI_DOORBELL_HASH(entry->idx); 337 vmci_list_insert(&vmci_doorbell_it.entries[bucket], entry, 338 idx_list_item); 339 340 vmci_release_lock_bh(&vmci_doorbell_it.lock); 341 } 342 343 /* 344 *------------------------------------------------------------------------------ 345 * 346 * vmci_doorbell_index_table_remove -- 347 * 348 * Remove the given entry from the index table. This will release() the 349 * entry's resource. 350 * 351 * Results: 352 * None. 353 * 354 * Side effects: 355 * None. 356 * 357 *------------------------------------------------------------------------------ 358 */ 359 360 static void 361 vmci_doorbell_index_table_remove(struct vmci_doorbell_entry *entry) 362 { 363 ASSERT(entry); 364 365 vmci_grab_lock_bh(&vmci_doorbell_it.lock); 366 367 vmci_list_remove(entry, idx_list_item); 368 369 notify_idx_count--; 370 if (entry->idx == max_notify_idx - 1) { 371 /* 372 * If we delete an entry with the maximum known notification 373 * index, we take the opportunity to prune the current max. As 374 * there might be other unused indices immediately below, we 375 * lower the maximum until we hit an index in use 376 */ 377 378 while (max_notify_idx > 0 && 379 !vmci_doorbell_index_table_find(max_notify_idx - 1)) 380 max_notify_idx--; 381 } 382 last_notify_idx_released = entry->idx; 383 384 vmci_release_lock_bh(&vmci_doorbell_it.lock); 385 386 vmci_resource_release(&entry->resource); 387 } 388 389 /* 390 *------------------------------------------------------------------------------ 391 * 392 * vmci_doorbell_link -- 393 * 394 * Creates a link between the given doorbell handle and the given index in 395 * the bitmap in the device backend. 396 * 397 * Results: 398 * VMCI_SUCCESS if success, appropriate error code otherwise. 399 * 400 * Side effects: 401 * Notification state is created in hypervisor. 402 * 403 *------------------------------------------------------------------------------ 404 */ 405 406 static int 407 vmci_doorbell_link(struct vmci_handle handle, bool is_doorbell, 408 uint32_t notify_idx) 409 { 410 struct vmci_doorbell_link_msg link_msg; 411 vmci_id resource_id; 412 413 ASSERT(!VMCI_HANDLE_INVALID(handle)); 414 415 if (is_doorbell) 416 resource_id = VMCI_DOORBELL_LINK; 417 else { 418 ASSERT(false); 419 return (VMCI_ERROR_UNAVAILABLE); 420 } 421 422 link_msg.hdr.dst = VMCI_MAKE_HANDLE(VMCI_HYPERVISOR_CONTEXT_ID, 423 resource_id); 424 link_msg.hdr.src = VMCI_ANON_SRC_HANDLE; 425 link_msg.hdr.payload_size = sizeof(link_msg) - VMCI_DG_HEADERSIZE; 426 link_msg.handle = handle; 427 link_msg.notify_idx = notify_idx; 428 429 return (vmci_send_datagram((struct vmci_datagram *)&link_msg)); 430 } 431 432 /* 433 *------------------------------------------------------------------------------ 434 * 435 * vmci_doorbell_unlink -- 436 * 437 * Unlinks the given doorbell handle from an index in the bitmap in the 438 * device backend. 439 * 440 * Results: 441 * VMCI_SUCCESS if success, appropriate error code otherwise. 442 * 443 * Side effects: 444 * Notification state is destroyed in hypervisor. 445 * 446 *------------------------------------------------------------------------------ 447 */ 448 449 static int 450 vmci_doorbell_unlink(struct vmci_handle handle, bool is_doorbell) 451 { 452 struct vmci_doorbell_unlink_msg unlink_msg; 453 vmci_id resource_id; 454 455 ASSERT(!VMCI_HANDLE_INVALID(handle)); 456 457 if (is_doorbell) 458 resource_id = VMCI_DOORBELL_UNLINK; 459 else { 460 ASSERT(false); 461 return (VMCI_ERROR_UNAVAILABLE); 462 } 463 464 unlink_msg.hdr.dst = VMCI_MAKE_HANDLE(VMCI_HYPERVISOR_CONTEXT_ID, 465 resource_id); 466 unlink_msg.hdr.src = VMCI_ANON_SRC_HANDLE; 467 unlink_msg.hdr.payload_size = sizeof(unlink_msg) - VMCI_DG_HEADERSIZE; 468 unlink_msg.handle = handle; 469 470 return (vmci_send_datagram((struct vmci_datagram *)&unlink_msg)); 471 } 472 473 /* 474 *------------------------------------------------------------------------------ 475 * 476 * vmci_doorbell_create -- 477 * 478 * Creates a doorbell with the given callback. If the handle is 479 * VMCI_INVALID_HANDLE, a free handle will be assigned, if possible. The 480 * callback can be run immediately (potentially with locks held - the 481 * default) or delayed (in a kernel thread) by specifying the flag 482 * VMCI_FLAG_DELAYED_CB. If delayed execution is selected, a given callback 483 * may not be run if the kernel is unable to allocate memory for the delayed 484 * execution (highly unlikely). 485 * 486 * Results: 487 * VMCI_SUCCESS on success, appropriate error code otherwise. 488 * 489 * Side effects: 490 * None. 491 * 492 *------------------------------------------------------------------------------ 493 */ 494 495 int 496 vmci_doorbell_create(struct vmci_handle *handle, uint32_t flags, 497 vmci_privilege_flags priv_flags, vmci_callback notify_cb, void *client_data) 498 { 499 struct vmci_doorbell_entry *entry; 500 struct vmci_handle new_handle; 501 int result; 502 503 if (!handle || !notify_cb || flags & ~VMCI_FLAG_DELAYED_CB || 504 priv_flags & ~VMCI_PRIVILEGE_ALL_FLAGS) 505 return (VMCI_ERROR_INVALID_ARGS); 506 507 entry = vmci_alloc_kernel_mem(sizeof(*entry), VMCI_MEMORY_NORMAL); 508 if (entry == NULL) { 509 VMCI_LOG_WARNING(LGPFX"Failed allocating memory for datagram " 510 "entry.\n"); 511 return (VMCI_ERROR_NO_MEM); 512 } 513 514 if (!vmci_can_schedule_delayed_work() && 515 (flags & VMCI_FLAG_DELAYED_CB)) { 516 result = VMCI_ERROR_INVALID_ARGS; 517 goto free_mem; 518 } 519 520 if (VMCI_HANDLE_INVALID(*handle)) { 521 vmci_id context_id; 522 523 context_id = vmci_get_context_id(); 524 vmci_id resource_id = vmci_resource_get_id(context_id); 525 if (resource_id == VMCI_INVALID_ID) { 526 result = VMCI_ERROR_NO_HANDLE; 527 goto free_mem; 528 } 529 new_handle = VMCI_MAKE_HANDLE(context_id, resource_id); 530 } else { 531 if (VMCI_INVALID_ID == handle->resource) { 532 VMCI_LOG_DEBUG(LGPFX"Invalid argument " 533 "(handle=0x%x:0x%x).\n", handle->context, 534 handle->resource); 535 result = VMCI_ERROR_INVALID_ARGS; 536 goto free_mem; 537 } 538 new_handle = *handle; 539 } 540 541 entry->idx = 0; 542 entry->priv_flags = priv_flags; 543 entry->is_doorbell = true; 544 entry->run_delayed = (flags & VMCI_FLAG_DELAYED_CB) ? true : false; 545 entry->notify_cb = notify_cb; 546 entry->client_data = client_data; 547 atomic_store_int(&entry->active, 0); 548 vmci_create_event(&entry->destroy_event); 549 550 result = vmci_resource_add(&entry->resource, 551 VMCI_RESOURCE_TYPE_DOORBELL, new_handle, vmci_doorbell_free_cb, 552 entry); 553 if (result != VMCI_SUCCESS) { 554 VMCI_LOG_WARNING(LGPFX"Failed to add new resource " 555 "(handle=0x%x:0x%x).\n", new_handle.context, 556 new_handle.resource); 557 if (result == VMCI_ERROR_DUPLICATE_ENTRY) 558 result = VMCI_ERROR_ALREADY_EXISTS; 559 560 goto destroy; 561 } 562 563 vmci_doorbell_index_table_add(entry); 564 result = vmci_doorbell_link(new_handle, entry->is_doorbell, entry->idx); 565 if (VMCI_SUCCESS != result) 566 goto destroy_resource; 567 atomic_store_int(&entry->active, 1); 568 569 if (VMCI_HANDLE_INVALID(*handle)) 570 *handle = new_handle; 571 572 return (result); 573 574 destroy_resource: 575 vmci_doorbell_index_table_remove(entry); 576 vmci_resource_remove(new_handle, VMCI_RESOURCE_TYPE_DOORBELL); 577 destroy: 578 vmci_destroy_event(&entry->destroy_event); 579 free_mem: 580 vmci_free_kernel_mem(entry, sizeof(*entry)); 581 return (result); 582 } 583 584 /* 585 *------------------------------------------------------------------------------ 586 * 587 * vmci_doorbell_destroy -- 588 * 589 * Destroys a doorbell previously created with vmci_doorbell_create. This 590 * operation may block waiting for a callback to finish. 591 * 592 * Results: 593 * VMCI_SUCCESS on success, appropriate error code otherwise. 594 * 595 * Side effects: 596 * May block. 597 * 598 *------------------------------------------------------------------------------ 599 */ 600 601 int 602 vmci_doorbell_destroy(struct vmci_handle handle) 603 { 604 struct vmci_doorbell_entry *entry; 605 struct vmci_resource *resource; 606 int result; 607 608 if (VMCI_HANDLE_INVALID(handle)) 609 return (VMCI_ERROR_INVALID_ARGS); 610 611 resource = vmci_resource_get(handle, VMCI_RESOURCE_TYPE_DOORBELL); 612 if (resource == NULL) { 613 VMCI_LOG_DEBUG(LGPFX"Failed to destroy doorbell " 614 "(handle=0x%x:0x%x).\n", handle.context, handle.resource); 615 return (VMCI_ERROR_NOT_FOUND); 616 } 617 entry = RESOURCE_CONTAINER(resource, struct vmci_doorbell_entry, 618 resource); 619 620 vmci_doorbell_index_table_remove(entry); 621 622 result = vmci_doorbell_unlink(handle, entry->is_doorbell); 623 if (VMCI_SUCCESS != result) { 624 /* 625 * The only reason this should fail would be an inconsistency 626 * between guest and hypervisor state, where the guest believes 627 * it has an active registration whereas the hypervisor doesn't. 628 * One case where this may happen is if a doorbell is 629 * unregistered following a hibernation at a time where the 630 * doorbell state hasn't been restored on the hypervisor side 631 * yet. Since the handle has now been removed in the guest, 632 * we just print a warning and return success. 633 */ 634 635 VMCI_LOG_DEBUG(LGPFX"Unlink of %s (handle=0x%x:0x%x) unknown " 636 "by hypervisor (error=%d).\n", 637 entry->is_doorbell ? "doorbell" : "queuepair", 638 handle.context, handle.resource, result); 639 } 640 641 /* 642 * Now remove the resource from the table. It might still be in use 643 * after this, in a callback or still on the delayed work queue. 644 */ 645 646 vmci_resource_remove(handle, VMCI_RESOURCE_TYPE_DOORBELL); 647 648 /* 649 * We now wait on the destroyEvent and release the reference we got 650 * above. 651 */ 652 653 vmci_wait_on_event(&entry->destroy_event, vmci_doorbell_release_cb, 654 entry); 655 656 /* 657 * We know that we are now the only reference to the above entry so 658 * can safely free it. 659 */ 660 661 vmci_destroy_event(&entry->destroy_event); 662 vmci_free_kernel_mem(entry, sizeof(*entry)); 663 664 return (VMCI_SUCCESS); 665 } 666 667 /* 668 *------------------------------------------------------------------------------ 669 * 670 * vmci_doorbell_notify_as_guest -- 671 * 672 * Notify another guest or the host. We send a datagram down to the host 673 * via the hypervisor with the notification info. 674 * 675 * Results: 676 * VMCI_SUCCESS on success, appropriate error code otherwise. 677 * 678 * Side effects: 679 * May do a hypercall. 680 * 681 *------------------------------------------------------------------------------ 682 */ 683 684 static int 685 vmci_doorbell_notify_as_guest(struct vmci_handle handle, 686 vmci_privilege_flags priv_flags) 687 { 688 struct vmci_doorbell_notify_msg notify_msg; 689 690 notify_msg.hdr.dst = VMCI_MAKE_HANDLE(VMCI_HYPERVISOR_CONTEXT_ID, 691 VMCI_DOORBELL_NOTIFY); 692 notify_msg.hdr.src = VMCI_ANON_SRC_HANDLE; 693 notify_msg.hdr.payload_size = sizeof(notify_msg) - VMCI_DG_HEADERSIZE; 694 notify_msg.handle = handle; 695 696 return (vmci_send_datagram((struct vmci_datagram *)¬ify_msg)); 697 } 698 699 /* 700 *------------------------------------------------------------------------------ 701 * 702 * vmci_doorbell_notify -- 703 * 704 * Generates a notification on the doorbell identified by the handle. For 705 * host side generation of notifications, the caller can specify what the 706 * privilege of the calling side is. 707 * 708 * Results: 709 * VMCI_SUCCESS on success, appropriate error code otherwise. 710 * 711 * Side effects: 712 * May do a hypercall. 713 * 714 *------------------------------------------------------------------------------ 715 */ 716 717 int 718 vmci_doorbell_notify(struct vmci_handle dst, vmci_privilege_flags priv_flags) 719 { 720 if (VMCI_HANDLE_INVALID(dst) || 721 (priv_flags & ~VMCI_PRIVILEGE_ALL_FLAGS)) 722 return (VMCI_ERROR_INVALID_ARGS); 723 724 return (vmci_doorbell_notify_as_guest(dst, priv_flags)); 725 } 726 727 /* 728 *------------------------------------------------------------------------------ 729 * 730 * vmci_doorbell_delayed_dispatch_cb -- 731 * 732 * Calls the specified callback in a delayed context. 733 * 734 * Results: 735 * None. 736 * 737 * Side effects: 738 * None. 739 * 740 *------------------------------------------------------------------------------ 741 */ 742 743 static void 744 vmci_doorbell_delayed_dispatch_cb(void *data) 745 { 746 struct vmci_doorbell_entry *entry = (struct vmci_doorbell_entry *)data; 747 748 ASSERT(data); 749 750 entry->notify_cb(entry->client_data); 751 752 vmci_resource_release(&entry->resource); 753 } 754 755 /* 756 *------------------------------------------------------------------------------ 757 * 758 * vmci_doorbell_sync -- 759 * 760 * Use this as a synchronization point when setting globals, for example, 761 * during device shutdown. 762 * 763 * Results: 764 * None. 765 * 766 * Side effects: 767 * None. 768 * 769 *------------------------------------------------------------------------------ 770 */ 771 772 void 773 vmci_doorbell_sync(void) 774 { 775 776 vmci_grab_lock_bh(&vmci_doorbell_it.lock); 777 vmci_release_lock_bh(&vmci_doorbell_it.lock); 778 vmci_resource_sync(); 779 } 780 781 /* 782 *------------------------------------------------------------------------------ 783 * 784 * vmci_register_notification_bitmap -- 785 * 786 * Register the notification bitmap with the host. 787 * 788 * Results: 789 * true if the bitmap is registered successfully with the device, false 790 * otherwise. 791 * 792 * Side effects: 793 * None. 794 * 795 *------------------------------------------------------------------------------ 796 */ 797 798 bool 799 vmci_register_notification_bitmap(PPN bitmap_ppn) 800 { 801 struct vmci_notify_bitmap_set_msg bitmap_set_msg; 802 int result; 803 804 /* 805 * Do not ASSERT() on the guest device here. This function can get 806 * called during device initialization, so the ASSERT() will fail even 807 * though the device is (almost) up. 808 */ 809 810 bitmap_set_msg.hdr.dst = VMCI_MAKE_HANDLE(VMCI_HYPERVISOR_CONTEXT_ID, 811 VMCI_SET_NOTIFY_BITMAP); 812 bitmap_set_msg.hdr.src = VMCI_ANON_SRC_HANDLE; 813 bitmap_set_msg.hdr.payload_size = 814 sizeof(bitmap_set_msg) - VMCI_DG_HEADERSIZE; 815 bitmap_set_msg.bitmap_ppn = bitmap_ppn; 816 817 result = vmci_send_datagram((struct vmci_datagram *)&bitmap_set_msg); 818 if (result != VMCI_SUCCESS) { 819 VMCI_LOG_DEBUG(LGPFX"Failed to register (PPN=%u) as " 820 "notification bitmap (error=%d).\n", 821 bitmap_ppn, result); 822 return (false); 823 } 824 return (true); 825 } 826 827 /* 828 *------------------------------------------------------------------------------ 829 * 830 * vmci_doorbell_fire_entries -- 831 * 832 * Executes or schedules the handlers for a given notify index. 833 * 834 * Result: 835 * Notification hash entry if found. NULL otherwise. 836 * 837 * Side effects: 838 * Whatever the side effects of the handlers are. 839 * 840 *------------------------------------------------------------------------------ 841 */ 842 843 static void 844 vmci_doorbell_fire_entries(uint32_t notify_idx) 845 { 846 struct vmci_doorbell_entry *iter; 847 uint32_t bucket = VMCI_DOORBELL_HASH(notify_idx); 848 849 vmci_grab_lock_bh(&vmci_doorbell_it.lock); 850 851 vmci_list_scan(iter, &vmci_doorbell_it.entries[bucket], idx_list_item) { 852 if (iter->idx == notify_idx && 853 atomic_load_int(&iter->active) == 1) { 854 ASSERT(iter->notify_cb); 855 if (iter->run_delayed) { 856 int err; 857 858 vmci_resource_hold(&iter->resource); 859 err = vmci_schedule_delayed_work( 860 vmci_doorbell_delayed_dispatch_cb, iter); 861 if (err != VMCI_SUCCESS) { 862 vmci_resource_release(&iter->resource); 863 goto out; 864 } 865 } else 866 iter->notify_cb(iter->client_data); 867 } 868 } 869 870 out: 871 vmci_release_lock_bh(&vmci_doorbell_it.lock); 872 } 873 874 /* 875 *------------------------------------------------------------------------------ 876 * 877 * vmci_scan_notification_bitmap -- 878 * 879 * Scans the notification bitmap, collects pending notifications, resets 880 * the bitmap and invokes appropriate callbacks. 881 * 882 * Results: 883 * None. 884 * 885 * Side effects: 886 * May schedule tasks, allocate memory and run callbacks. 887 * 888 *------------------------------------------------------------------------------ 889 */ 890 891 void 892 vmci_scan_notification_bitmap(uint8_t *bitmap) 893 { 894 uint32_t idx; 895 896 ASSERT(bitmap); 897 898 for (idx = 0; idx < max_notify_idx; idx++) { 899 if (bitmap[idx] & 0x1) { 900 bitmap[idx] &= ~1; 901 vmci_doorbell_fire_entries(idx); 902 } 903 } 904 } 905