2018-03-25 00:57:00 +00:00
|
|
|
/*-
|
2018-04-08 01:32:56 +00:00
|
|
|
* Copyright (c) 2018 VMware, Inc.
|
2018-03-25 00:57:00 +00:00
|
|
|
*
|
2018-03-27 06:33:00 +00:00
|
|
|
* SPDX-License-Identifier: (BSD-2-Clause OR GPL-2.0)
|
2018-03-25 00:57:00 +00:00
|
|
|
*
|
|
|
|
* $FreeBSD$
|
|
|
|
*/
|
|
|
|
|
|
|
|
#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_ */
|