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