8c302b2e86
Approved by: Vishnu Dasa <vdasa@vmware.com>
838 lines
22 KiB
C
838 lines
22 KiB
C
/*-
|
|
* Copyright (c) 2018 VMware, Inc. All Rights Reserved.
|
|
*
|
|
* SPDX-License-Identifier: (BSD-2-Clause OR GPL-2.0)
|
|
*/
|
|
|
|
/* This file implements Queue accessor methods. */
|
|
|
|
/*
|
|
* vmci_qpair is an interface that hides the queue pair internals. Rather than
|
|
* access each queue in a pair directly, operations are performed on the queue
|
|
* as a whole. This is simpler and less error-prone, and allows for future
|
|
* queue pair features to be added under the hood with no change to the client
|
|
* code.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
#include "vmci_kernel_api.h"
|
|
#include "vmci_kernel_defs.h"
|
|
#include "vmci_kernel_if.h"
|
|
#include "vmci_queue.h"
|
|
#include "vmci_queue_pair.h"
|
|
|
|
/* This structure is opaque to the clients. */
|
|
struct vmci_qpair {
|
|
struct vmci_handle handle;
|
|
struct vmci_queue *produce_q;
|
|
struct vmci_queue *consume_q;
|
|
uint64_t produce_q_size;
|
|
uint64_t consume_q_size;
|
|
vmci_id peer;
|
|
uint32_t flags;
|
|
vmci_privilege_flags priv_flags;
|
|
uint32_t blocked;
|
|
vmci_event event;
|
|
};
|
|
|
|
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);
|
|
|
|
/*
|
|
*------------------------------------------------------------------------------
|
|
*
|
|
* vmci_queue_add_producer_tail --
|
|
*
|
|
* Helper routine to increment the Producer Tail.
|
|
*
|
|
* Results:
|
|
* VMCI_ERROR_NOT_FOUND if the vmm_world registered with the queue cannot
|
|
* be found. Otherwise VMCI_SUCCESS.
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
*
|
|
*------------------------------------------------------------------------------
|
|
*/
|
|
|
|
static inline int
|
|
vmci_queue_add_producer_tail(struct vmci_queue *queue,
|
|
size_t add, uint64_t queue_size)
|
|
{
|
|
|
|
vmci_queue_header_add_producer_tail(queue->q_header, add, queue_size);
|
|
return (VMCI_SUCCESS);
|
|
}
|
|
|
|
/*
|
|
*------------------------------------------------------------------------------
|
|
*
|
|
* vmci_queue_add_consumer_head --
|
|
*
|
|
* Helper routine to increment the Consumer Head.
|
|
*
|
|
* Results:
|
|
* VMCI_ERROR_NOT_FOUND if the vmm_world registered with the queue cannot
|
|
* be found. Otherwise VMCI_SUCCESS.
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
*
|
|
*------------------------------------------------------------------------------
|
|
*/
|
|
|
|
static inline int
|
|
vmci_queue_add_consumer_head(struct vmci_queue *queue,
|
|
size_t add, uint64_t queue_size)
|
|
{
|
|
|
|
vmci_queue_header_add_consumer_head(queue->q_header, add, queue_size);
|
|
return (VMCI_SUCCESS);
|
|
}
|
|
|
|
/*
|
|
*------------------------------------------------------------------------------
|
|
*
|
|
* vmci_qpair_get_queue_headers --
|
|
*
|
|
* Helper routine that will retrieve the produce and consume headers of a
|
|
* given queue pair.
|
|
*
|
|
* Results:
|
|
* VMCI_SUCCESS if either current or saved queue headers are found.
|
|
* Appropriate error code otherwise.
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
*
|
|
*------------------------------------------------------------------------------
|
|
*/
|
|
|
|
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)
|
|
{
|
|
|
|
ASSERT((qpair->produce_q != NULL) && (qpair->consume_q != NULL));
|
|
*produce_q_header = qpair->produce_q->q_header;
|
|
*consume_q_header = qpair->consume_q->q_header;
|
|
}
|
|
|
|
/*
|
|
*------------------------------------------------------------------------------
|
|
*
|
|
* vmci_qpair_alloc --
|
|
*
|
|
* This is the client interface for allocating the memory for a vmci_qpair
|
|
* structure and then attaching to the underlying queue. If an error occurs
|
|
* allocating the memory for the vmci_qpair structure, no attempt is made to
|
|
* attach. If an error occurs attaching, then there's the vmci_qpair
|
|
* structure is freed.
|
|
*
|
|
* Results:
|
|
* An err, if < 0.
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
*
|
|
*------------------------------------------------------------------------------
|
|
*/
|
|
|
|
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)
|
|
{
|
|
struct vmci_qpair *my_qpair;
|
|
vmci_event_release_cb wakeup_cb;
|
|
void *client_data;
|
|
int retval;
|
|
|
|
/*
|
|
* Restrict the size of a queuepair. Though the device enforces a limit
|
|
* on the total amount of memory that can be allocated to queuepairs for
|
|
* a guest, we avoid unnecessarily allocating a lot of memory. Also, we
|
|
* try to allocate this memory before we make the queuepair allocation
|
|
* hypercall.
|
|
*
|
|
* (Note that this doesn't prevent all cases; a user with only this much
|
|
* physical memory could still get into trouble.) The error used by the
|
|
* device is NO_RESOURCES, so use that here too.
|
|
*/
|
|
|
|
if (produce_q_size + consume_q_size <
|
|
MAX(produce_q_size, consume_q_size) ||
|
|
produce_q_size + consume_q_size > VMCI_MAX_GUEST_QP_MEMORY)
|
|
return (VMCI_ERROR_NO_RESOURCES);
|
|
|
|
if (flags & VMCI_QPFLAG_NONBLOCK)
|
|
return (VMCI_ERROR_INVALID_ARGS);
|
|
|
|
my_qpair = vmci_alloc_kernel_mem(sizeof(*my_qpair), VMCI_MEMORY_NORMAL);
|
|
if (!my_qpair)
|
|
return (VMCI_ERROR_NO_MEM);
|
|
|
|
my_qpair->produce_q_size = produce_q_size;
|
|
my_qpair->consume_q_size = consume_q_size;
|
|
my_qpair->peer = peer;
|
|
my_qpair->flags = flags;
|
|
my_qpair->priv_flags = priv_flags;
|
|
|
|
client_data = NULL;
|
|
wakeup_cb = NULL;
|
|
|
|
retval = vmci_queue_pair_alloc(handle, &my_qpair->produce_q,
|
|
my_qpair->produce_q_size, &my_qpair->consume_q,
|
|
my_qpair->consume_q_size, my_qpair->peer, my_qpair->flags,
|
|
my_qpair->priv_flags);
|
|
|
|
if (retval < VMCI_SUCCESS) {
|
|
vmci_free_kernel_mem(my_qpair, sizeof(*my_qpair));
|
|
return (retval);
|
|
}
|
|
|
|
*qpair = my_qpair;
|
|
my_qpair->handle = *handle;
|
|
|
|
return (retval);
|
|
}
|
|
|
|
/*
|
|
*------------------------------------------------------------------------------
|
|
*
|
|
* vmci_qpair_detach --
|
|
*
|
|
* This is the client interface for detaching from a vmci_qpair. Note that
|
|
* this routine will free the memory allocated for the vmci_qpair structure,
|
|
* too.
|
|
*
|
|
* Results:
|
|
* An error, if < 0.
|
|
*
|
|
* Side effects:
|
|
* Will clear the caller's pointer to the vmci_qpair structure.
|
|
*
|
|
*------------------------------------------------------------------------------
|
|
*/
|
|
|
|
int
|
|
vmci_qpair_detach(struct vmci_qpair **qpair)
|
|
{
|
|
struct vmci_qpair *old_qpair;
|
|
int result;
|
|
|
|
if (!qpair || !(*qpair))
|
|
return (VMCI_ERROR_INVALID_ARGS);
|
|
|
|
old_qpair = *qpair;
|
|
result = vmci_queue_pair_detach(old_qpair->handle);
|
|
|
|
/*
|
|
* The guest can fail to detach for a number of reasons, and if it does
|
|
* so, it will cleanup the entry (if there is one). We need to release
|
|
* the qpair struct here; there isn't much the caller can do, and we
|
|
* don't want to leak.
|
|
*/
|
|
|
|
if (old_qpair->flags & VMCI_QPFLAG_LOCAL)
|
|
vmci_destroy_event(&old_qpair->event);
|
|
|
|
vmci_free_kernel_mem(old_qpair, sizeof(*old_qpair));
|
|
*qpair = NULL;
|
|
|
|
return (result);
|
|
}
|
|
|
|
/*
|
|
*------------------------------------------------------------------------------
|
|
*
|
|
* vmci_qpair_get_produce_indexes --
|
|
*
|
|
* This is the client interface for getting the current indexes of the
|
|
* qpair from the point of the view of the caller as the producer.
|
|
*
|
|
* Results:
|
|
* err, if < 0
|
|
* Success otherwise.
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
*
|
|
*------------------------------------------------------------------------------
|
|
*/
|
|
|
|
int
|
|
vmci_qpair_get_produce_indexes(const struct vmci_qpair *qpair,
|
|
uint64_t *producer_tail, uint64_t *consumer_head)
|
|
{
|
|
struct vmci_queue_header *consume_q_header;
|
|
struct vmci_queue_header *produce_q_header;
|
|
|
|
if (!qpair)
|
|
return (VMCI_ERROR_INVALID_ARGS);
|
|
|
|
vmci_qpair_get_queue_headers(qpair, &produce_q_header,
|
|
&consume_q_header);
|
|
vmci_queue_header_get_pointers(produce_q_header, consume_q_header,
|
|
producer_tail, consumer_head);
|
|
|
|
if ((producer_tail && *producer_tail >= qpair->produce_q_size) ||
|
|
(consumer_head && *consumer_head >= qpair->produce_q_size))
|
|
return (VMCI_ERROR_INVALID_SIZE);
|
|
|
|
return (VMCI_SUCCESS);
|
|
}
|
|
|
|
/*
|
|
*------------------------------------------------------------------------------
|
|
*
|
|
* vmci_qpair_get_consume_indexes --
|
|
*
|
|
* This is the client interface for getting the current indexes of the
|
|
* QPair from the point of the view of the caller as the consumer.
|
|
*
|
|
* Results:
|
|
* err, if < 0
|
|
* Success otherwise.
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
*
|
|
*------------------------------------------------------------------------------
|
|
*/
|
|
|
|
int
|
|
vmci_qpair_get_consume_indexes(const struct vmci_qpair *qpair,
|
|
uint64_t *consumer_tail, uint64_t *producer_head)
|
|
{
|
|
struct vmci_queue_header *consume_q_header;
|
|
struct vmci_queue_header *produce_q_header;
|
|
|
|
if (!qpair)
|
|
return (VMCI_ERROR_INVALID_ARGS);
|
|
|
|
vmci_qpair_get_queue_headers(qpair, &produce_q_header,
|
|
&consume_q_header);
|
|
vmci_queue_header_get_pointers(consume_q_header, produce_q_header,
|
|
consumer_tail, producer_head);
|
|
|
|
if ((consumer_tail && *consumer_tail >= qpair->consume_q_size) ||
|
|
(producer_head && *producer_head >= qpair->consume_q_size))
|
|
return (VMCI_ERROR_INVALID_SIZE);
|
|
|
|
return (VMCI_SUCCESS);
|
|
}
|
|
|
|
/*
|
|
*------------------------------------------------------------------------------
|
|
*
|
|
* vmci_qpair_produce_free_space --
|
|
*
|
|
* This is the client interface for getting the amount of free space in the
|
|
* QPair from the point of the view of the caller as the producer which is
|
|
* the common case.
|
|
*
|
|
* Results:
|
|
* Err, if < 0.
|
|
* Full queue if = 0.
|
|
* Number of available bytes into which data can be enqueued if > 0.
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
*
|
|
*------------------------------------------------------------------------------
|
|
*/
|
|
|
|
int64_t
|
|
vmci_qpair_produce_free_space(const struct vmci_qpair *qpair)
|
|
{
|
|
struct vmci_queue_header *consume_q_header;
|
|
struct vmci_queue_header *produce_q_header;
|
|
int64_t result;
|
|
|
|
if (!qpair)
|
|
return (VMCI_ERROR_INVALID_ARGS);
|
|
|
|
vmci_qpair_get_queue_headers(qpair, &produce_q_header,
|
|
&consume_q_header);
|
|
result = vmci_queue_header_free_space(produce_q_header, consume_q_header,
|
|
qpair->produce_q_size);
|
|
|
|
return (result);
|
|
}
|
|
|
|
/*
|
|
*------------------------------------------------------------------------------
|
|
*
|
|
* vmci_qpair_consume_free_space --
|
|
*
|
|
* This is the client interface for getting the amount of free space in the
|
|
* QPair from the point of the view of the caller as the consumer which is
|
|
* not the common case (see vmci_qpair_Produce_free_space(), above).
|
|
*
|
|
* Results:
|
|
* Err, if < 0.
|
|
* Full queue if = 0.
|
|
* Number of available bytes into which data can be enqueued if > 0.
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
*
|
|
*------------------------------------------------------------------------------
|
|
*/
|
|
|
|
int64_t
|
|
vmci_qpair_consume_free_space(const struct vmci_qpair *qpair)
|
|
{
|
|
struct vmci_queue_header *consume_q_header;
|
|
struct vmci_queue_header *produce_q_header;
|
|
int64_t result;
|
|
|
|
if (!qpair)
|
|
return (VMCI_ERROR_INVALID_ARGS);
|
|
|
|
vmci_qpair_get_queue_headers(qpair, &produce_q_header,
|
|
&consume_q_header);
|
|
result = vmci_queue_header_free_space(consume_q_header, produce_q_header,
|
|
qpair->consume_q_size);
|
|
|
|
return (result);
|
|
}
|
|
|
|
/*
|
|
*------------------------------------------------------------------------------
|
|
*
|
|
* vmci_qpair_produce_buf_ready --
|
|
*
|
|
* This is the client interface for getting the amount of enqueued data in
|
|
* the QPair from the point of the view of the caller as the producer which
|
|
* is not the common case (see vmci_qpair_Consume_buf_ready(), above).
|
|
*
|
|
* Results:
|
|
* Err, if < 0.
|
|
* Empty queue if = 0.
|
|
* Number of bytes ready to be dequeued if > 0.
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
*
|
|
*------------------------------------------------------------------------------
|
|
*/
|
|
|
|
int64_t
|
|
vmci_qpair_produce_buf_ready(const struct vmci_qpair *qpair)
|
|
{
|
|
struct vmci_queue_header *consume_q_header;
|
|
struct vmci_queue_header *produce_q_header;
|
|
int64_t result;
|
|
|
|
if (!qpair)
|
|
return (VMCI_ERROR_INVALID_ARGS);
|
|
|
|
vmci_qpair_get_queue_headers(qpair, &produce_q_header,
|
|
&consume_q_header);
|
|
result = vmci_queue_header_buf_ready(produce_q_header, consume_q_header,
|
|
qpair->produce_q_size);
|
|
|
|
return (result);
|
|
}
|
|
|
|
/*
|
|
*------------------------------------------------------------------------------
|
|
*
|
|
* vmci_qpair_consume_buf_ready --
|
|
*
|
|
* This is the client interface for getting the amount of enqueued data in
|
|
* the QPair from the point of the view of the caller as the consumer which
|
|
* is the normal case.
|
|
*
|
|
* Results:
|
|
* Err, if < 0.
|
|
* Empty queue if = 0.
|
|
* Number of bytes ready to be dequeued if > 0.
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
*
|
|
*------------------------------------------------------------------------------
|
|
*/
|
|
|
|
int64_t
|
|
vmci_qpair_consume_buf_ready(const struct vmci_qpair *qpair)
|
|
{
|
|
struct vmci_queue_header *consume_q_header;
|
|
struct vmci_queue_header *produce_q_header;
|
|
int64_t result;
|
|
|
|
if (!qpair)
|
|
return (VMCI_ERROR_INVALID_ARGS);
|
|
|
|
vmci_qpair_get_queue_headers(qpair, &produce_q_header,
|
|
&consume_q_header);
|
|
result = vmci_queue_header_buf_ready(consume_q_header, produce_q_header,
|
|
qpair->consume_q_size);
|
|
|
|
return (result);
|
|
}
|
|
|
|
/*
|
|
*------------------------------------------------------------------------------
|
|
*
|
|
* enqueue --
|
|
*
|
|
* Enqueues a given buffer to the produce queue using the provided function.
|
|
* As many bytes as possible (space available in the queue) are enqueued.
|
|
*
|
|
* Results:
|
|
* VMCI_ERROR_QUEUEPAIR_NOSPACE if no space was available to enqueue data.
|
|
* VMCI_ERROR_INVALID_SIZE, if any queue pointer is outside the queue
|
|
* (as defined by the queue size).
|
|
* VMCI_ERROR_INVALID_ARGS, if an error occured when accessing the buffer.
|
|
* VMCI_ERROR_QUEUEPAIR_NOTATTACHED, if the queue pair pages aren't
|
|
* available.
|
|
* Otherwise, the number of bytes written to the queue is returned.
|
|
*
|
|
* Side effects:
|
|
* Updates the tail pointer of the produce queue.
|
|
*
|
|
*------------------------------------------------------------------------------
|
|
*/
|
|
|
|
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)
|
|
{
|
|
ssize_t result;
|
|
size_t written;
|
|
int64_t free_space;
|
|
uint64_t tail;
|
|
|
|
ASSERT((produce_q != NULL) && (consume_q != NULL));
|
|
|
|
free_space = vmci_queue_header_free_space(produce_q->q_header,
|
|
consume_q->q_header,
|
|
produce_q_size);
|
|
if (free_space == 0)
|
|
return (VMCI_ERROR_QUEUEPAIR_NOSPACE);
|
|
|
|
if (free_space < VMCI_SUCCESS)
|
|
return ((ssize_t)free_space);
|
|
|
|
written = (size_t)(free_space > buf_size ? buf_size : free_space);
|
|
tail = vmci_queue_header_producer_tail(produce_q->q_header);
|
|
if (LIKELY(tail + written < produce_q_size))
|
|
result = memcpy_to_queue(produce_q, tail, buf, 0, written,
|
|
buf_type, can_block);
|
|
else {
|
|
/* Tail pointer wraps around. */
|
|
|
|
const size_t tmp = (size_t)(produce_q_size - tail);
|
|
|
|
result = memcpy_to_queue(produce_q, tail, buf, 0, tmp, buf_type,
|
|
can_block);
|
|
if (result >= VMCI_SUCCESS)
|
|
result = memcpy_to_queue(produce_q, 0, buf, tmp,
|
|
written - tmp, buf_type, can_block);
|
|
}
|
|
|
|
if (result < VMCI_SUCCESS)
|
|
return (result);
|
|
|
|
result = vmci_queue_add_producer_tail(produce_q, written,
|
|
produce_q_size);
|
|
if (result < VMCI_SUCCESS)
|
|
return (result);
|
|
return (written);
|
|
}
|
|
|
|
/*
|
|
*------------------------------------------------------------------------------
|
|
*
|
|
* dequeue --
|
|
*
|
|
* Dequeues data (if available) from the given consume queue. Writes data
|
|
* to the user provided buffer using the provided function.
|
|
*
|
|
* Results:
|
|
* VMCI_ERROR_QUEUEPAIR_NODATA if no data was available to dequeue.
|
|
* VMCI_ERROR_INVALID_SIZE, if any queue pointer is outside the queue
|
|
* (as defined by the queue size).
|
|
* VMCI_ERROR_INVALID_ARGS, if an error occured when accessing the buffer.
|
|
* VMCI_ERROR_NOT_FOUND, if the vmm_world registered with the queue pair
|
|
* cannot be found.
|
|
* Otherwise the number of bytes dequeued is returned.
|
|
*
|
|
* Side effects:
|
|
* Updates the head pointer of the consume queue.
|
|
*
|
|
*------------------------------------------------------------------------------
|
|
*/
|
|
|
|
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)
|
|
{
|
|
ssize_t result;
|
|
size_t read;
|
|
int64_t buf_ready;
|
|
uint64_t head;
|
|
|
|
ASSERT((produce_q != NULL) && (consume_q != NULL));
|
|
|
|
buf_ready = vmci_queue_header_buf_ready(consume_q->q_header,
|
|
produce_q->q_header, consume_q_size);
|
|
if (buf_ready == 0)
|
|
return (VMCI_ERROR_QUEUEPAIR_NODATA);
|
|
if (buf_ready < VMCI_SUCCESS)
|
|
return ((ssize_t)buf_ready);
|
|
|
|
read = (size_t)(buf_ready > buf_size ? buf_size : buf_ready);
|
|
head = vmci_queue_header_consumer_head(produce_q->q_header);
|
|
if (LIKELY(head + read < consume_q_size))
|
|
result = memcpy_from_queue(buf, 0, consume_q, head, read,
|
|
buf_type, can_block);
|
|
else {
|
|
/* Head pointer wraps around. */
|
|
|
|
const size_t tmp = (size_t)(consume_q_size - head);
|
|
|
|
result = memcpy_from_queue(buf, 0, consume_q, head, tmp,
|
|
buf_type, can_block);
|
|
if (result >= VMCI_SUCCESS)
|
|
result = memcpy_from_queue(buf, tmp, consume_q, 0,
|
|
read - tmp, buf_type, can_block);
|
|
}
|
|
|
|
if (result < VMCI_SUCCESS)
|
|
return (result);
|
|
|
|
if (update_consumer) {
|
|
result = vmci_queue_add_consumer_head(produce_q, read,
|
|
consume_q_size);
|
|
if (result < VMCI_SUCCESS)
|
|
return (result);
|
|
}
|
|
|
|
return (read);
|
|
}
|
|
|
|
/*
|
|
*------------------------------------------------------------------------------
|
|
*
|
|
* vmci_qpair_enqueue --
|
|
*
|
|
* This is the client interface for enqueueing data into the queue.
|
|
*
|
|
* Results:
|
|
* Err, if < 0.
|
|
* Number of bytes enqueued if >= 0.
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
*
|
|
*------------------------------------------------------------------------------
|
|
*/
|
|
|
|
ssize_t
|
|
vmci_qpair_enqueue(struct vmci_qpair *qpair, const void *buf, size_t buf_size,
|
|
int buf_type)
|
|
{
|
|
ssize_t result;
|
|
|
|
if (!qpair || !buf)
|
|
return (VMCI_ERROR_INVALID_ARGS);
|
|
|
|
result = enqueue(qpair->produce_q, qpair->consume_q,
|
|
qpair->produce_q_size, buf, buf_size, buf_type,
|
|
qpair->flags & VMCI_QPFLAG_LOCAL?
|
|
vmci_memcpy_to_queue_local : vmci_memcpy_to_queue,
|
|
!(qpair->flags & VMCI_QPFLAG_NONBLOCK));
|
|
|
|
return (result);
|
|
}
|
|
|
|
/*
|
|
*------------------------------------------------------------------------------
|
|
*
|
|
* vmci_qpair_dequeue --
|
|
*
|
|
* This is the client interface for dequeueing data from the queue.
|
|
*
|
|
* Results:
|
|
* Err, if < 0.
|
|
* Number of bytes dequeued if >= 0.
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
*
|
|
*------------------------------------------------------------------------------
|
|
*/
|
|
|
|
ssize_t
|
|
vmci_qpair_dequeue(struct vmci_qpair *qpair, void *buf, size_t buf_size,
|
|
int buf_type)
|
|
{
|
|
ssize_t result;
|
|
|
|
if (!qpair || !buf)
|
|
return (VMCI_ERROR_INVALID_ARGS);
|
|
|
|
result = dequeue(qpair->produce_q, qpair->consume_q,
|
|
qpair->consume_q_size, buf, buf_size, buf_type,
|
|
qpair->flags & VMCI_QPFLAG_LOCAL?
|
|
vmci_memcpy_from_queue_local : vmci_memcpy_from_queue, true,
|
|
!(qpair->flags & VMCI_QPFLAG_NONBLOCK));
|
|
|
|
return (result);
|
|
}
|
|
|
|
/*
|
|
*------------------------------------------------------------------------------
|
|
*
|
|
* vmci_qpair_peek --
|
|
*
|
|
* This is the client interface for peeking into a queue. (I.e., copy
|
|
* data from the queue without updating the head pointer.)
|
|
*
|
|
* Results:
|
|
* Err, if < 0.
|
|
* Number of bytes peeked, if >= 0.
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
*
|
|
*------------------------------------------------------------------------------
|
|
*/
|
|
|
|
ssize_t
|
|
vmci_qpair_peek(struct vmci_qpair *qpair, void *buf, size_t buf_size,
|
|
int buf_type)
|
|
{
|
|
ssize_t result;
|
|
|
|
if (!qpair || !buf)
|
|
return (VMCI_ERROR_INVALID_ARGS);
|
|
|
|
result = dequeue(qpair->produce_q, qpair->consume_q,
|
|
qpair->consume_q_size, buf, buf_size, buf_type,
|
|
qpair->flags & VMCI_QPFLAG_LOCAL?
|
|
vmci_memcpy_from_queue_local : vmci_memcpy_from_queue, false,
|
|
!(qpair->flags & VMCI_QPFLAG_NONBLOCK));
|
|
|
|
return (result);
|
|
}
|
|
|
|
/*
|
|
*------------------------------------------------------------------------------
|
|
*
|
|
* vmci_qpair_enquev --
|
|
*
|
|
* This is the client interface for enqueueing data into the queue.
|
|
*
|
|
* Results:
|
|
* Err, if < 0.
|
|
* Number of bytes enqueued if >= 0.
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
*
|
|
*------------------------------------------------------------------------------
|
|
*/
|
|
|
|
ssize_t
|
|
vmci_qpair_enquev(struct vmci_qpair *qpair, void *iov, size_t iov_size,
|
|
int buf_type)
|
|
{
|
|
ssize_t result;
|
|
|
|
if (!qpair || !iov)
|
|
return (VMCI_ERROR_INVALID_ARGS);
|
|
|
|
result = enqueue(qpair->produce_q, qpair->consume_q,
|
|
qpair->produce_q_size, iov, iov_size, buf_type,
|
|
qpair->flags & VMCI_QPFLAG_LOCAL?
|
|
vmci_memcpy_to_queue_v_local : vmci_memcpy_to_queue_v,
|
|
!(qpair->flags & VMCI_QPFLAG_NONBLOCK));
|
|
|
|
return (result);
|
|
}
|
|
|
|
/*
|
|
*------------------------------------------------------------------------------
|
|
*
|
|
* vmci_qpair_dequev --
|
|
*
|
|
* This is the client interface for dequeueing data from the queue.
|
|
*
|
|
* Results:
|
|
* Err, if < 0.
|
|
* Number of bytes dequeued if >= 0.
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
*
|
|
*------------------------------------------------------------------------------
|
|
*/
|
|
|
|
ssize_t
|
|
vmci_qpair_dequev(struct vmci_qpair *qpair, void *iov, size_t iov_size,
|
|
int buf_type)
|
|
{
|
|
ssize_t result;
|
|
|
|
if (!qpair || !iov)
|
|
return (VMCI_ERROR_INVALID_ARGS);
|
|
|
|
result = dequeue(qpair->produce_q, qpair->consume_q,
|
|
qpair->consume_q_size, iov, iov_size, buf_type,
|
|
qpair->flags & VMCI_QPFLAG_LOCAL?
|
|
vmci_memcpy_from_queue_v_local : vmci_memcpy_from_queue_v, true,
|
|
!(qpair->flags & VMCI_QPFLAG_NONBLOCK));
|
|
|
|
return (result);
|
|
}
|
|
|
|
/*
|
|
*------------------------------------------------------------------------------
|
|
*
|
|
* vmci_qpair_peekv --
|
|
*
|
|
* This is the client interface for peeking into a queue. (I.e., copy
|
|
* data from the queue without updating the head pointer.)
|
|
*
|
|
* Results:
|
|
* Err, if < 0.
|
|
* Number of bytes peeked, if >= 0.
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
*
|
|
*------------------------------------------------------------------------------
|
|
*/
|
|
|
|
ssize_t
|
|
vmci_qpair_peekv(struct vmci_qpair *qpair, void *iov, size_t iov_size,
|
|
int buf_type)
|
|
{
|
|
ssize_t result;
|
|
|
|
if (!qpair || !iov)
|
|
return (VMCI_ERROR_INVALID_ARGS);
|
|
|
|
result = dequeue(qpair->produce_q, qpair->consume_q,
|
|
qpair->consume_q_size, iov, iov_size, buf_type,
|
|
qpair->flags & VMCI_QPFLAG_LOCAL?
|
|
vmci_memcpy_from_queue_v_local : vmci_memcpy_from_queue_v, false,
|
|
!(qpair->flags & VMCI_QPFLAG_NONBLOCK));
|
|
|
|
return (result);
|
|
}
|