The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/vmware/vmci/vmci_doorbell.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    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 *)&notify_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 }

Cache object: 6a09cfe367a3acc3f4aafd81c487a41a


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.