163a93856SMark Peek /*-
23eeb7511SMark Peek * Copyright (c) 2018 VMware, Inc.
363a93856SMark Peek *
48c302b2eSMark Peek * SPDX-License-Identifier: (BSD-2-Clause OR GPL-2.0)
563a93856SMark Peek */
663a93856SMark Peek
763a93856SMark Peek /* This file implements Queue accessor methods. */
863a93856SMark Peek
963a93856SMark Peek /*
1063a93856SMark Peek * vmci_qpair is an interface that hides the queue pair internals. Rather than
1163a93856SMark Peek * access each queue in a pair directly, operations are performed on the queue
1263a93856SMark Peek * as a whole. This is simpler and less error-prone, and allows for future
1363a93856SMark Peek * queue pair features to be added under the hood with no change to the client
1463a93856SMark Peek * code.
1563a93856SMark Peek */
1663a93856SMark Peek
1763a93856SMark Peek #include <sys/cdefs.h>
1863a93856SMark Peek #include "vmci_kernel_api.h"
1963a93856SMark Peek #include "vmci_kernel_defs.h"
2063a93856SMark Peek #include "vmci_kernel_if.h"
2163a93856SMark Peek #include "vmci_queue.h"
2263a93856SMark Peek #include "vmci_queue_pair.h"
2363a93856SMark Peek
2463a93856SMark Peek /* This structure is opaque to the clients. */
2563a93856SMark Peek struct vmci_qpair {
2663a93856SMark Peek struct vmci_handle handle;
2763a93856SMark Peek struct vmci_queue *produce_q;
2863a93856SMark Peek struct vmci_queue *consume_q;
2963a93856SMark Peek uint64_t produce_q_size;
3063a93856SMark Peek uint64_t consume_q_size;
3163a93856SMark Peek vmci_id peer;
3263a93856SMark Peek uint32_t flags;
3363a93856SMark Peek vmci_privilege_flags priv_flags;
3463a93856SMark Peek uint32_t blocked;
3563a93856SMark Peek vmci_event event;
3663a93856SMark Peek };
3763a93856SMark Peek
3863a93856SMark Peek static void vmci_qpair_get_queue_headers(const struct vmci_qpair *qpair,
3963a93856SMark Peek struct vmci_queue_header **produce_q_header,
4063a93856SMark Peek struct vmci_queue_header **consume_q_header);
4163a93856SMark Peek
4263a93856SMark Peek /*
4363a93856SMark Peek *------------------------------------------------------------------------------
4463a93856SMark Peek *
4563a93856SMark Peek * vmci_queue_add_producer_tail --
4663a93856SMark Peek *
4763a93856SMark Peek * Helper routine to increment the Producer Tail.
4863a93856SMark Peek *
4963a93856SMark Peek * Results:
5063a93856SMark Peek * VMCI_ERROR_NOT_FOUND if the vmm_world registered with the queue cannot
5163a93856SMark Peek * be found. Otherwise VMCI_SUCCESS.
5263a93856SMark Peek *
5363a93856SMark Peek * Side effects:
5463a93856SMark Peek * None.
5563a93856SMark Peek *
5663a93856SMark Peek *------------------------------------------------------------------------------
5763a93856SMark Peek */
5863a93856SMark Peek
5963a93856SMark Peek static inline int
vmci_queue_add_producer_tail(struct vmci_queue * queue,size_t add,uint64_t queue_size)6063a93856SMark Peek vmci_queue_add_producer_tail(struct vmci_queue *queue,
6163a93856SMark Peek size_t add, uint64_t queue_size)
6263a93856SMark Peek {
6363a93856SMark Peek
6463a93856SMark Peek vmci_queue_header_add_producer_tail(queue->q_header, add, queue_size);
6563a93856SMark Peek return (VMCI_SUCCESS);
6663a93856SMark Peek }
6763a93856SMark Peek
6863a93856SMark Peek /*
6963a93856SMark Peek *------------------------------------------------------------------------------
7063a93856SMark Peek *
7163a93856SMark Peek * vmci_queue_add_consumer_head --
7263a93856SMark Peek *
7363a93856SMark Peek * Helper routine to increment the Consumer Head.
7463a93856SMark Peek *
7563a93856SMark Peek * Results:
7663a93856SMark Peek * VMCI_ERROR_NOT_FOUND if the vmm_world registered with the queue cannot
7763a93856SMark Peek * be found. Otherwise VMCI_SUCCESS.
7863a93856SMark Peek *
7963a93856SMark Peek * Side effects:
8063a93856SMark Peek * None.
8163a93856SMark Peek *
8263a93856SMark Peek *------------------------------------------------------------------------------
8363a93856SMark Peek */
8463a93856SMark Peek
8563a93856SMark Peek static inline int
vmci_queue_add_consumer_head(struct vmci_queue * queue,size_t add,uint64_t queue_size)8663a93856SMark Peek vmci_queue_add_consumer_head(struct vmci_queue *queue,
8763a93856SMark Peek size_t add, uint64_t queue_size)
8863a93856SMark Peek {
8963a93856SMark Peek
9063a93856SMark Peek vmci_queue_header_add_consumer_head(queue->q_header, add, queue_size);
9163a93856SMark Peek return (VMCI_SUCCESS);
9263a93856SMark Peek }
9363a93856SMark Peek
9463a93856SMark Peek /*
9563a93856SMark Peek *------------------------------------------------------------------------------
9663a93856SMark Peek *
9763a93856SMark Peek * vmci_qpair_get_queue_headers --
9863a93856SMark Peek *
9963a93856SMark Peek * Helper routine that will retrieve the produce and consume headers of a
10063a93856SMark Peek * given queue pair.
10163a93856SMark Peek *
10263a93856SMark Peek * Results:
10363a93856SMark Peek * VMCI_SUCCESS if either current or saved queue headers are found.
10463a93856SMark Peek * Appropriate error code otherwise.
10563a93856SMark Peek *
10663a93856SMark Peek * Side effects:
10763a93856SMark Peek * None.
10863a93856SMark Peek *
10963a93856SMark Peek *------------------------------------------------------------------------------
11063a93856SMark Peek */
11163a93856SMark Peek
11263a93856SMark Peek static void
vmci_qpair_get_queue_headers(const struct vmci_qpair * qpair,struct vmci_queue_header ** produce_q_header,struct vmci_queue_header ** consume_q_header)11363a93856SMark Peek vmci_qpair_get_queue_headers(const struct vmci_qpair *qpair,
11463a93856SMark Peek struct vmci_queue_header **produce_q_header,
11563a93856SMark Peek struct vmci_queue_header **consume_q_header)
11663a93856SMark Peek {
11763a93856SMark Peek
11863a93856SMark Peek ASSERT((qpair->produce_q != NULL) && (qpair->consume_q != NULL));
11963a93856SMark Peek *produce_q_header = qpair->produce_q->q_header;
12063a93856SMark Peek *consume_q_header = qpair->consume_q->q_header;
12163a93856SMark Peek }
12263a93856SMark Peek
12363a93856SMark Peek /*
12463a93856SMark Peek *------------------------------------------------------------------------------
12563a93856SMark Peek *
12663a93856SMark Peek * vmci_qpair_alloc --
12763a93856SMark Peek *
12863a93856SMark Peek * This is the client interface for allocating the memory for a vmci_qpair
12963a93856SMark Peek * structure and then attaching to the underlying queue. If an error occurs
13063a93856SMark Peek * allocating the memory for the vmci_qpair structure, no attempt is made to
13163a93856SMark Peek * attach. If an error occurs attaching, then there's the vmci_qpair
13263a93856SMark Peek * structure is freed.
13363a93856SMark Peek *
13463a93856SMark Peek * Results:
13563a93856SMark Peek * An err, if < 0.
13663a93856SMark Peek *
13763a93856SMark Peek * Side effects:
13863a93856SMark Peek * None.
13963a93856SMark Peek *
14063a93856SMark Peek *------------------------------------------------------------------------------
14163a93856SMark Peek */
14263a93856SMark Peek
14363a93856SMark Peek int
vmci_qpair_alloc(struct vmci_qpair ** qpair,struct vmci_handle * handle,uint64_t produce_q_size,uint64_t consume_q_size,vmci_id peer,uint32_t flags,vmci_privilege_flags priv_flags)14463a93856SMark Peek vmci_qpair_alloc(struct vmci_qpair **qpair, struct vmci_handle *handle,
14563a93856SMark Peek uint64_t produce_q_size, uint64_t consume_q_size, vmci_id peer,
14663a93856SMark Peek uint32_t flags, vmci_privilege_flags priv_flags)
14763a93856SMark Peek {
14863a93856SMark Peek struct vmci_qpair *my_qpair;
14963a93856SMark Peek int retval;
15063a93856SMark Peek
15163a93856SMark Peek /*
15263a93856SMark Peek * Restrict the size of a queuepair. Though the device enforces a limit
15363a93856SMark Peek * on the total amount of memory that can be allocated to queuepairs for
15463a93856SMark Peek * a guest, we avoid unnecessarily allocating a lot of memory. Also, we
15563a93856SMark Peek * try to allocate this memory before we make the queuepair allocation
15663a93856SMark Peek * hypercall.
15763a93856SMark Peek *
15863a93856SMark Peek * (Note that this doesn't prevent all cases; a user with only this much
15963a93856SMark Peek * physical memory could still get into trouble.) The error used by the
16063a93856SMark Peek * device is NO_RESOURCES, so use that here too.
16163a93856SMark Peek */
16263a93856SMark Peek
16363a93856SMark Peek if (produce_q_size + consume_q_size <
16463a93856SMark Peek MAX(produce_q_size, consume_q_size) ||
16563a93856SMark Peek produce_q_size + consume_q_size > VMCI_MAX_GUEST_QP_MEMORY)
16663a93856SMark Peek return (VMCI_ERROR_NO_RESOURCES);
16763a93856SMark Peek
16863a93856SMark Peek if (flags & VMCI_QPFLAG_NONBLOCK)
16963a93856SMark Peek return (VMCI_ERROR_INVALID_ARGS);
17063a93856SMark Peek
17163a93856SMark Peek my_qpair = vmci_alloc_kernel_mem(sizeof(*my_qpair), VMCI_MEMORY_NORMAL);
17263a93856SMark Peek if (!my_qpair)
17363a93856SMark Peek return (VMCI_ERROR_NO_MEM);
17463a93856SMark Peek
17563a93856SMark Peek my_qpair->produce_q_size = produce_q_size;
17663a93856SMark Peek my_qpair->consume_q_size = consume_q_size;
17763a93856SMark Peek my_qpair->peer = peer;
17863a93856SMark Peek my_qpair->flags = flags;
17963a93856SMark Peek my_qpair->priv_flags = priv_flags;
18063a93856SMark Peek
18163a93856SMark Peek retval = vmci_queue_pair_alloc(handle, &my_qpair->produce_q,
18263a93856SMark Peek my_qpair->produce_q_size, &my_qpair->consume_q,
18363a93856SMark Peek my_qpair->consume_q_size, my_qpair->peer, my_qpair->flags,
18463a93856SMark Peek my_qpair->priv_flags);
18563a93856SMark Peek
18663a93856SMark Peek if (retval < VMCI_SUCCESS) {
18763a93856SMark Peek vmci_free_kernel_mem(my_qpair, sizeof(*my_qpair));
18863a93856SMark Peek return (retval);
18963a93856SMark Peek }
19063a93856SMark Peek
19163a93856SMark Peek *qpair = my_qpair;
19263a93856SMark Peek my_qpair->handle = *handle;
19363a93856SMark Peek
19463a93856SMark Peek return (retval);
19563a93856SMark Peek }
19663a93856SMark Peek
19763a93856SMark Peek /*
19863a93856SMark Peek *------------------------------------------------------------------------------
19963a93856SMark Peek *
20063a93856SMark Peek * vmci_qpair_detach --
20163a93856SMark Peek *
20263a93856SMark Peek * This is the client interface for detaching from a vmci_qpair. Note that
20363a93856SMark Peek * this routine will free the memory allocated for the vmci_qpair structure,
20463a93856SMark Peek * too.
20563a93856SMark Peek *
20663a93856SMark Peek * Results:
20763a93856SMark Peek * An error, if < 0.
20863a93856SMark Peek *
20963a93856SMark Peek * Side effects:
21063a93856SMark Peek * Will clear the caller's pointer to the vmci_qpair structure.
21163a93856SMark Peek *
21263a93856SMark Peek *------------------------------------------------------------------------------
21363a93856SMark Peek */
21463a93856SMark Peek
21563a93856SMark Peek int
vmci_qpair_detach(struct vmci_qpair ** qpair)21663a93856SMark Peek vmci_qpair_detach(struct vmci_qpair **qpair)
21763a93856SMark Peek {
21863a93856SMark Peek struct vmci_qpair *old_qpair;
21963a93856SMark Peek int result;
22063a93856SMark Peek
22163a93856SMark Peek if (!qpair || !(*qpair))
22263a93856SMark Peek return (VMCI_ERROR_INVALID_ARGS);
22363a93856SMark Peek
22463a93856SMark Peek old_qpair = *qpair;
22563a93856SMark Peek result = vmci_queue_pair_detach(old_qpair->handle);
22663a93856SMark Peek
22763a93856SMark Peek /*
22863a93856SMark Peek * The guest can fail to detach for a number of reasons, and if it does
22963a93856SMark Peek * so, it will cleanup the entry (if there is one). We need to release
23063a93856SMark Peek * the qpair struct here; there isn't much the caller can do, and we
23163a93856SMark Peek * don't want to leak.
23263a93856SMark Peek */
23363a93856SMark Peek
23463a93856SMark Peek if (old_qpair->flags & VMCI_QPFLAG_LOCAL)
23563a93856SMark Peek vmci_destroy_event(&old_qpair->event);
23663a93856SMark Peek
23763a93856SMark Peek vmci_free_kernel_mem(old_qpair, sizeof(*old_qpair));
23863a93856SMark Peek *qpair = NULL;
23963a93856SMark Peek
24063a93856SMark Peek return (result);
24163a93856SMark Peek }
24263a93856SMark Peek
24363a93856SMark Peek /*
24463a93856SMark Peek *------------------------------------------------------------------------------
24563a93856SMark Peek *
24663a93856SMark Peek * vmci_qpair_get_produce_indexes --
24763a93856SMark Peek *
24863a93856SMark Peek * This is the client interface for getting the current indexes of the
24963a93856SMark Peek * qpair from the point of the view of the caller as the producer.
25063a93856SMark Peek *
25163a93856SMark Peek * Results:
25263a93856SMark Peek * err, if < 0
25363a93856SMark Peek * Success otherwise.
25463a93856SMark Peek *
25563a93856SMark Peek * Side effects:
25663a93856SMark Peek * None.
25763a93856SMark Peek *
25863a93856SMark Peek *------------------------------------------------------------------------------
25963a93856SMark Peek */
26063a93856SMark Peek
26163a93856SMark Peek int
vmci_qpair_get_produce_indexes(const struct vmci_qpair * qpair,uint64_t * producer_tail,uint64_t * consumer_head)26263a93856SMark Peek vmci_qpair_get_produce_indexes(const struct vmci_qpair *qpair,
26363a93856SMark Peek uint64_t *producer_tail, uint64_t *consumer_head)
26463a93856SMark Peek {
26563a93856SMark Peek struct vmci_queue_header *consume_q_header;
26663a93856SMark Peek struct vmci_queue_header *produce_q_header;
26763a93856SMark Peek
26863a93856SMark Peek if (!qpair)
26963a93856SMark Peek return (VMCI_ERROR_INVALID_ARGS);
27063a93856SMark Peek
27163a93856SMark Peek vmci_qpair_get_queue_headers(qpair, &produce_q_header,
27263a93856SMark Peek &consume_q_header);
27363a93856SMark Peek vmci_queue_header_get_pointers(produce_q_header, consume_q_header,
27463a93856SMark Peek producer_tail, consumer_head);
27563a93856SMark Peek
27663a93856SMark Peek if ((producer_tail && *producer_tail >= qpair->produce_q_size) ||
27763a93856SMark Peek (consumer_head && *consumer_head >= qpair->produce_q_size))
27863a93856SMark Peek return (VMCI_ERROR_INVALID_SIZE);
27963a93856SMark Peek
28063a93856SMark Peek return (VMCI_SUCCESS);
28163a93856SMark Peek }
28263a93856SMark Peek
28363a93856SMark Peek /*
28463a93856SMark Peek *------------------------------------------------------------------------------
28563a93856SMark Peek *
28663a93856SMark Peek * vmci_qpair_get_consume_indexes --
28763a93856SMark Peek *
28863a93856SMark Peek * This is the client interface for getting the current indexes of the
28963a93856SMark Peek * QPair from the point of the view of the caller as the consumer.
29063a93856SMark Peek *
29163a93856SMark Peek * Results:
29263a93856SMark Peek * err, if < 0
29363a93856SMark Peek * Success otherwise.
29463a93856SMark Peek *
29563a93856SMark Peek * Side effects:
29663a93856SMark Peek * None.
29763a93856SMark Peek *
29863a93856SMark Peek *------------------------------------------------------------------------------
29963a93856SMark Peek */
30063a93856SMark Peek
30163a93856SMark Peek int
vmci_qpair_get_consume_indexes(const struct vmci_qpair * qpair,uint64_t * consumer_tail,uint64_t * producer_head)30263a93856SMark Peek vmci_qpair_get_consume_indexes(const struct vmci_qpair *qpair,
30363a93856SMark Peek uint64_t *consumer_tail, uint64_t *producer_head)
30463a93856SMark Peek {
30563a93856SMark Peek struct vmci_queue_header *consume_q_header;
30663a93856SMark Peek struct vmci_queue_header *produce_q_header;
30763a93856SMark Peek
30863a93856SMark Peek if (!qpair)
30963a93856SMark Peek return (VMCI_ERROR_INVALID_ARGS);
31063a93856SMark Peek
31163a93856SMark Peek vmci_qpair_get_queue_headers(qpair, &produce_q_header,
31263a93856SMark Peek &consume_q_header);
31363a93856SMark Peek vmci_queue_header_get_pointers(consume_q_header, produce_q_header,
31463a93856SMark Peek consumer_tail, producer_head);
31563a93856SMark Peek
31663a93856SMark Peek if ((consumer_tail && *consumer_tail >= qpair->consume_q_size) ||
31763a93856SMark Peek (producer_head && *producer_head >= qpair->consume_q_size))
31863a93856SMark Peek return (VMCI_ERROR_INVALID_SIZE);
31963a93856SMark Peek
32063a93856SMark Peek return (VMCI_SUCCESS);
32163a93856SMark Peek }
32263a93856SMark Peek
32363a93856SMark Peek /*
32463a93856SMark Peek *------------------------------------------------------------------------------
32563a93856SMark Peek *
32663a93856SMark Peek * vmci_qpair_produce_free_space --
32763a93856SMark Peek *
32863a93856SMark Peek * This is the client interface for getting the amount of free space in the
32963a93856SMark Peek * QPair from the point of the view of the caller as the producer which is
33063a93856SMark Peek * the common case.
33163a93856SMark Peek *
33263a93856SMark Peek * Results:
33363a93856SMark Peek * Err, if < 0.
33463a93856SMark Peek * Full queue if = 0.
33563a93856SMark Peek * Number of available bytes into which data can be enqueued if > 0.
33663a93856SMark Peek *
33763a93856SMark Peek * Side effects:
33863a93856SMark Peek * None.
33963a93856SMark Peek *
34063a93856SMark Peek *------------------------------------------------------------------------------
34163a93856SMark Peek */
34263a93856SMark Peek
34363a93856SMark Peek int64_t
vmci_qpair_produce_free_space(const struct vmci_qpair * qpair)34463a93856SMark Peek vmci_qpair_produce_free_space(const struct vmci_qpair *qpair)
34563a93856SMark Peek {
34663a93856SMark Peek struct vmci_queue_header *consume_q_header;
34763a93856SMark Peek struct vmci_queue_header *produce_q_header;
34863a93856SMark Peek int64_t result;
34963a93856SMark Peek
35063a93856SMark Peek if (!qpair)
35163a93856SMark Peek return (VMCI_ERROR_INVALID_ARGS);
35263a93856SMark Peek
35363a93856SMark Peek vmci_qpair_get_queue_headers(qpair, &produce_q_header,
35463a93856SMark Peek &consume_q_header);
35563a93856SMark Peek result = vmci_queue_header_free_space(produce_q_header, consume_q_header,
35663a93856SMark Peek qpair->produce_q_size);
35763a93856SMark Peek
35863a93856SMark Peek return (result);
35963a93856SMark Peek }
36063a93856SMark Peek
36163a93856SMark Peek /*
36263a93856SMark Peek *------------------------------------------------------------------------------
36363a93856SMark Peek *
36463a93856SMark Peek * vmci_qpair_consume_free_space --
36563a93856SMark Peek *
36663a93856SMark Peek * This is the client interface for getting the amount of free space in the
36763a93856SMark Peek * QPair from the point of the view of the caller as the consumer which is
36863a93856SMark Peek * not the common case (see vmci_qpair_Produce_free_space(), above).
36963a93856SMark Peek *
37063a93856SMark Peek * Results:
37163a93856SMark Peek * Err, if < 0.
37263a93856SMark Peek * Full queue if = 0.
37363a93856SMark Peek * Number of available bytes into which data can be enqueued if > 0.
37463a93856SMark Peek *
37563a93856SMark Peek * Side effects:
37663a93856SMark Peek * None.
37763a93856SMark Peek *
37863a93856SMark Peek *------------------------------------------------------------------------------
37963a93856SMark Peek */
38063a93856SMark Peek
38163a93856SMark Peek int64_t
vmci_qpair_consume_free_space(const struct vmci_qpair * qpair)38263a93856SMark Peek vmci_qpair_consume_free_space(const struct vmci_qpair *qpair)
38363a93856SMark Peek {
38463a93856SMark Peek struct vmci_queue_header *consume_q_header;
38563a93856SMark Peek struct vmci_queue_header *produce_q_header;
38663a93856SMark Peek int64_t result;
38763a93856SMark Peek
38863a93856SMark Peek if (!qpair)
38963a93856SMark Peek return (VMCI_ERROR_INVALID_ARGS);
39063a93856SMark Peek
39163a93856SMark Peek vmci_qpair_get_queue_headers(qpair, &produce_q_header,
39263a93856SMark Peek &consume_q_header);
39363a93856SMark Peek result = vmci_queue_header_free_space(consume_q_header, produce_q_header,
39463a93856SMark Peek qpair->consume_q_size);
39563a93856SMark Peek
39663a93856SMark Peek return (result);
39763a93856SMark Peek }
39863a93856SMark Peek
39963a93856SMark Peek /*
40063a93856SMark Peek *------------------------------------------------------------------------------
40163a93856SMark Peek *
40263a93856SMark Peek * vmci_qpair_produce_buf_ready --
40363a93856SMark Peek *
40463a93856SMark Peek * This is the client interface for getting the amount of enqueued data in
40563a93856SMark Peek * the QPair from the point of the view of the caller as the producer which
40663a93856SMark Peek * is not the common case (see vmci_qpair_Consume_buf_ready(), above).
40763a93856SMark Peek *
40863a93856SMark Peek * Results:
40963a93856SMark Peek * Err, if < 0.
41063a93856SMark Peek * Empty queue if = 0.
41163a93856SMark Peek * Number of bytes ready to be dequeued if > 0.
41263a93856SMark Peek *
41363a93856SMark Peek * Side effects:
41463a93856SMark Peek * None.
41563a93856SMark Peek *
41663a93856SMark Peek *------------------------------------------------------------------------------
41763a93856SMark Peek */
41863a93856SMark Peek
41963a93856SMark Peek int64_t
vmci_qpair_produce_buf_ready(const struct vmci_qpair * qpair)42063a93856SMark Peek vmci_qpair_produce_buf_ready(const struct vmci_qpair *qpair)
42163a93856SMark Peek {
42263a93856SMark Peek struct vmci_queue_header *consume_q_header;
42363a93856SMark Peek struct vmci_queue_header *produce_q_header;
42463a93856SMark Peek int64_t result;
42563a93856SMark Peek
42663a93856SMark Peek if (!qpair)
42763a93856SMark Peek return (VMCI_ERROR_INVALID_ARGS);
42863a93856SMark Peek
42963a93856SMark Peek vmci_qpair_get_queue_headers(qpair, &produce_q_header,
43063a93856SMark Peek &consume_q_header);
43163a93856SMark Peek result = vmci_queue_header_buf_ready(produce_q_header, consume_q_header,
43263a93856SMark Peek qpair->produce_q_size);
43363a93856SMark Peek
43463a93856SMark Peek return (result);
43563a93856SMark Peek }
43663a93856SMark Peek
43763a93856SMark Peek /*
43863a93856SMark Peek *------------------------------------------------------------------------------
43963a93856SMark Peek *
44063a93856SMark Peek * vmci_qpair_consume_buf_ready --
44163a93856SMark Peek *
44263a93856SMark Peek * This is the client interface for getting the amount of enqueued data in
44363a93856SMark Peek * the QPair from the point of the view of the caller as the consumer which
44463a93856SMark Peek * is the normal case.
44563a93856SMark Peek *
44663a93856SMark Peek * Results:
44763a93856SMark Peek * Err, if < 0.
44863a93856SMark Peek * Empty queue if = 0.
44963a93856SMark Peek * Number of bytes ready to be dequeued if > 0.
45063a93856SMark Peek *
45163a93856SMark Peek * Side effects:
45263a93856SMark Peek * None.
45363a93856SMark Peek *
45463a93856SMark Peek *------------------------------------------------------------------------------
45563a93856SMark Peek */
45663a93856SMark Peek
45763a93856SMark Peek int64_t
vmci_qpair_consume_buf_ready(const struct vmci_qpair * qpair)45863a93856SMark Peek vmci_qpair_consume_buf_ready(const struct vmci_qpair *qpair)
45963a93856SMark Peek {
46063a93856SMark Peek struct vmci_queue_header *consume_q_header;
46163a93856SMark Peek struct vmci_queue_header *produce_q_header;
46263a93856SMark Peek int64_t result;
46363a93856SMark Peek
46463a93856SMark Peek if (!qpair)
46563a93856SMark Peek return (VMCI_ERROR_INVALID_ARGS);
46663a93856SMark Peek
46763a93856SMark Peek vmci_qpair_get_queue_headers(qpair, &produce_q_header,
46863a93856SMark Peek &consume_q_header);
46963a93856SMark Peek result = vmci_queue_header_buf_ready(consume_q_header, produce_q_header,
47063a93856SMark Peek qpair->consume_q_size);
47163a93856SMark Peek
47263a93856SMark Peek return (result);
47363a93856SMark Peek }
47463a93856SMark Peek
47563a93856SMark Peek /*
47663a93856SMark Peek *------------------------------------------------------------------------------
47763a93856SMark Peek *
47863a93856SMark Peek * enqueue --
47963a93856SMark Peek *
48063a93856SMark Peek * Enqueues a given buffer to the produce queue using the provided function.
48163a93856SMark Peek * As many bytes as possible (space available in the queue) are enqueued.
48263a93856SMark Peek *
48363a93856SMark Peek * Results:
48463a93856SMark Peek * VMCI_ERROR_QUEUEPAIR_NOSPACE if no space was available to enqueue data.
48563a93856SMark Peek * VMCI_ERROR_INVALID_SIZE, if any queue pointer is outside the queue
48663a93856SMark Peek * (as defined by the queue size).
487*f2e13c27SGordon Bergling * VMCI_ERROR_INVALID_ARGS, if an error occurred when accessing the buffer.
48863a93856SMark Peek * VMCI_ERROR_QUEUEPAIR_NOTATTACHED, if the queue pair pages aren't
48963a93856SMark Peek * available.
49063a93856SMark Peek * Otherwise, the number of bytes written to the queue is returned.
49163a93856SMark Peek *
49263a93856SMark Peek * Side effects:
49363a93856SMark Peek * Updates the tail pointer of the produce queue.
49463a93856SMark Peek *
49563a93856SMark Peek *------------------------------------------------------------------------------
49663a93856SMark Peek */
49763a93856SMark Peek
49863a93856SMark Peek static ssize_t
enqueue(struct vmci_queue * produce_q,struct vmci_queue * consume_q,const uint64_t produce_q_size,const void * buf,size_t buf_size,int buf_type,vmci_memcpy_to_queue_func memcpy_to_queue,bool can_block)49963a93856SMark Peek enqueue(struct vmci_queue *produce_q, struct vmci_queue *consume_q,
50063a93856SMark Peek const uint64_t produce_q_size, const void *buf, size_t buf_size,
50163a93856SMark Peek int buf_type, vmci_memcpy_to_queue_func memcpy_to_queue, bool can_block)
50263a93856SMark Peek {
50363a93856SMark Peek ssize_t result;
50463a93856SMark Peek size_t written;
50563a93856SMark Peek int64_t free_space;
50663a93856SMark Peek uint64_t tail;
50763a93856SMark Peek
50863a93856SMark Peek ASSERT((produce_q != NULL) && (consume_q != NULL));
50963a93856SMark Peek
51063a93856SMark Peek free_space = vmci_queue_header_free_space(produce_q->q_header,
51163a93856SMark Peek consume_q->q_header,
51263a93856SMark Peek produce_q_size);
51363a93856SMark Peek if (free_space == 0)
51463a93856SMark Peek return (VMCI_ERROR_QUEUEPAIR_NOSPACE);
51563a93856SMark Peek
51663a93856SMark Peek if (free_space < VMCI_SUCCESS)
51763a93856SMark Peek return ((ssize_t)free_space);
51863a93856SMark Peek
51963a93856SMark Peek written = (size_t)(free_space > buf_size ? buf_size : free_space);
52063a93856SMark Peek tail = vmci_queue_header_producer_tail(produce_q->q_header);
52163a93856SMark Peek if (LIKELY(tail + written < produce_q_size))
52263a93856SMark Peek result = memcpy_to_queue(produce_q, tail, buf, 0, written,
52363a93856SMark Peek buf_type, can_block);
52463a93856SMark Peek else {
52563a93856SMark Peek /* Tail pointer wraps around. */
52663a93856SMark Peek
52763a93856SMark Peek const size_t tmp = (size_t)(produce_q_size - tail);
52863a93856SMark Peek
52963a93856SMark Peek result = memcpy_to_queue(produce_q, tail, buf, 0, tmp, buf_type,
53063a93856SMark Peek can_block);
53163a93856SMark Peek if (result >= VMCI_SUCCESS)
53263a93856SMark Peek result = memcpy_to_queue(produce_q, 0, buf, tmp,
53363a93856SMark Peek written - tmp, buf_type, can_block);
53463a93856SMark Peek }
53563a93856SMark Peek
53663a93856SMark Peek if (result < VMCI_SUCCESS)
53763a93856SMark Peek return (result);
53863a93856SMark Peek
53963a93856SMark Peek result = vmci_queue_add_producer_tail(produce_q, written,
54063a93856SMark Peek produce_q_size);
54163a93856SMark Peek if (result < VMCI_SUCCESS)
54263a93856SMark Peek return (result);
54363a93856SMark Peek return (written);
54463a93856SMark Peek }
54563a93856SMark Peek
54663a93856SMark Peek /*
54763a93856SMark Peek *------------------------------------------------------------------------------
54863a93856SMark Peek *
54963a93856SMark Peek * dequeue --
55063a93856SMark Peek *
55163a93856SMark Peek * Dequeues data (if available) from the given consume queue. Writes data
55263a93856SMark Peek * to the user provided buffer using the provided function.
55363a93856SMark Peek *
55463a93856SMark Peek * Results:
55563a93856SMark Peek * VMCI_ERROR_QUEUEPAIR_NODATA if no data was available to dequeue.
55663a93856SMark Peek * VMCI_ERROR_INVALID_SIZE, if any queue pointer is outside the queue
55763a93856SMark Peek * (as defined by the queue size).
558*f2e13c27SGordon Bergling * VMCI_ERROR_INVALID_ARGS, if an error occurred when accessing the buffer.
55963a93856SMark Peek * VMCI_ERROR_NOT_FOUND, if the vmm_world registered with the queue pair
56063a93856SMark Peek * cannot be found.
56163a93856SMark Peek * Otherwise the number of bytes dequeued is returned.
56263a93856SMark Peek *
56363a93856SMark Peek * Side effects:
56463a93856SMark Peek * Updates the head pointer of the consume queue.
56563a93856SMark Peek *
56663a93856SMark Peek *------------------------------------------------------------------------------
56763a93856SMark Peek */
56863a93856SMark Peek
56963a93856SMark Peek static ssize_t
dequeue(struct vmci_queue * produce_q,struct vmci_queue * consume_q,const uint64_t consume_q_size,void * buf,size_t buf_size,int buf_type,vmci_memcpy_from_queue_func memcpy_from_queue,bool update_consumer,bool can_block)57063a93856SMark Peek dequeue(struct vmci_queue *produce_q,
57163a93856SMark Peek struct vmci_queue *consume_q, const uint64_t consume_q_size, void *buf,
57263a93856SMark Peek size_t buf_size, int buf_type,
57363a93856SMark Peek vmci_memcpy_from_queue_func memcpy_from_queue, bool update_consumer,
57463a93856SMark Peek bool can_block)
57563a93856SMark Peek {
57663a93856SMark Peek ssize_t result;
57763a93856SMark Peek size_t read;
57863a93856SMark Peek int64_t buf_ready;
57963a93856SMark Peek uint64_t head;
58063a93856SMark Peek
58163a93856SMark Peek ASSERT((produce_q != NULL) && (consume_q != NULL));
58263a93856SMark Peek
58363a93856SMark Peek buf_ready = vmci_queue_header_buf_ready(consume_q->q_header,
58463a93856SMark Peek produce_q->q_header, consume_q_size);
58563a93856SMark Peek if (buf_ready == 0)
58663a93856SMark Peek return (VMCI_ERROR_QUEUEPAIR_NODATA);
58763a93856SMark Peek if (buf_ready < VMCI_SUCCESS)
58863a93856SMark Peek return ((ssize_t)buf_ready);
58963a93856SMark Peek
59063a93856SMark Peek read = (size_t)(buf_ready > buf_size ? buf_size : buf_ready);
59163a93856SMark Peek head = vmci_queue_header_consumer_head(produce_q->q_header);
59263a93856SMark Peek if (LIKELY(head + read < consume_q_size))
59363a93856SMark Peek result = memcpy_from_queue(buf, 0, consume_q, head, read,
59463a93856SMark Peek buf_type, can_block);
59563a93856SMark Peek else {
59663a93856SMark Peek /* Head pointer wraps around. */
59763a93856SMark Peek
59863a93856SMark Peek const size_t tmp = (size_t)(consume_q_size - head);
59963a93856SMark Peek
60063a93856SMark Peek result = memcpy_from_queue(buf, 0, consume_q, head, tmp,
60163a93856SMark Peek buf_type, can_block);
60263a93856SMark Peek if (result >= VMCI_SUCCESS)
60363a93856SMark Peek result = memcpy_from_queue(buf, tmp, consume_q, 0,
60463a93856SMark Peek read - tmp, buf_type, can_block);
60563a93856SMark Peek }
60663a93856SMark Peek
60763a93856SMark Peek if (result < VMCI_SUCCESS)
60863a93856SMark Peek return (result);
60963a93856SMark Peek
61063a93856SMark Peek if (update_consumer) {
61163a93856SMark Peek result = vmci_queue_add_consumer_head(produce_q, read,
61263a93856SMark Peek consume_q_size);
61363a93856SMark Peek if (result < VMCI_SUCCESS)
61463a93856SMark Peek return (result);
61563a93856SMark Peek }
61663a93856SMark Peek
61763a93856SMark Peek return (read);
61863a93856SMark Peek }
61963a93856SMark Peek
62063a93856SMark Peek /*
62163a93856SMark Peek *------------------------------------------------------------------------------
62263a93856SMark Peek *
62363a93856SMark Peek * vmci_qpair_enqueue --
62463a93856SMark Peek *
62563a93856SMark Peek * This is the client interface for enqueueing data into the queue.
62663a93856SMark Peek *
62763a93856SMark Peek * Results:
62863a93856SMark Peek * Err, if < 0.
62963a93856SMark Peek * Number of bytes enqueued if >= 0.
63063a93856SMark Peek *
63163a93856SMark Peek * Side effects:
63263a93856SMark Peek * None.
63363a93856SMark Peek *
63463a93856SMark Peek *------------------------------------------------------------------------------
63563a93856SMark Peek */
63663a93856SMark Peek
63763a93856SMark Peek ssize_t
vmci_qpair_enqueue(struct vmci_qpair * qpair,const void * buf,size_t buf_size,int buf_type)63863a93856SMark Peek vmci_qpair_enqueue(struct vmci_qpair *qpair, const void *buf, size_t buf_size,
63963a93856SMark Peek int buf_type)
64063a93856SMark Peek {
64163a93856SMark Peek ssize_t result;
64263a93856SMark Peek
64363a93856SMark Peek if (!qpair || !buf)
64463a93856SMark Peek return (VMCI_ERROR_INVALID_ARGS);
64563a93856SMark Peek
64663a93856SMark Peek result = enqueue(qpair->produce_q, qpair->consume_q,
64763a93856SMark Peek qpair->produce_q_size, buf, buf_size, buf_type,
64863a93856SMark Peek qpair->flags & VMCI_QPFLAG_LOCAL?
64963a93856SMark Peek vmci_memcpy_to_queue_local : vmci_memcpy_to_queue,
65063a93856SMark Peek !(qpair->flags & VMCI_QPFLAG_NONBLOCK));
65163a93856SMark Peek
65263a93856SMark Peek return (result);
65363a93856SMark Peek }
65463a93856SMark Peek
65563a93856SMark Peek /*
65663a93856SMark Peek *------------------------------------------------------------------------------
65763a93856SMark Peek *
65863a93856SMark Peek * vmci_qpair_dequeue --
65963a93856SMark Peek *
66063a93856SMark Peek * This is the client interface for dequeueing data from the queue.
66163a93856SMark Peek *
66263a93856SMark Peek * Results:
66363a93856SMark Peek * Err, if < 0.
66463a93856SMark Peek * Number of bytes dequeued if >= 0.
66563a93856SMark Peek *
66663a93856SMark Peek * Side effects:
66763a93856SMark Peek * None.
66863a93856SMark Peek *
66963a93856SMark Peek *------------------------------------------------------------------------------
67063a93856SMark Peek */
67163a93856SMark Peek
67263a93856SMark Peek ssize_t
vmci_qpair_dequeue(struct vmci_qpair * qpair,void * buf,size_t buf_size,int buf_type)67363a93856SMark Peek vmci_qpair_dequeue(struct vmci_qpair *qpair, void *buf, size_t buf_size,
67463a93856SMark Peek int buf_type)
67563a93856SMark Peek {
67663a93856SMark Peek ssize_t result;
67763a93856SMark Peek
67863a93856SMark Peek if (!qpair || !buf)
67963a93856SMark Peek return (VMCI_ERROR_INVALID_ARGS);
68063a93856SMark Peek
68163a93856SMark Peek result = dequeue(qpair->produce_q, qpair->consume_q,
68263a93856SMark Peek qpair->consume_q_size, buf, buf_size, buf_type,
68363a93856SMark Peek qpair->flags & VMCI_QPFLAG_LOCAL?
68463a93856SMark Peek vmci_memcpy_from_queue_local : vmci_memcpy_from_queue, true,
68563a93856SMark Peek !(qpair->flags & VMCI_QPFLAG_NONBLOCK));
68663a93856SMark Peek
68763a93856SMark Peek return (result);
68863a93856SMark Peek }
68963a93856SMark Peek
69063a93856SMark Peek /*
69163a93856SMark Peek *------------------------------------------------------------------------------
69263a93856SMark Peek *
69363a93856SMark Peek * vmci_qpair_peek --
69463a93856SMark Peek *
69563a93856SMark Peek * This is the client interface for peeking into a queue. (I.e., copy
69663a93856SMark Peek * data from the queue without updating the head pointer.)
69763a93856SMark Peek *
69863a93856SMark Peek * Results:
69963a93856SMark Peek * Err, if < 0.
70063a93856SMark Peek * Number of bytes peeked, if >= 0.
70163a93856SMark Peek *
70263a93856SMark Peek * Side effects:
70363a93856SMark Peek * None.
70463a93856SMark Peek *
70563a93856SMark Peek *------------------------------------------------------------------------------
70663a93856SMark Peek */
70763a93856SMark Peek
70863a93856SMark Peek ssize_t
vmci_qpair_peek(struct vmci_qpair * qpair,void * buf,size_t buf_size,int buf_type)70963a93856SMark Peek vmci_qpair_peek(struct vmci_qpair *qpair, void *buf, size_t buf_size,
71063a93856SMark Peek int buf_type)
71163a93856SMark Peek {
71263a93856SMark Peek ssize_t result;
71363a93856SMark Peek
71463a93856SMark Peek if (!qpair || !buf)
71563a93856SMark Peek return (VMCI_ERROR_INVALID_ARGS);
71663a93856SMark Peek
71763a93856SMark Peek result = dequeue(qpair->produce_q, qpair->consume_q,
71863a93856SMark Peek qpair->consume_q_size, buf, buf_size, buf_type,
71963a93856SMark Peek qpair->flags & VMCI_QPFLAG_LOCAL?
72063a93856SMark Peek vmci_memcpy_from_queue_local : vmci_memcpy_from_queue, false,
72163a93856SMark Peek !(qpair->flags & VMCI_QPFLAG_NONBLOCK));
72263a93856SMark Peek
72363a93856SMark Peek return (result);
72463a93856SMark Peek }
72563a93856SMark Peek
72663a93856SMark Peek /*
72763a93856SMark Peek *------------------------------------------------------------------------------
72863a93856SMark Peek *
72963a93856SMark Peek * vmci_qpair_enquev --
73063a93856SMark Peek *
73163a93856SMark Peek * This is the client interface for enqueueing data into the queue.
73263a93856SMark Peek *
73363a93856SMark Peek * Results:
73463a93856SMark Peek * Err, if < 0.
73563a93856SMark Peek * Number of bytes enqueued if >= 0.
73663a93856SMark Peek *
73763a93856SMark Peek * Side effects:
73863a93856SMark Peek * None.
73963a93856SMark Peek *
74063a93856SMark Peek *------------------------------------------------------------------------------
74163a93856SMark Peek */
74263a93856SMark Peek
74363a93856SMark Peek ssize_t
vmci_qpair_enquev(struct vmci_qpair * qpair,void * iov,size_t iov_size,int buf_type)74463a93856SMark Peek vmci_qpair_enquev(struct vmci_qpair *qpair, void *iov, size_t iov_size,
74563a93856SMark Peek int buf_type)
74663a93856SMark Peek {
74763a93856SMark Peek ssize_t result;
74863a93856SMark Peek
74963a93856SMark Peek if (!qpair || !iov)
75063a93856SMark Peek return (VMCI_ERROR_INVALID_ARGS);
75163a93856SMark Peek
75263a93856SMark Peek result = enqueue(qpair->produce_q, qpair->consume_q,
75363a93856SMark Peek qpair->produce_q_size, iov, iov_size, buf_type,
75463a93856SMark Peek qpair->flags & VMCI_QPFLAG_LOCAL?
75563a93856SMark Peek vmci_memcpy_to_queue_v_local : vmci_memcpy_to_queue_v,
75663a93856SMark Peek !(qpair->flags & VMCI_QPFLAG_NONBLOCK));
75763a93856SMark Peek
75863a93856SMark Peek return (result);
75963a93856SMark Peek }
76063a93856SMark Peek
76163a93856SMark Peek /*
76263a93856SMark Peek *------------------------------------------------------------------------------
76363a93856SMark Peek *
76463a93856SMark Peek * vmci_qpair_dequev --
76563a93856SMark Peek *
76663a93856SMark Peek * This is the client interface for dequeueing data from the queue.
76763a93856SMark Peek *
76863a93856SMark Peek * Results:
76963a93856SMark Peek * Err, if < 0.
77063a93856SMark Peek * Number of bytes dequeued if >= 0.
77163a93856SMark Peek *
77263a93856SMark Peek * Side effects:
77363a93856SMark Peek * None.
77463a93856SMark Peek *
77563a93856SMark Peek *------------------------------------------------------------------------------
77663a93856SMark Peek */
77763a93856SMark Peek
77863a93856SMark Peek ssize_t
vmci_qpair_dequev(struct vmci_qpair * qpair,void * iov,size_t iov_size,int buf_type)77963a93856SMark Peek vmci_qpair_dequev(struct vmci_qpair *qpair, void *iov, size_t iov_size,
78063a93856SMark Peek int buf_type)
78163a93856SMark Peek {
78263a93856SMark Peek ssize_t result;
78363a93856SMark Peek
78463a93856SMark Peek if (!qpair || !iov)
78563a93856SMark Peek return (VMCI_ERROR_INVALID_ARGS);
78663a93856SMark Peek
78763a93856SMark Peek result = dequeue(qpair->produce_q, qpair->consume_q,
78863a93856SMark Peek qpair->consume_q_size, iov, iov_size, buf_type,
78963a93856SMark Peek qpair->flags & VMCI_QPFLAG_LOCAL?
79063a93856SMark Peek vmci_memcpy_from_queue_v_local : vmci_memcpy_from_queue_v, true,
79163a93856SMark Peek !(qpair->flags & VMCI_QPFLAG_NONBLOCK));
79263a93856SMark Peek
79363a93856SMark Peek return (result);
79463a93856SMark Peek }
79563a93856SMark Peek
79663a93856SMark Peek /*
79763a93856SMark Peek *------------------------------------------------------------------------------
79863a93856SMark Peek *
79963a93856SMark Peek * vmci_qpair_peekv --
80063a93856SMark Peek *
80163a93856SMark Peek * This is the client interface for peeking into a queue. (I.e., copy
80263a93856SMark Peek * data from the queue without updating the head pointer.)
80363a93856SMark Peek *
80463a93856SMark Peek * Results:
80563a93856SMark Peek * Err, if < 0.
80663a93856SMark Peek * Number of bytes peeked, if >= 0.
80763a93856SMark Peek *
80863a93856SMark Peek * Side effects:
80963a93856SMark Peek * None.
81063a93856SMark Peek *
81163a93856SMark Peek *------------------------------------------------------------------------------
81263a93856SMark Peek */
81363a93856SMark Peek
81463a93856SMark Peek ssize_t
vmci_qpair_peekv(struct vmci_qpair * qpair,void * iov,size_t iov_size,int buf_type)81563a93856SMark Peek vmci_qpair_peekv(struct vmci_qpair *qpair, void *iov, size_t iov_size,
81663a93856SMark Peek int buf_type)
81763a93856SMark Peek {
81863a93856SMark Peek ssize_t result;
81963a93856SMark Peek
82063a93856SMark Peek if (!qpair || !iov)
82163a93856SMark Peek return (VMCI_ERROR_INVALID_ARGS);
82263a93856SMark Peek
82363a93856SMark Peek result = dequeue(qpair->produce_q, qpair->consume_q,
82463a93856SMark Peek qpair->consume_q_size, iov, iov_size, buf_type,
82563a93856SMark Peek qpair->flags & VMCI_QPFLAG_LOCAL?
82663a93856SMark Peek vmci_memcpy_from_queue_v_local : vmci_memcpy_from_queue_v, false,
82763a93856SMark Peek !(qpair->flags & VMCI_QPFLAG_NONBLOCK));
82863a93856SMark Peek
82963a93856SMark Peek return (result);
83063a93856SMark Peek }
831