#include <sys/cdefs.h>
#include "vmci_kernel_api.h"
#include "vmci_kernel_defs.h"
#include "vmci_kernel_if.h"
#include "vmci_queue.h"
#include "vmci_queue_pair.h"
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);
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);
}
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);
}
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;
}
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;
int retval;
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;
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);
}
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);
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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 {
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);
}
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 {
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}