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