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_queue_pair.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 /* VMCI QueuePair API implementation. */
    8 
    9 #include <sys/cdefs.h>
   10 __FBSDID("$FreeBSD$");
   11 
   12 #include "vmci.h"
   13 #include "vmci_driver.h"
   14 #include "vmci_event.h"
   15 #include "vmci_kernel_api.h"
   16 #include "vmci_kernel_defs.h"
   17 #include "vmci_queue_pair.h"
   18 
   19 #define LGPFX   "vmci_queue_pair: "
   20 
   21 struct queue_pair_entry {
   22         vmci_list_item(queue_pair_entry) list_item;
   23         struct vmci_handle handle;
   24         vmci_id         peer;
   25         uint32_t        flags;
   26         uint64_t        produce_size;
   27         uint64_t        consume_size;
   28         uint32_t        ref_count;
   29 };
   30 
   31 struct qp_guest_endpoint {
   32         struct queue_pair_entry qp;
   33         uint64_t        num_ppns;
   34         void            *produce_q;
   35         void            *consume_q;
   36         bool            hibernate_failure;
   37         struct ppn_set  ppn_set;
   38 };
   39 
   40 struct queue_pair_list {
   41         vmci_list(queue_pair_entry) head;
   42         volatile int    hibernate;
   43         vmci_mutex      mutex;
   44 };
   45 
   46 #define QPE_NUM_PAGES(_QPE)                                             \
   47         ((uint32_t)(CEILING(_QPE.produce_size, PAGE_SIZE) +             \
   48         CEILING(_QPE.consume_size, PAGE_SIZE) + 2))
   49 
   50 static struct queue_pair_list qp_guest_endpoints;
   51 
   52 static struct   queue_pair_entry *queue_pair_list_find_entry(
   53                     struct queue_pair_list *qp_list, struct vmci_handle handle);
   54 static void     queue_pair_list_add_entry(struct queue_pair_list *qp_list,
   55                     struct queue_pair_entry *entry);
   56 static void     queue_pair_list_remove_entry(struct queue_pair_list *qp_list,
   57                     struct queue_pair_entry *entry);
   58 static struct   queue_pair_entry *queue_pair_list_get_head(
   59                     struct queue_pair_list *qp_list);
   60 static int      queue_pair_notify_peer_local(bool attach,
   61                     struct vmci_handle handle);
   62 static struct   qp_guest_endpoint *qp_guest_endpoint_create(
   63                     struct vmci_handle handle, vmci_id peer, uint32_t flags,
   64                     uint64_t produce_size, uint64_t consume_size,
   65                     void *produce_q, void *consume_q);
   66 static void     qp_guest_endpoint_destroy(struct qp_guest_endpoint *entry);
   67 static int      vmci_queue_pair_alloc_hypercall(
   68                     const struct qp_guest_endpoint *entry);
   69 static int      vmci_queue_pair_alloc_guest_work(struct vmci_handle *handle,
   70                     struct vmci_queue **produce_q, uint64_t produce_size,
   71                     struct vmci_queue **consume_q, uint64_t consume_size,
   72                     vmci_id peer, uint32_t flags,
   73                     vmci_privilege_flags priv_flags);
   74 static int      vmci_queue_pair_detach_guest_work(struct vmci_handle handle);
   75 static int      vmci_queue_pair_detach_hypercall(struct vmci_handle handle);
   76 
   77 /*
   78  *------------------------------------------------------------------------------
   79  *
   80  * vmci_queue_pair_alloc --
   81  *
   82  *     Allocates a VMCI QueuePair. Only checks validity of input arguments. The
   83  *     real work is done in the host or guest specific function.
   84  *
   85  * Results:
   86  *     VMCI_SUCCESS on success, appropriate error code otherwise.
   87  *
   88  * Side effects:
   89  *     None.
   90  *
   91  *------------------------------------------------------------------------------
   92  */
   93 
   94 int
   95 vmci_queue_pair_alloc(struct vmci_handle *handle, struct vmci_queue **produce_q,
   96     uint64_t produce_size, struct vmci_queue **consume_q, uint64_t consume_size,
   97     vmci_id peer, uint32_t flags, vmci_privilege_flags priv_flags)
   98 {
   99 
  100         if (!handle || !produce_q || !consume_q ||
  101             (!produce_size && !consume_size) || (flags & ~VMCI_QP_ALL_FLAGS))
  102                 return (VMCI_ERROR_INVALID_ARGS);
  103 
  104         return (vmci_queue_pair_alloc_guest_work(handle, produce_q,
  105             produce_size, consume_q, consume_size, peer, flags, priv_flags));
  106 }
  107 
  108 /*
  109  *------------------------------------------------------------------------------
  110  *
  111  * vmci_queue_pair_detach --
  112  *
  113  *     Detaches from a VMCI QueuePair. Only checks validity of input argument.
  114  *     Real work is done in the host or guest specific function.
  115  *
  116  * Results:
  117  *     Success or failure.
  118  *
  119  * Side effects:
  120  *     Memory is freed.
  121  *
  122  *------------------------------------------------------------------------------
  123  */
  124 
  125 int
  126 vmci_queue_pair_detach(struct vmci_handle handle)
  127 {
  128 
  129         if (VMCI_HANDLE_INVALID(handle))
  130                 return (VMCI_ERROR_INVALID_ARGS);
  131 
  132         return (vmci_queue_pair_detach_guest_work(handle));
  133 }
  134 
  135 /*
  136  *------------------------------------------------------------------------------
  137  *
  138  * queue_pair_list_init --
  139  *
  140  *     Initializes the list of QueuePairs.
  141  *
  142  * Results:
  143  *     Success or failure.
  144  *
  145  * Side effects:
  146  *     None.
  147  *
  148  *------------------------------------------------------------------------------
  149  */
  150 
  151 static inline int
  152 queue_pair_list_init(struct queue_pair_list *qp_list)
  153 {
  154         int ret;
  155 
  156         vmci_list_init(&qp_list->head);
  157         atomic_store_int(&qp_list->hibernate, 0);
  158         ret = vmci_mutex_init(&qp_list->mutex, "VMCI QP List lock");
  159         return (ret);
  160 }
  161 
  162 /*
  163  *------------------------------------------------------------------------------
  164  *
  165  * queue_pair_list_destroy --
  166  *
  167  *     Destroy the list's mutex.
  168  *
  169  * Results:
  170  *     None.
  171  *
  172  * Side effects:
  173  *     None.
  174  *
  175  *------------------------------------------------------------------------------
  176  */
  177 
  178 static inline void
  179 queue_pair_list_destroy(struct queue_pair_list *qp_list)
  180 {
  181 
  182         vmci_mutex_destroy(&qp_list->mutex);
  183         vmci_list_init(&qp_list->head);
  184 }
  185 
  186 /*
  187  *------------------------------------------------------------------------------
  188  *
  189  * queue_pair_list_find_entry --
  190  *
  191  *     Finds the entry in the list corresponding to a given handle. Assumes that
  192  *     the list is locked.
  193  *
  194  * Results:
  195  *     Pointer to entry.
  196  *
  197  * Side effects:
  198  *     None.
  199  *
  200  *------------------------------------------------------------------------------
  201  */
  202 
  203 static struct queue_pair_entry *
  204 queue_pair_list_find_entry(struct queue_pair_list *qp_list,
  205     struct vmci_handle handle)
  206 {
  207         struct queue_pair_entry *next;
  208 
  209         if (VMCI_HANDLE_INVALID(handle))
  210                 return (NULL);
  211 
  212         vmci_list_scan(next, &qp_list->head, list_item) {
  213                 if (VMCI_HANDLE_EQUAL(next->handle, handle))
  214                         return (next);
  215         }
  216 
  217         return (NULL);
  218 }
  219 
  220 /*
  221  *------------------------------------------------------------------------------
  222  *
  223  * queue_pair_list_add_entry --
  224  *
  225  *     Adds the given entry to the list. Assumes that the list is locked.
  226  *
  227  * Results:
  228  *     None.
  229  *
  230  * Side effects:
  231  *     None.
  232  *
  233  *------------------------------------------------------------------------------
  234  */
  235 
  236 static void
  237 queue_pair_list_add_entry(struct queue_pair_list *qp_list,
  238     struct queue_pair_entry *entry)
  239 {
  240 
  241         if (entry)
  242                 vmci_list_insert(&qp_list->head, entry, list_item);
  243 }
  244 
  245 /*
  246  *------------------------------------------------------------------------------
  247  *
  248  * queue_pair_list_remove_entry --
  249  *
  250  *     Removes the given entry from the list. Assumes that the list is locked.
  251  *
  252  * Results:
  253  *     None.
  254  *
  255  * Side effects:
  256  *     None.
  257  *
  258  *------------------------------------------------------------------------------
  259  */
  260 
  261 static void
  262 queue_pair_list_remove_entry(struct queue_pair_list *qp_list,
  263     struct queue_pair_entry *entry)
  264 {
  265 
  266         if (entry)
  267                 vmci_list_remove(entry, list_item);
  268 }
  269 
  270 /*
  271  *------------------------------------------------------------------------------
  272  *
  273  * queue_pair_list_get_head --
  274  *
  275  *     Returns the entry from the head of the list. Assumes that the list is
  276  *     locked.
  277  *
  278  * Results:
  279  *     Pointer to entry.
  280  *
  281  * Side effects:
  282  *     None.
  283  *
  284  *------------------------------------------------------------------------------
  285  */
  286 
  287 static struct queue_pair_entry *
  288 queue_pair_list_get_head(struct queue_pair_list *qp_list)
  289 {
  290 
  291         return (vmci_list_first(&qp_list->head));
  292 }
  293 
  294 /*
  295  *------------------------------------------------------------------------------
  296  *
  297  * vmci_qp_guest_endpoints_init --
  298  *
  299  *     Initalizes data structure state keeping track of queue pair guest
  300  *     endpoints.
  301  *
  302  * Results:
  303  *     VMCI_SUCCESS on success and appropriate failure code otherwise.
  304  *
  305  * Side effects:
  306  *     None.
  307  *
  308  *------------------------------------------------------------------------------
  309  */
  310 
  311 int
  312 vmci_qp_guest_endpoints_init(void)
  313 {
  314 
  315         return (queue_pair_list_init(&qp_guest_endpoints));
  316 }
  317 
  318 /*
  319  *------------------------------------------------------------------------------
  320  *
  321  * vmci_qp_guest_endpoints_exit --
  322  *
  323  *     Destroys all guest queue pair endpoints. If active guest queue pairs
  324  *     still exist, hypercalls to attempt detach from these queue pairs will be
  325  *     made. Any failure to detach is silently ignored.
  326  *
  327  * Results:
  328  *     None.
  329  *
  330  * Side effects:
  331  *     None.
  332  *
  333  *------------------------------------------------------------------------------
  334  */
  335 
  336 void
  337 vmci_qp_guest_endpoints_exit(void)
  338 {
  339         struct qp_guest_endpoint *entry;
  340 
  341         if (!vmci_mutex_initialized(&qp_guest_endpoints.mutex))
  342                 return;
  343 
  344         vmci_mutex_acquire(&qp_guest_endpoints.mutex);
  345 
  346         while ((entry =
  347             (struct qp_guest_endpoint *)queue_pair_list_get_head(
  348             &qp_guest_endpoints)) != NULL) {
  349                 /*
  350                  * Don't make a hypercall for local QueuePairs.
  351                  */
  352                 if (!(entry->qp.flags & VMCI_QPFLAG_LOCAL))
  353                         vmci_queue_pair_detach_hypercall(entry->qp.handle);
  354                 /*
  355                  * We cannot fail the exit, so let's reset ref_count.
  356                  */
  357                 entry->qp.ref_count = 0;
  358                 queue_pair_list_remove_entry(&qp_guest_endpoints, &entry->qp);
  359                 qp_guest_endpoint_destroy(entry);
  360         }
  361 
  362         atomic_store_int(&qp_guest_endpoints.hibernate, 0);
  363         vmci_mutex_release(&qp_guest_endpoints.mutex);
  364         queue_pair_list_destroy(&qp_guest_endpoints);
  365 }
  366 
  367 /*
  368  *------------------------------------------------------------------------------
  369  *
  370  * vmci_qp_guest_endpoints_sync --
  371  *
  372  *     Use this as a synchronization point when setting globals, for example,
  373  *     during device shutdown.
  374  *
  375  * Results:
  376  *     true.
  377  *
  378  * Side effects:
  379  *     None.
  380  *
  381  *------------------------------------------------------------------------------
  382  */
  383 
  384 void
  385 vmci_qp_guest_endpoints_sync(void)
  386 {
  387 
  388         vmci_mutex_acquire(&qp_guest_endpoints.mutex);
  389         vmci_mutex_release(&qp_guest_endpoints.mutex);
  390 }
  391 
  392 /*
  393  *------------------------------------------------------------------------------
  394  *
  395  * qp_guest_endpoint_create --
  396  *
  397  *     Allocates and initializes a qp_guest_endpoint structure. Allocates a
  398  *     QueuePair rid (and handle) iff the given entry has an invalid handle.
  399  *     0 through VMCI_RESERVED_RESOURCE_ID_MAX are reserved handles. Assumes
  400  *     that the QP list mutex is held by the caller.
  401  *
  402  * Results:
  403  *     Pointer to structure intialized.
  404  *
  405  * Side effects:
  406  *     None.
  407  *
  408  *------------------------------------------------------------------------------
  409  */
  410 
  411 struct qp_guest_endpoint *
  412 qp_guest_endpoint_create(struct vmci_handle handle, vmci_id peer,
  413     uint32_t flags, uint64_t produce_size, uint64_t consume_size,
  414     void *produce_q, void *consume_q)
  415 {
  416         struct qp_guest_endpoint *entry;
  417         static vmci_id queue_pair_rid;
  418         const uint64_t num_ppns = CEILING(produce_size, PAGE_SIZE) +
  419             CEILING(consume_size, PAGE_SIZE) +
  420             2; /* One page each for the queue headers. */
  421 
  422         queue_pair_rid = VMCI_RESERVED_RESOURCE_ID_MAX + 1;
  423 
  424         ASSERT((produce_size || consume_size) && produce_q && consume_q);
  425 
  426         if (VMCI_HANDLE_INVALID(handle)) {
  427                 vmci_id context_id = vmci_get_context_id();
  428                 vmci_id old_rid = queue_pair_rid;
  429 
  430                 /*
  431                  * Generate a unique QueuePair rid.  Keep on trying until we
  432                  * wrap around in the RID space.
  433                  */
  434                 ASSERT(old_rid > VMCI_RESERVED_RESOURCE_ID_MAX);
  435                 do {
  436                         handle = VMCI_MAKE_HANDLE(context_id, queue_pair_rid);
  437                         entry =
  438                             (struct qp_guest_endpoint *)
  439                             queue_pair_list_find_entry(&qp_guest_endpoints,
  440                             handle);
  441                         queue_pair_rid++;
  442                         if (UNLIKELY(!queue_pair_rid)) {
  443                                 /*
  444                                  * Skip the reserved rids.
  445                                  */
  446                                 queue_pair_rid =
  447                                     VMCI_RESERVED_RESOURCE_ID_MAX + 1;
  448                         }
  449                 } while (entry && queue_pair_rid != old_rid);
  450 
  451                 if (UNLIKELY(entry != NULL)) {
  452                         ASSERT(queue_pair_rid == old_rid);
  453                         /*
  454                          * We wrapped around --- no rids were free.
  455                          */
  456                         return (NULL);
  457                 }
  458         }
  459 
  460         ASSERT(!VMCI_HANDLE_INVALID(handle) &&
  461             queue_pair_list_find_entry(&qp_guest_endpoints, handle) == NULL);
  462         entry = vmci_alloc_kernel_mem(sizeof(*entry), VMCI_MEMORY_NORMAL);
  463         if (entry) {
  464                 entry->qp.handle = handle;
  465                 entry->qp.peer = peer;
  466                 entry->qp.flags = flags;
  467                 entry->qp.produce_size = produce_size;
  468                 entry->qp.consume_size = consume_size;
  469                 entry->qp.ref_count = 0;
  470                 entry->num_ppns = num_ppns;
  471                 memset(&entry->ppn_set, 0, sizeof(entry->ppn_set));
  472                 entry->produce_q = produce_q;
  473                 entry->consume_q = consume_q;
  474         }
  475         return (entry);
  476 }
  477 
  478 /*
  479  *------------------------------------------------------------------------------
  480  *
  481  * qp_guest_endpoint_destroy --
  482  *
  483  *     Frees a qp_guest_endpoint structure.
  484  *
  485  * Results:
  486  *     None.
  487  *
  488  * Side effects:
  489  *     None.
  490  *
  491  *------------------------------------------------------------------------------
  492  */
  493 
  494 void
  495 qp_guest_endpoint_destroy(struct qp_guest_endpoint *entry)
  496 {
  497 
  498         ASSERT(entry);
  499         ASSERT(entry->qp.ref_count == 0);
  500 
  501         vmci_free_ppn_set(&entry->ppn_set);
  502         vmci_free_queue(entry->produce_q, entry->qp.produce_size);
  503         vmci_free_queue(entry->consume_q, entry->qp.consume_size);
  504         vmci_free_kernel_mem(entry, sizeof(*entry));
  505 }
  506 
  507 /*
  508  *------------------------------------------------------------------------------
  509  *
  510  * vmci_queue_pair_alloc_hypercall --
  511  *
  512  *     Helper to make a QueuePairAlloc hypercall when the driver is
  513  *     supporting a guest device.
  514  *
  515  * Results:
  516  *     Result of the hypercall.
  517  *
  518  * Side effects:
  519  *     Memory is allocated & freed.
  520  *
  521  *------------------------------------------------------------------------------
  522  */
  523 static int
  524 vmci_queue_pair_alloc_hypercall(const struct qp_guest_endpoint *entry)
  525 {
  526         struct vmci_queue_pair_alloc_msg *alloc_msg;
  527         size_t msg_size;
  528         int result;
  529 
  530         if (!entry || entry->num_ppns <= 2)
  531                 return (VMCI_ERROR_INVALID_ARGS);
  532 
  533         ASSERT(!(entry->qp.flags & VMCI_QPFLAG_LOCAL));
  534 
  535         msg_size = sizeof(*alloc_msg) + (size_t)entry->num_ppns * sizeof(PPN);
  536         alloc_msg = vmci_alloc_kernel_mem(msg_size, VMCI_MEMORY_NORMAL);
  537         if (!alloc_msg)
  538                 return (VMCI_ERROR_NO_MEM);
  539 
  540         alloc_msg->hdr.dst = VMCI_MAKE_HANDLE(VMCI_HYPERVISOR_CONTEXT_ID,
  541             VMCI_QUEUEPAIR_ALLOC);
  542         alloc_msg->hdr.src = VMCI_ANON_SRC_HANDLE;
  543         alloc_msg->hdr.payload_size = msg_size - VMCI_DG_HEADERSIZE;
  544         alloc_msg->handle = entry->qp.handle;
  545         alloc_msg->peer = entry->qp.peer;
  546         alloc_msg->flags = entry->qp.flags;
  547         alloc_msg->produce_size = entry->qp.produce_size;
  548         alloc_msg->consume_size = entry->qp.consume_size;
  549         alloc_msg->num_ppns = entry->num_ppns;
  550         result = vmci_populate_ppn_list((uint8_t *)alloc_msg +
  551             sizeof(*alloc_msg), &entry->ppn_set);
  552         if (result == VMCI_SUCCESS)
  553                 result = vmci_send_datagram((struct vmci_datagram *)alloc_msg);
  554         vmci_free_kernel_mem(alloc_msg, msg_size);
  555 
  556         return (result);
  557 }
  558 
  559 /*
  560  *------------------------------------------------------------------------------
  561  *
  562  * vmci_queue_pair_alloc_guest_work --
  563  *
  564  *     This functions handles the actual allocation of a VMCI queue pair guest
  565  *     endpoint. Allocates physical pages for the queue pair. It makes OS
  566  *     dependent calls through generic wrappers.
  567  *
  568  * Results:
  569  *     Success or failure.
  570  *
  571  * Side effects:
  572  *     Memory is allocated.
  573  *
  574  *------------------------------------------------------------------------------
  575  */
  576 
  577 static int
  578 vmci_queue_pair_alloc_guest_work(struct vmci_handle *handle,
  579     struct vmci_queue **produce_q, uint64_t produce_size,
  580     struct vmci_queue **consume_q, uint64_t consume_size, vmci_id peer,
  581     uint32_t flags, vmci_privilege_flags priv_flags)
  582 {
  583         struct qp_guest_endpoint *queue_pair_entry = NULL;
  584         void *my_consume_q = NULL;
  585         void *my_produce_q = NULL;
  586         const uint64_t num_consume_pages = CEILING(consume_size, PAGE_SIZE) + 1;
  587         const uint64_t num_produce_pages = CEILING(produce_size, PAGE_SIZE) + 1;
  588         int result;
  589 
  590         ASSERT(handle && produce_q && consume_q &&
  591             (produce_size || consume_size));
  592 
  593         if (priv_flags != VMCI_NO_PRIVILEGE_FLAGS)
  594                 return (VMCI_ERROR_NO_ACCESS);
  595 
  596         vmci_mutex_acquire(&qp_guest_endpoints.mutex);
  597 
  598         if ((atomic_load_int(&qp_guest_endpoints.hibernate) == 1) &&
  599                  !(flags & VMCI_QPFLAG_LOCAL)) {
  600                 /*
  601                  * While guest OS is in hibernate state, creating non-local
  602                  * queue pairs is not allowed after the point where the VMCI
  603                  * guest driver converted the existing queue pairs to local
  604                  * ones.
  605                  */
  606 
  607                 result = VMCI_ERROR_UNAVAILABLE;
  608                 goto error;
  609         }
  610 
  611         if ((queue_pair_entry =
  612             (struct qp_guest_endpoint *)queue_pair_list_find_entry(
  613             &qp_guest_endpoints, *handle)) != NULL) {
  614                 if (queue_pair_entry->qp.flags & VMCI_QPFLAG_LOCAL) {
  615                         /* Local attach case. */
  616                         if (queue_pair_entry->qp.ref_count > 1) {
  617                                 VMCI_LOG_DEBUG(LGPFX"Error attempting to "
  618                                     "attach more than once.\n");
  619                                 result = VMCI_ERROR_UNAVAILABLE;
  620                                 goto error_keep_entry;
  621                         }
  622 
  623                         if (queue_pair_entry->qp.produce_size != consume_size ||
  624                             queue_pair_entry->qp.consume_size != produce_size ||
  625                             queue_pair_entry->qp.flags !=
  626                             (flags & ~VMCI_QPFLAG_ATTACH_ONLY)) {
  627                                 VMCI_LOG_DEBUG(LGPFX"Error mismatched "
  628                                     "queue pair in local attach.\n");
  629                                 result = VMCI_ERROR_QUEUEPAIR_MISMATCH;
  630                                 goto error_keep_entry;
  631                         }
  632 
  633                         /*
  634                          * Do a local attach. We swap the consume and produce
  635                          * queues for the attacher and deliver an attach event.
  636                          */
  637                         result = queue_pair_notify_peer_local(true, *handle);
  638                         if (result < VMCI_SUCCESS)
  639                                 goto error_keep_entry;
  640                         my_produce_q = queue_pair_entry->consume_q;
  641                         my_consume_q = queue_pair_entry->produce_q;
  642                         goto out;
  643                 }
  644                 result = VMCI_ERROR_ALREADY_EXISTS;
  645                 goto error_keep_entry;
  646         }
  647 
  648         my_produce_q = vmci_alloc_queue(produce_size, flags);
  649         if (!my_produce_q) {
  650                 VMCI_LOG_WARNING(LGPFX"Error allocating pages for produce "
  651                     "queue.\n");
  652                 result = VMCI_ERROR_NO_MEM;
  653                 goto error;
  654         }
  655 
  656         my_consume_q = vmci_alloc_queue(consume_size, flags);
  657         if (!my_consume_q) {
  658                 VMCI_LOG_WARNING(LGPFX"Error allocating pages for consume "
  659                     "queue.\n");
  660                 result = VMCI_ERROR_NO_MEM;
  661                 goto error;
  662         }
  663 
  664         queue_pair_entry = qp_guest_endpoint_create(*handle, peer, flags,
  665             produce_size, consume_size, my_produce_q, my_consume_q);
  666         if (!queue_pair_entry) {
  667                 VMCI_LOG_WARNING(LGPFX"Error allocating memory in %s.\n",
  668                     __FUNCTION__);
  669                 result = VMCI_ERROR_NO_MEM;
  670                 goto error;
  671         }
  672 
  673         result = vmci_alloc_ppn_set(my_produce_q, num_produce_pages,
  674             my_consume_q, num_consume_pages, &queue_pair_entry->ppn_set);
  675         if (result < VMCI_SUCCESS) {
  676                 VMCI_LOG_WARNING(LGPFX"vmci_alloc_ppn_set failed.\n");
  677                 goto error;
  678         }
  679 
  680         /*
  681          * It's only necessary to notify the host if this queue pair will be
  682          * attached to from another context.
  683          */
  684         if (queue_pair_entry->qp.flags & VMCI_QPFLAG_LOCAL) {
  685                 /* Local create case. */
  686                 vmci_id context_id = vmci_get_context_id();
  687 
  688                 /*
  689                  * Enforce similar checks on local queue pairs as we do for
  690                  * regular ones. The handle's context must match the creator
  691                  * or attacher context id (here they are both the current
  692                  * context id) and the attach-only flag cannot exist during
  693                  * create. We also ensure specified peer is this context or
  694                  * an invalid one.
  695                  */
  696                 if (queue_pair_entry->qp.handle.context != context_id ||
  697                     (queue_pair_entry->qp.peer != VMCI_INVALID_ID &&
  698                     queue_pair_entry->qp.peer != context_id)) {
  699                         result = VMCI_ERROR_NO_ACCESS;
  700                         goto error;
  701                 }
  702 
  703                 if (queue_pair_entry->qp.flags & VMCI_QPFLAG_ATTACH_ONLY) {
  704                         result = VMCI_ERROR_NOT_FOUND;
  705                         goto error;
  706                 }
  707         } else {
  708                 result = vmci_queue_pair_alloc_hypercall(queue_pair_entry);
  709                 if (result < VMCI_SUCCESS) {
  710                         VMCI_LOG_WARNING(
  711                             LGPFX"vmci_queue_pair_alloc_hypercall result = "
  712                             "%d.\n", result);
  713                         goto error;
  714                 }
  715         }
  716 
  717         queue_pair_list_add_entry(&qp_guest_endpoints, &queue_pair_entry->qp);
  718 
  719 out:
  720         queue_pair_entry->qp.ref_count++;
  721         *handle = queue_pair_entry->qp.handle;
  722         *produce_q = (struct vmci_queue *)my_produce_q;
  723         *consume_q = (struct vmci_queue *)my_consume_q;
  724 
  725         /*
  726          * We should initialize the queue pair header pages on a local queue
  727          * pair create. For non-local queue pairs, the hypervisor initializes
  728          * the header pages in the create step.
  729          */
  730         if ((queue_pair_entry->qp.flags & VMCI_QPFLAG_LOCAL) &&
  731             queue_pair_entry->qp.ref_count == 1) {
  732                 vmci_queue_header_init((*produce_q)->q_header, *handle);
  733                 vmci_queue_header_init((*consume_q)->q_header, *handle);
  734         }
  735 
  736         vmci_mutex_release(&qp_guest_endpoints.mutex);
  737 
  738         return (VMCI_SUCCESS);
  739 
  740 error:
  741         vmci_mutex_release(&qp_guest_endpoints.mutex);
  742         if (queue_pair_entry) {
  743                 /* The queues will be freed inside the destroy routine. */
  744                 qp_guest_endpoint_destroy(queue_pair_entry);
  745         } else {
  746                 if (my_produce_q)
  747                         vmci_free_queue(my_produce_q, produce_size);
  748                 if (my_consume_q)
  749                         vmci_free_queue(my_consume_q, consume_size);
  750         }
  751         return (result);
  752 
  753 error_keep_entry:
  754         /* This path should only be used when an existing entry was found. */
  755         ASSERT(queue_pair_entry->qp.ref_count > 0);
  756         vmci_mutex_release(&qp_guest_endpoints.mutex);
  757         return (result);
  758 }
  759 
  760 /*
  761  *------------------------------------------------------------------------------
  762  *
  763  * vmci_queue_pair_detach_hypercall --
  764  *
  765  *     Helper to make a QueuePairDetach hypercall when the driver is supporting
  766  *     a guest device.
  767  *
  768  * Results:
  769  *     Result of the hypercall.
  770  *
  771  * Side effects:
  772  *     None.
  773  *
  774  *------------------------------------------------------------------------------
  775  */
  776 
  777 int
  778 vmci_queue_pair_detach_hypercall(struct vmci_handle handle)
  779 {
  780         struct vmci_queue_pair_detach_msg detach_msg;
  781 
  782         detach_msg.hdr.dst = VMCI_MAKE_HANDLE(VMCI_HYPERVISOR_CONTEXT_ID,
  783             VMCI_QUEUEPAIR_DETACH);
  784         detach_msg.hdr.src = VMCI_ANON_SRC_HANDLE;
  785         detach_msg.hdr.payload_size = sizeof(handle);
  786         detach_msg.handle = handle;
  787 
  788         return (vmci_send_datagram((struct vmci_datagram *)&detach_msg));
  789 }
  790 
  791 /*
  792  *------------------------------------------------------------------------------
  793  *
  794  * vmci_queue_pair_detach_guest_work --
  795  *
  796  *     Helper for VMCI QueuePair detach interface. Frees the physical pages for
  797  *     the queue pair.
  798  *
  799  * Results:
  800  *     Success or failure.
  801  *
  802  * Side effects:
  803  *     Memory may be freed.
  804  *
  805  *------------------------------------------------------------------------------
  806  */
  807 
  808 static int
  809 vmci_queue_pair_detach_guest_work(struct vmci_handle handle)
  810 {
  811         struct qp_guest_endpoint *entry;
  812         int result;
  813         uint32_t ref_count;
  814 
  815         ASSERT(!VMCI_HANDLE_INVALID(handle));
  816 
  817         vmci_mutex_acquire(&qp_guest_endpoints.mutex);
  818 
  819         entry = (struct qp_guest_endpoint *)queue_pair_list_find_entry(
  820             &qp_guest_endpoints, handle);
  821         if (!entry) {
  822                 vmci_mutex_release(&qp_guest_endpoints.mutex);
  823                 return (VMCI_ERROR_NOT_FOUND);
  824         }
  825 
  826         ASSERT(entry->qp.ref_count >= 1);
  827 
  828         if (entry->qp.flags & VMCI_QPFLAG_LOCAL) {
  829                 result = VMCI_SUCCESS;
  830 
  831                 if (entry->qp.ref_count > 1) {
  832                         result = queue_pair_notify_peer_local(false, handle);
  833 
  834                         /*
  835                          * We can fail to notify a local queuepair because we
  836                          * can't allocate. We still want to release the entry
  837                          * if that happens, so don't bail out yet.
  838                          */
  839                 }
  840         } else {
  841                 result = vmci_queue_pair_detach_hypercall(handle);
  842                 if (entry->hibernate_failure) {
  843                         if (result == VMCI_ERROR_NOT_FOUND) {
  844                                 /*
  845                                  * If a queue pair detach failed when entering
  846                                  * hibernation, the guest driver and the device
  847                                  * may disagree on its existence when coming
  848                                  * out of hibernation. The guest driver will
  849                                  * regard it as a non-local queue pair, but
  850                                  * the device state is gone, since the device
  851                                  * has been powered off. In this case, we
  852                                  * treat the queue pair as a local queue pair
  853                                  * with no peer.
  854                                  */
  855 
  856                                 ASSERT(entry->qp.ref_count == 1);
  857                                 result = VMCI_SUCCESS;
  858                         }
  859                 }
  860                 if (result < VMCI_SUCCESS) {
  861                         /*
  862                          * We failed to notify a non-local queuepair. That other
  863                          * queuepair might still be accessing the shared
  864                          * memory, so don't release the entry yet. It will get
  865                          * cleaned up by vmci_queue_pair_Exit() if necessary
  866                          * (assuming we are going away, otherwise why did this
  867                          * fail?).
  868                          */
  869 
  870                         vmci_mutex_release(&qp_guest_endpoints.mutex);
  871                         return (result);
  872                 }
  873         }
  874 
  875         /*
  876          * If we get here then we either failed to notify a local queuepair, or
  877          * we succeeded in all cases.  Release the entry if required.
  878          */
  879 
  880         entry->qp.ref_count--;
  881         if (entry->qp.ref_count == 0)
  882                 queue_pair_list_remove_entry(&qp_guest_endpoints, &entry->qp);
  883 
  884         /* If we didn't remove the entry, this could change once we unlock. */
  885         ref_count = entry ? entry->qp.ref_count :
  886             0xffffffff; /*
  887                          * Value does not matter, silence the
  888                          * compiler.
  889                          */
  890 
  891         vmci_mutex_release(&qp_guest_endpoints.mutex);
  892 
  893         if (ref_count == 0)
  894                 qp_guest_endpoint_destroy(entry);
  895         return (result);
  896 }
  897 
  898 /*
  899  *------------------------------------------------------------------------------
  900  *
  901  * queue_pair_notify_peer_local --
  902  *
  903  *     Dispatches a queue pair event message directly into the local event
  904  *     queue.
  905  *
  906  * Results:
  907  *     VMCI_SUCCESS on success, error code otherwise
  908  *
  909  * Side effects:
  910  *     None.
  911  *
  912  *------------------------------------------------------------------------------
  913  */
  914 
  915 static int
  916 queue_pair_notify_peer_local(bool attach, struct vmci_handle handle)
  917 {
  918         struct vmci_event_msg *e_msg;
  919         struct vmci_event_payload_qp *e_payload;
  920         /* buf is only 48 bytes. */
  921         vmci_id context_id;
  922         context_id = vmci_get_context_id();
  923         char buf[sizeof(*e_msg) + sizeof(*e_payload)];
  924 
  925         e_msg = (struct vmci_event_msg *)buf;
  926         e_payload = vmci_event_msg_payload(e_msg);
  927 
  928         e_msg->hdr.dst = VMCI_MAKE_HANDLE(context_id, VMCI_EVENT_HANDLER);
  929         e_msg->hdr.src = VMCI_MAKE_HANDLE(VMCI_HYPERVISOR_CONTEXT_ID,
  930             VMCI_CONTEXT_RESOURCE_ID);
  931         e_msg->hdr.payload_size = sizeof(*e_msg) + sizeof(*e_payload) -
  932             sizeof(e_msg->hdr);
  933         e_msg->event_data.event = attach ? VMCI_EVENT_QP_PEER_ATTACH :
  934             VMCI_EVENT_QP_PEER_DETACH;
  935         e_payload->peer_id = context_id;
  936         e_payload->handle = handle;
  937 
  938         return (vmci_event_dispatch((struct vmci_datagram *)e_msg));
  939 }

Cache object: 3b45f6f1c216a5bfc2c41073e2472ef8


[ 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.