Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/vmware/vmci/vmci_defs.h
- This file was added.
Property | Old Value | New Value |
---|---|---|
svn:eol-style | null | native \ No newline at end of property |
svn:executable | null | * \ No newline at end of property |
svn:keywords | null | FreeBSD=%H \ No newline at end of property |
svn:mime-type | null | text/plain \ No newline at end of property |
/*- | |||||
* Copyright (c) 2018 VMware, Inc. All Rights Reserved. | |||||
* | |||||
* SPDX-License-Identifier: (BSD-2-Clause AND GPL-2.0) | |||||
*/ | |||||
#ifndef _VMCI_DEFS_H_ | |||||
#define _VMCI_DEFS_H_ | |||||
#include <sys/types.h> | |||||
#include <machine/atomic.h> | |||||
#include "vmci_kernel_defs.h" | |||||
#pragma GCC diagnostic ignored "-Wcast-qual" | |||||
/* Register offsets. */ | |||||
#define VMCI_STATUS_ADDR 0x00 | |||||
#define VMCI_CONTROL_ADDR 0x04 | |||||
#define VMCI_ICR_ADDR 0x08 | |||||
#define VMCI_IMR_ADDR 0x0c | |||||
#define VMCI_DATA_OUT_ADDR 0x10 | |||||
#define VMCI_DATA_IN_ADDR 0x14 | |||||
#define VMCI_CAPS_ADDR 0x18 | |||||
#define VMCI_RESULT_LOW_ADDR 0x1c | |||||
#define VMCI_RESULT_HIGH_ADDR 0x20 | |||||
/* Status register bits. */ | |||||
#define VMCI_STATUS_INT_ON 0x1 | |||||
/* Control register bits. */ | |||||
#define VMCI_CONTROL_RESET 0x1 | |||||
#define VMCI_CONTROL_INT_ENABLE 0x2 | |||||
#define VMCI_CONTROL_INT_DISABLE 0x4 | |||||
/* Capabilities register bits. */ | |||||
#define VMCI_CAPS_HYPERCALL 0x1 | |||||
#define VMCI_CAPS_GUESTCALL 0x2 | |||||
#define VMCI_CAPS_DATAGRAM 0x4 | |||||
#define VMCI_CAPS_NOTIFICATIONS 0x8 | |||||
/* Interrupt Cause register bits. */ | |||||
#define VMCI_ICR_DATAGRAM 0x1 | |||||
#define VMCI_ICR_NOTIFICATION 0x2 | |||||
/* Interrupt Mask register bits. */ | |||||
#define VMCI_IMR_DATAGRAM 0x1 | |||||
#define VMCI_IMR_NOTIFICATION 0x2 | |||||
/* Interrupt type. */ | |||||
typedef enum vmci_intr_type { | |||||
VMCI_INTR_TYPE_INTX = 0, | |||||
VMCI_INTR_TYPE_MSI = 1, | |||||
VMCI_INTR_TYPE_MSIX = 2 | |||||
} vmci_intr_type; | |||||
/* | |||||
* Maximum MSI/MSI-X interrupt vectors in the device. | |||||
*/ | |||||
#define VMCI_MAX_INTRS 2 | |||||
/* | |||||
* Supported interrupt vectors. There is one for each ICR value above, | |||||
* but here they indicate the position in the vector array/message ID. | |||||
*/ | |||||
#define VMCI_INTR_DATAGRAM 0 | |||||
#define VMCI_INTR_NOTIFICATION 1 | |||||
/* | |||||
* A single VMCI device has an upper limit of 128 MiB on the amount of | |||||
* memory that can be used for queue pairs. | |||||
*/ | |||||
#define VMCI_MAX_GUEST_QP_MEMORY (128 * 1024 * 1024) | |||||
/* | |||||
* We have a fixed set of resource IDs available in the VMX. | |||||
* This allows us to have a very simple implementation since we statically | |||||
* know how many will create datagram handles. If a new caller arrives and | |||||
* we have run out of slots we can manually increment the maximum size of | |||||
* available resource IDs. | |||||
*/ | |||||
typedef uint32_t vmci_resource; | |||||
/* VMCI reserved hypervisor datagram resource IDs. */ | |||||
#define VMCI_RESOURCES_QUERY 0 | |||||
#define VMCI_GET_CONTEXT_ID 1 | |||||
#define VMCI_SET_NOTIFY_BITMAP 2 | |||||
#define VMCI_DOORBELL_LINK 3 | |||||
#define VMCI_DOORBELL_UNLINK 4 | |||||
#define VMCI_DOORBELL_NOTIFY 5 | |||||
/* | |||||
* VMCI_DATAGRAM_REQUEST_MAP and VMCI_DATAGRAM_REMOVE_MAP are | |||||
* obsoleted by the removal of VM to VM communication. | |||||
*/ | |||||
#define VMCI_DATAGRAM_REQUEST_MAP 6 | |||||
#define VMCI_DATAGRAM_REMOVE_MAP 7 | |||||
#define VMCI_EVENT_SUBSCRIBE 8 | |||||
#define VMCI_EVENT_UNSUBSCRIBE 9 | |||||
#define VMCI_QUEUEPAIR_ALLOC 10 | |||||
#define VMCI_QUEUEPAIR_DETACH 11 | |||||
/* | |||||
* VMCI_VSOCK_VMX_LOOKUP was assigned to 12 for Fusion 3.0/3.1, | |||||
* WS 7.0/7.1 and ESX 4.1 | |||||
*/ | |||||
#define VMCI_HGFS_TRANSPORT 13 | |||||
#define VMCI_UNITY_PBRPC_REGISTER 14 | |||||
/* | |||||
* This resource is used for VMCI socket control packets sent to the | |||||
* hypervisor (CID 0) because RID 1 is already reserved. | |||||
*/ | |||||
#define VSOCK_PACKET_HYPERVISOR_RID 15 | |||||
#define VMCI_RESOURCE_MAX 16 | |||||
/* | |||||
* The core VMCI device functionality only requires the resource IDs of | |||||
* VMCI_QUEUEPAIR_DETACH and below. | |||||
*/ | |||||
#define VMCI_CORE_DEVICE_RESOURCE_MAX VMCI_QUEUEPAIR_DETACH | |||||
/* | |||||
* VMCI reserved host datagram resource IDs. | |||||
* vsock control channel has resource id 1. | |||||
*/ | |||||
#define VMCI_DVFILTER_DATA_PATH_DATAGRAM 2 | |||||
/* VMCI Ids. */ | |||||
typedef uint32_t vmci_id; | |||||
struct vmci_id_range { | |||||
int8_t action; /* VMCI_FA_X, for use in filters. */ | |||||
vmci_id begin; /* Beginning of range. */ | |||||
vmci_id end; /* End of range. */ | |||||
}; | |||||
struct vmci_handle { | |||||
vmci_id context; | |||||
vmci_id resource; | |||||
}; | |||||
static inline struct vmci_handle | |||||
VMCI_MAKE_HANDLE(vmci_id cid, vmci_id rid) | |||||
{ | |||||
struct vmci_handle h; | |||||
h.context = cid; | |||||
h.resource = rid; | |||||
return (h); | |||||
} | |||||
#define VMCI_HANDLE_TO_CONTEXT_ID(_handle) \ | |||||
((_handle).context) | |||||
#define VMCI_HANDLE_TO_RESOURCE_ID(_handle) \ | |||||
((_handle).resource) | |||||
#define VMCI_HANDLE_EQUAL(_h1, _h2) \ | |||||
((_h1).context == (_h2).context && (_h1).resource == (_h2).resource) | |||||
#define VMCI_INVALID_ID 0xFFFFFFFF | |||||
static const struct vmci_handle VMCI_INVALID_HANDLE = {VMCI_INVALID_ID, | |||||
VMCI_INVALID_ID}; | |||||
#define VMCI_HANDLE_INVALID(_handle) \ | |||||
VMCI_HANDLE_EQUAL((_handle), VMCI_INVALID_HANDLE) | |||||
/* | |||||
* The below defines can be used to send anonymous requests. | |||||
* This also indicates that no response is expected. | |||||
*/ | |||||
#define VMCI_ANON_SRC_CONTEXT_ID \ | |||||
VMCI_INVALID_ID | |||||
#define VMCI_ANON_SRC_RESOURCE_ID \ | |||||
VMCI_INVALID_ID | |||||
#define VMCI_ANON_SRC_HANDLE \ | |||||
VMCI_MAKE_HANDLE(VMCI_ANON_SRC_CONTEXT_ID, \ | |||||
VMCI_ANON_SRC_RESOURCE_ID) | |||||
/* The lowest 16 context ids are reserved for internal use. */ | |||||
#define VMCI_RESERVED_CID_LIMIT 16 | |||||
/* | |||||
* Hypervisor context id, used for calling into hypervisor | |||||
* supplied services from the VM. | |||||
*/ | |||||
#define VMCI_HYPERVISOR_CONTEXT_ID 0 | |||||
/* | |||||
* Well-known context id, a logical context that contains a set of | |||||
* well-known services. This context ID is now obsolete. | |||||
*/ | |||||
#define VMCI_WELL_KNOWN_CONTEXT_ID 1 | |||||
/* | |||||
* Context ID used by host endpoints. | |||||
*/ | |||||
#define VMCI_HOST_CONTEXT_ID 2 | |||||
#define VMCI_HOST_CONTEXT_INVALID_EVENT ((uintptr_t)~0) | |||||
#define VMCI_CONTEXT_IS_VM(_cid) \ | |||||
(VMCI_INVALID_ID != _cid && _cid > VMCI_HOST_CONTEXT_ID) | |||||
/* | |||||
* The VMCI_CONTEXT_RESOURCE_ID is used together with VMCI_MAKE_HANDLE to make | |||||
* handles that refer to a specific context. | |||||
*/ | |||||
#define VMCI_CONTEXT_RESOURCE_ID 0 | |||||
/* | |||||
*------------------------------------------------------------------------------ | |||||
* | |||||
* VMCI error codes. | |||||
* | |||||
*------------------------------------------------------------------------------ | |||||
*/ | |||||
#define VMCI_SUCCESS_QUEUEPAIR_ATTACH 5 | |||||
#define VMCI_SUCCESS_QUEUEPAIR_CREATE 4 | |||||
#define VMCI_SUCCESS_LAST_DETACH 3 | |||||
#define VMCI_SUCCESS_ACCESS_GRANTED 2 | |||||
#define VMCI_SUCCESS_ENTRY_DEAD 1 | |||||
#define VMCI_SUCCESS 0LL | |||||
#define VMCI_ERROR_INVALID_RESOURCE (-1) | |||||
#define VMCI_ERROR_INVALID_ARGS (-2) | |||||
#define VMCI_ERROR_NO_MEM (-3) | |||||
#define VMCI_ERROR_DATAGRAM_FAILED (-4) | |||||
#define VMCI_ERROR_MORE_DATA (-5) | |||||
#define VMCI_ERROR_NO_MORE_DATAGRAMS (-6) | |||||
#define VMCI_ERROR_NO_ACCESS (-7) | |||||
#define VMCI_ERROR_NO_HANDLE (-8) | |||||
#define VMCI_ERROR_DUPLICATE_ENTRY (-9) | |||||
#define VMCI_ERROR_DST_UNREACHABLE (-10) | |||||
#define VMCI_ERROR_PAYLOAD_TOO_LARGE (-11) | |||||
#define VMCI_ERROR_INVALID_PRIV (-12) | |||||
#define VMCI_ERROR_GENERIC (-13) | |||||
#define VMCI_ERROR_PAGE_ALREADY_SHARED (-14) | |||||
#define VMCI_ERROR_CANNOT_SHARE_PAGE (-15) | |||||
#define VMCI_ERROR_CANNOT_UNSHARE_PAGE (-16) | |||||
#define VMCI_ERROR_NO_PROCESS (-17) | |||||
#define VMCI_ERROR_NO_DATAGRAM (-18) | |||||
#define VMCI_ERROR_NO_RESOURCES (-19) | |||||
#define VMCI_ERROR_UNAVAILABLE (-20) | |||||
#define VMCI_ERROR_NOT_FOUND (-21) | |||||
#define VMCI_ERROR_ALREADY_EXISTS (-22) | |||||
#define VMCI_ERROR_NOT_PAGE_ALIGNED (-23) | |||||
#define VMCI_ERROR_INVALID_SIZE (-24) | |||||
#define VMCI_ERROR_REGION_ALREADY_SHARED (-25) | |||||
#define VMCI_ERROR_TIMEOUT (-26) | |||||
#define VMCI_ERROR_DATAGRAM_INCOMPLETE (-27) | |||||
#define VMCI_ERROR_INCORRECT_IRQL (-28) | |||||
#define VMCI_ERROR_EVENT_UNKNOWN (-29) | |||||
#define VMCI_ERROR_OBSOLETE (-30) | |||||
#define VMCI_ERROR_QUEUEPAIR_MISMATCH (-31) | |||||
#define VMCI_ERROR_QUEUEPAIR_NOTSET (-32) | |||||
#define VMCI_ERROR_QUEUEPAIR_NOTOWNER (-33) | |||||
#define VMCI_ERROR_QUEUEPAIR_NOTATTACHED (-34) | |||||
#define VMCI_ERROR_QUEUEPAIR_NOSPACE (-35) | |||||
#define VMCI_ERROR_QUEUEPAIR_NODATA (-36) | |||||
#define VMCI_ERROR_BUSMEM_INVALIDATION (-37) | |||||
#define VMCI_ERROR_MODULE_NOT_LOADED (-38) | |||||
#define VMCI_ERROR_DEVICE_NOT_FOUND (-39) | |||||
#define VMCI_ERROR_QUEUEPAIR_NOT_READY (-40) | |||||
#define VMCI_ERROR_WOULD_BLOCK (-41) | |||||
/* VMCI clients should return error code withing this range */ | |||||
#define VMCI_ERROR_CLIENT_MIN (-500) | |||||
#define VMCI_ERROR_CLIENT_MAX (-550) | |||||
/* Internal error codes. */ | |||||
#define VMCI_SHAREDMEM_ERROR_BAD_CONTEXT (-1000) | |||||
#define VMCI_PATH_MAX 256 | |||||
/* VMCI reserved events. */ | |||||
typedef uint32_t vmci_event_type; | |||||
#define VMCI_EVENT_CTX_ID_UPDATE 0 // Only applicable to guest | |||||
// endpoints | |||||
#define VMCI_EVENT_CTX_REMOVED 1 // Applicable to guest and host | |||||
#define VMCI_EVENT_QP_RESUMED 2 // Only applicable to guest | |||||
// endpoints | |||||
#define VMCI_EVENT_QP_PEER_ATTACH 3 // Applicable to guest, host | |||||
// and VMX | |||||
#define VMCI_EVENT_QP_PEER_DETACH 4 // Applicable to guest, host | |||||
// and VMX | |||||
#define VMCI_EVENT_MEM_ACCESS_ON 5 // Applicable to VMX and vmk. On | |||||
// vmk, this event has the | |||||
// Context payload type | |||||
#define VMCI_EVENT_MEM_ACCESS_OFF 6 // Applicable to VMX and vmk. | |||||
// Same as above for the payload | |||||
// type | |||||
#define VMCI_EVENT_GUEST_PAUSED 7 // Applicable to vmk. This | |||||
// event has the Context | |||||
// payload type | |||||
#define VMCI_EVENT_GUEST_UNPAUSED 8 // Applicable to vmk. Same as | |||||
// above for the payload type. | |||||
#define VMCI_EVENT_MAX 9 | |||||
/* | |||||
* Of the above events, a few are reserved for use in the VMX, and other | |||||
* endpoints (guest and host kernel) should not use them. For the rest of the | |||||
* events, we allow both host and guest endpoints to subscribe to them, to | |||||
* maintain the same API for host and guest endpoints. | |||||
*/ | |||||
#define VMCI_EVENT_VALID_VMX(_event) \ | |||||
(_event == VMCI_EVENT_QP_PEER_ATTACH || \ | |||||
_event == VMCI_EVENT_QP_PEER_DETACH || \ | |||||
_event == VMCI_EVENT_MEM_ACCESS_ON || \ | |||||
_event == VMCI_EVENT_MEM_ACCESS_OFF) | |||||
#define VMCI_EVENT_VALID(_event) \ | |||||
(_event < VMCI_EVENT_MAX && \ | |||||
_event != VMCI_EVENT_MEM_ACCESS_ON && \ | |||||
_event != VMCI_EVENT_MEM_ACCESS_OFF && \ | |||||
_event != VMCI_EVENT_GUEST_PAUSED && \ | |||||
_event != VMCI_EVENT_GUEST_UNPAUSED) | |||||
/* Reserved guest datagram resource ids. */ | |||||
#define VMCI_EVENT_HANDLER 0 | |||||
/* | |||||
* VMCI coarse-grained privileges (per context or host process/endpoint. An | |||||
* entity with the restricted flag is only allowed to interact with the | |||||
* hypervisor and trusted entities. | |||||
*/ | |||||
typedef uint32_t vmci_privilege_flags; | |||||
#define VMCI_PRIVILEGE_FLAG_RESTRICTED 0x01 | |||||
#define VMCI_PRIVILEGE_FLAG_TRUSTED 0x02 | |||||
#define VMCI_PRIVILEGE_ALL_FLAGS \ | |||||
(VMCI_PRIVILEGE_FLAG_RESTRICTED | VMCI_PRIVILEGE_FLAG_TRUSTED) | |||||
#define VMCI_NO_PRIVILEGE_FLAGS 0x00 | |||||
#define VMCI_DEFAULT_PROC_PRIVILEGE_FLAGS VMCI_NO_PRIVILEGE_FLAGS | |||||
#define VMCI_LEAST_PRIVILEGE_FLAGS VMCI_PRIVILEGE_FLAG_RESTRICTED | |||||
#define VMCI_MAX_PRIVILEGE_FLAGS VMCI_PRIVILEGE_FLAG_TRUSTED | |||||
/* 0 through VMCI_RESERVED_RESOURCE_ID_MAX are reserved. */ | |||||
#define VMCI_RESERVED_RESOURCE_ID_MAX 1023 | |||||
#define VMCI_DOMAIN_NAME_MAXLEN 32 | |||||
#define VMCI_LGPFX "vmci: " | |||||
/* | |||||
* struct vmci_queue_header | |||||
* | |||||
* A Queue cannot stand by itself as designed. Each Queue's header contains a | |||||
* pointer into itself (the producer_tail) and into its peer (consumer_head). | |||||
* The reason for the separation is one of accessibility: Each end-point can | |||||
* modify two things: where the next location to enqueue is within its produce_q | |||||
* (producer_tail); and where the next dequeue location is in its consume_q | |||||
* (consumer_head). | |||||
* | |||||
* An end-point cannot modify the pointers of its peer (guest to guest; NOTE | |||||
* that in the host both queue headers are mapped r/w). But, each end-point | |||||
* needs read access to both Queue header structures in order to determine how | |||||
* much space is used (or left) in the Queue. This is because for an end-point | |||||
* to know how full its produce_q is, it needs to use the consumer_head that | |||||
* points into the produce_q but -that- consumer_head is in the Queue header | |||||
* for that end-points consume_q. | |||||
* | |||||
* Thoroughly confused? Sorry. | |||||
* | |||||
* producer_tail: the point to enqueue new entrants. When you approach a line | |||||
* in a store, for example, you walk up to the tail. | |||||
* | |||||
* consumer_head: the point in the queue from which the next element is | |||||
* dequeued. In other words, who is next in line is he who is at the head of | |||||
* the line. | |||||
* | |||||
* Also, producer_tail points to an empty byte in the Queue, whereas | |||||
* consumer_head points to a valid byte of data (unless producer_tail == | |||||
* consumer_head in which case consumerHead does not point to a valid byte of | |||||
* data). | |||||
* | |||||
* For a queue of buffer 'size' bytes, the tail and head pointers will be in | |||||
* the range [0, size-1]. | |||||
* | |||||
* If produce_q_header->producer_tail == consume_q_header->consumer_head then | |||||
* the produce_q is empty. | |||||
*/ | |||||
struct vmci_queue_header { | |||||
/* All fields are 64bit and aligned. */ | |||||
struct vmci_handle handle; /* Identifier. */ | |||||
volatile uint64_t producer_tail; /* Offset in this queue. */ | |||||
volatile uint64_t consumer_head; /* Offset in peer queue. */ | |||||
}; | |||||
/* | |||||
* If one client of a QueuePair is a 32bit entity, we restrict the QueuePair | |||||
* size to be less than 4GB, and use 32bit atomic operations on the head and | |||||
* tail pointers. 64bit atomic read on a 32bit entity involves cmpxchg8b which | |||||
* is an atomic read-modify-write. This will cause traces to fire when a 32bit | |||||
* consumer tries to read the producer's tail pointer, for example, because the | |||||
* consumer has read-only access to the producer's tail pointer. | |||||
* | |||||
* We provide the following macros to invoke 32bit or 64bit atomic operations | |||||
* based on the architecture the code is being compiled on. | |||||
*/ | |||||
#ifdef __x86_64__ | |||||
#define QP_MAX_QUEUE_SIZE_ARCH CONST64U(0xffffffffffffffff) | |||||
#define qp_atomic_read_offset(x) atomic_load_64(x) | |||||
#define qp_atomic_write_offset(x, y) atomic_store_64(x, y) | |||||
#else /* __x86_64__ */ | |||||
/* | |||||
* Wrappers below are being used because atomic_store_<type> operates | |||||
* on a specific <type>. Likewise for atomic_load_<type> | |||||
*/ | |||||
static inline uint32_t | |||||
type_safe_atomic_read_32(void *var) | |||||
{ | |||||
return (atomic_load_32((volatile uint32_t *)(var))); | |||||
} | |||||
static inline void | |||||
type_safe_atomic_write_32(void *var, uint32_t val) | |||||
{ | |||||
atomic_store_32((volatile uint32_t *)(var), (uint32_t)(val)); | |||||
} | |||||
#define QP_MAX_QUEUE_SIZE_ARCH CONST64U(0xffffffff) | |||||
#define qp_atomic_read_offset(x) type_safe_atomic_read_32((void *)(x)) | |||||
#define qp_atomic_write_offset(x, y) \ | |||||
type_safe_atomic_write_32((void *)(x), (uint32_t)(y)) | |||||
#endif /* __x86_64__ */ | |||||
/* | |||||
*------------------------------------------------------------------------------ | |||||
* | |||||
* qp_add_pointer -- | |||||
* | |||||
* Helper to add a given offset to a head or tail pointer. Wraps the value | |||||
* of the pointer around the max size of the queue. | |||||
* | |||||
* Results: | |||||
* None. | |||||
* | |||||
* Side effects: | |||||
* None. | |||||
* | |||||
*------------------------------------------------------------------------------ | |||||
*/ | |||||
static inline void | |||||
qp_add_pointer(volatile uint64_t *var, size_t add, uint64_t size) | |||||
{ | |||||
uint64_t new_val = qp_atomic_read_offset(var); | |||||
if (new_val >= size - add) | |||||
new_val -= size; | |||||
new_val += add; | |||||
qp_atomic_write_offset(var, new_val); | |||||
} | |||||
/* | |||||
*------------------------------------------------------------------------------ | |||||
* | |||||
* vmci_queue_header_producer_tail -- | |||||
* | |||||
* Helper routine to get the Producer Tail from the supplied queue. | |||||
* | |||||
* Results: | |||||
* The contents of the queue's producer tail. | |||||
* | |||||
* Side effects: | |||||
* None. | |||||
* | |||||
*------------------------------------------------------------------------------ | |||||
*/ | |||||
static inline uint64_t | |||||
vmci_queue_header_producer_tail(const struct vmci_queue_header *q_header) | |||||
{ | |||||
struct vmci_queue_header *qh = (struct vmci_queue_header *)q_header; | |||||
return (qp_atomic_read_offset(&qh->producer_tail)); | |||||
} | |||||
/* | |||||
*------------------------------------------------------------------------------ | |||||
* | |||||
* vmci_queue_header_consumer_head -- | |||||
* | |||||
* Helper routine to get the Consumer Head from the supplied queue. | |||||
* | |||||
* Results: | |||||
* The contents of the queue's consumer tail. | |||||
* | |||||
* Side effects: | |||||
* None. | |||||
* | |||||
*------------------------------------------------------------------------------ | |||||
*/ | |||||
static inline uint64_t | |||||
vmci_queue_header_consumer_head(const struct vmci_queue_header *q_header) | |||||
{ | |||||
struct vmci_queue_header *qh = (struct vmci_queue_header *)q_header; | |||||
return (qp_atomic_read_offset(&qh->consumer_head)); | |||||
} | |||||
/* | |||||
*------------------------------------------------------------------------------ | |||||
* | |||||
* vmci_queue_header_add_producer_tail -- | |||||
* | |||||
* Helper routine to increment the Producer Tail. Fundamentally, | |||||
* qp_add_pointer() is used to manipulate the tail itself. | |||||
* | |||||
* Results: | |||||
* None. | |||||
* | |||||
* Side effects: | |||||
* None. | |||||
* | |||||
*------------------------------------------------------------------------------ | |||||
*/ | |||||
static inline void | |||||
vmci_queue_header_add_producer_tail(struct vmci_queue_header *q_header, | |||||
size_t add, uint64_t queue_size) | |||||
{ | |||||
qp_add_pointer(&q_header->producer_tail, add, queue_size); | |||||
} | |||||
/* | |||||
*------------------------------------------------------------------------------ | |||||
* | |||||
* vmci_queue_header_add_consumer_head -- | |||||
* | |||||
* Helper routine to increment the Consumer Head. Fundamentally, | |||||
* qp_add_pointer() is used to manipulate the head itself. | |||||
* | |||||
* Results: | |||||
* None. | |||||
* | |||||
* Side effects: | |||||
* None. | |||||
* | |||||
*------------------------------------------------------------------------------ | |||||
*/ | |||||
static inline void | |||||
vmci_queue_header_add_consumer_head(struct vmci_queue_header *q_header, | |||||
size_t add, uint64_t queue_size) | |||||
{ | |||||
qp_add_pointer(&q_header->consumer_head, add, queue_size); | |||||
} | |||||
/* | |||||
*------------------------------------------------------------------------------ | |||||
* | |||||
* vmci_queue_header_get_pointers -- | |||||
* | |||||
* Helper routine for getting the head and the tail pointer for a queue. | |||||
* Both the VMCIQueues are needed to get both the pointers for one queue. | |||||
* | |||||
* Results: | |||||
* None. | |||||
* | |||||
* Side effects: | |||||
* None. | |||||
* | |||||
*------------------------------------------------------------------------------ | |||||
*/ | |||||
static inline void | |||||
vmci_queue_header_get_pointers(const struct vmci_queue_header *produce_q_header, | |||||
const struct vmci_queue_header *consume_q_header, uint64_t *producer_tail, | |||||
uint64_t *consumer_head) | |||||
{ | |||||
if (producer_tail) | |||||
*producer_tail = | |||||
vmci_queue_header_producer_tail(produce_q_header); | |||||
if (consumer_head) | |||||
*consumer_head = | |||||
vmci_queue_header_consumer_head(consume_q_header); | |||||
} | |||||
/* | |||||
*------------------------------------------------------------------------------ | |||||
* | |||||
* vmci_queue_header_reset_pointers -- | |||||
* | |||||
* Reset the tail pointer (of "this" queue) and the head pointer (of "peer" | |||||
* queue). | |||||
* | |||||
* Results: | |||||
* None. | |||||
* | |||||
* Side effects: | |||||
* None. | |||||
* | |||||
*------------------------------------------------------------------------------ | |||||
*/ | |||||
static inline void | |||||
vmci_queue_header_reset_pointers(struct vmci_queue_header *q_header) | |||||
{ | |||||
qp_atomic_write_offset(&q_header->producer_tail, CONST64U(0)); | |||||
qp_atomic_write_offset(&q_header->consumer_head, CONST64U(0)); | |||||
} | |||||
/* | |||||
*------------------------------------------------------------------------------ | |||||
* | |||||
* vmci_queue_header_init -- | |||||
* | |||||
* Initializes a queue's state (head & tail pointers). | |||||
* | |||||
* Results: | |||||
* None. | |||||
* | |||||
* Side effects: | |||||
* None. | |||||
* | |||||
*------------------------------------------------------------------------------ | |||||
*/ | |||||
static inline void | |||||
vmci_queue_header_init(struct vmci_queue_header *q_header, | |||||
const struct vmci_handle handle) | |||||
{ | |||||
q_header->handle = handle; | |||||
vmci_queue_header_reset_pointers(q_header); | |||||
} | |||||
/* | |||||
*------------------------------------------------------------------------------ | |||||
* | |||||
* vmci_queue_header_free_space -- | |||||
* | |||||
* Finds available free space in a produce queue to enqueue more data or | |||||
* reports an error if queue pair corruption is detected. | |||||
* | |||||
* Results: | |||||
* Free space size in bytes or an error code. | |||||
* | |||||
* Side effects: | |||||
* None. | |||||
* | |||||
*------------------------------------------------------------------------------ | |||||
*/ | |||||
static inline int64_t | |||||
vmci_queue_header_free_space(const struct vmci_queue_header *produce_q_header, | |||||
const struct vmci_queue_header *consume_q_header, | |||||
const uint64_t produce_q_size) | |||||
{ | |||||
uint64_t free_space; | |||||
uint64_t head; | |||||
uint64_t tail; | |||||
tail = vmci_queue_header_producer_tail(produce_q_header); | |||||
head = vmci_queue_header_consumer_head(consume_q_header); | |||||
if (tail >= produce_q_size || head >= produce_q_size) | |||||
return (VMCI_ERROR_INVALID_SIZE); | |||||
/* | |||||
* Deduct 1 to avoid tail becoming equal to head which causes ambiguity. | |||||
* If head and tail are equal it means that the queue is empty. | |||||
*/ | |||||
if (tail >= head) | |||||
free_space = produce_q_size - (tail - head) - 1; | |||||
else | |||||
free_space = head - tail - 1; | |||||
return (free_space); | |||||
} | |||||
/* | |||||
*------------------------------------------------------------------------------ | |||||
* | |||||
* vmci_queue_header_buf_ready -- | |||||
* | |||||
* vmci_queue_header_free_space() does all the heavy lifting of determing | |||||
* the number of free bytes in a Queue. This routine, then subtracts that | |||||
* size from the full size of the Queue so the caller knows how many bytes | |||||
* are ready to be dequeued. | |||||
* | |||||
* Results: | |||||
* On success, available data size in bytes (up to MAX_INT64). | |||||
* On failure, appropriate error code. | |||||
* | |||||
* Side effects: | |||||
* None. | |||||
* | |||||
*------------------------------------------------------------------------------ | |||||
*/ | |||||
static inline int64_t | |||||
vmci_queue_header_buf_ready(const struct vmci_queue_header *consume_q_header, | |||||
const struct vmci_queue_header *produce_q_header, | |||||
const uint64_t consume_q_size) | |||||
{ | |||||
int64_t free_space; | |||||
free_space = vmci_queue_header_free_space(consume_q_header, | |||||
produce_q_header, consume_q_size); | |||||
if (free_space < VMCI_SUCCESS) | |||||
return (free_space); | |||||
else | |||||
return (consume_q_size - free_space - 1); | |||||
} | |||||
#endif /* !_VMCI_DEFS_H_ */ |