建立工程,成功创建两个虚拟串口
This commit is contained in:
2
source/OpenAMP/open-amp/lib/virtio/CMakeLists.txt
Normal file
2
source/OpenAMP/open-amp/lib/virtio/CMakeLists.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
collect (PROJECT_LIB_SOURCES virtio.c)
|
||||
collect (PROJECT_LIB_SOURCES virtqueue.c)
|
||||
121
source/OpenAMP/open-amp/lib/virtio/virtio.c
Normal file
121
source/OpenAMP/open-amp/lib/virtio/virtio.c
Normal file
@@ -0,0 +1,121 @@
|
||||
/*-
|
||||
* Copyright (c) 2011, Bryan Venteicher <bryanv@FreeBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
#include <openamp/virtio.h>
|
||||
|
||||
static const char *virtio_feature_name(unsigned long feature,
|
||||
const struct virtio_feature_desc *);
|
||||
|
||||
//TODO : This structure may change depending on the types of devices we support.
|
||||
static const struct virtio_ident {
|
||||
unsigned short devid;
|
||||
const char *name;
|
||||
} virtio_ident_table[] = {
|
||||
{
|
||||
VIRTIO_ID_NETWORK, "Network"}, {
|
||||
VIRTIO_ID_BLOCK, "Block"}, {
|
||||
VIRTIO_ID_CONSOLE, "Console"}, {
|
||||
VIRTIO_ID_ENTROPY, "Entropy"}, {
|
||||
VIRTIO_ID_BALLOON, "Balloon"}, {
|
||||
VIRTIO_ID_IOMEMORY, "IOMemory"}, {
|
||||
VIRTIO_ID_SCSI, "SCSI"}, {
|
||||
VIRTIO_ID_9P, "9P Transport"}, {
|
||||
0, NULL}
|
||||
};
|
||||
|
||||
/* Device independent features. */
|
||||
static const struct virtio_feature_desc virtio_common_feature_desc[] = {
|
||||
{VIRTIO_F_NOTIFY_ON_EMPTY, "NotifyOnEmpty"},
|
||||
{VIRTIO_RING_F_INDIRECT_DESC, "RingIndirect"},
|
||||
{VIRTIO_RING_F_EVENT_IDX, "EventIdx"},
|
||||
{VIRTIO_F_BAD_FEATURE, "BadFeature"},
|
||||
|
||||
{0, NULL}
|
||||
};
|
||||
|
||||
const char *virtio_dev_name(unsigned short devid)
|
||||
{
|
||||
const struct virtio_ident *ident;
|
||||
|
||||
for (ident = virtio_ident_table; ident->name != NULL; ident++) {
|
||||
if (ident->devid == devid)
|
||||
return (ident->name);
|
||||
}
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
static const char *virtio_feature_name(unsigned long val,
|
||||
const struct virtio_feature_desc *desc)
|
||||
{
|
||||
int i, j;
|
||||
const struct virtio_feature_desc *descs[2] = { desc,
|
||||
virtio_common_feature_desc
|
||||
};
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
if (!descs[i])
|
||||
continue;
|
||||
|
||||
for (j = 0; descs[i][j].vfd_val != 0; j++) {
|
||||
if (val == descs[i][j].vfd_val)
|
||||
return (descs[i][j].vfd_str);
|
||||
}
|
||||
}
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
void virtio_describe(struct virtio_device *dev, const char *msg,
|
||||
uint32_t features, struct virtio_feature_desc *desc)
|
||||
{
|
||||
(void)dev;
|
||||
(void)msg;
|
||||
(void)features;
|
||||
|
||||
// TODO: Not used currently - keeping it for future use
|
||||
virtio_feature_name(0, desc);
|
||||
}
|
||||
|
||||
int virtio_create_virtqueues(struct virtio_device *vdev, unsigned int flags,
|
||||
unsigned int nvqs, const char *names[],
|
||||
vq_callback *callbacks[])
|
||||
{
|
||||
struct virtio_vring_info *vring_info;
|
||||
struct vring_alloc_info *vring_alloc;
|
||||
unsigned int num_vrings, i;
|
||||
int ret;
|
||||
(void)flags;
|
||||
|
||||
num_vrings = vdev->vrings_num;
|
||||
if (nvqs > num_vrings)
|
||||
return -ERROR_VQUEUE_INVLD_PARAM;
|
||||
/* Initialize virtqueue for each vring */
|
||||
for (i = 0; i < nvqs; i++) {
|
||||
vring_info = &vdev->vrings_info[i];
|
||||
|
||||
vring_alloc = &vring_info->info;
|
||||
#ifndef VIRTIO_SLAVE_ONLY
|
||||
if (vdev->role == VIRTIO_DEV_MASTER) {
|
||||
size_t offset;
|
||||
struct metal_io_region *io = vring_info->io;
|
||||
|
||||
offset = metal_io_virt_to_offset(io,
|
||||
vring_alloc->vaddr);
|
||||
metal_io_block_set(io, offset, 0,
|
||||
vring_size(vring_alloc->num_descs,
|
||||
vring_alloc->align));
|
||||
}
|
||||
#endif
|
||||
ret = virtqueue_create(vdev, i, names[i], vring_alloc,
|
||||
callbacks[i], vdev->func->notify,
|
||||
vring_info->vq);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
618
source/OpenAMP/open-amp/lib/virtio/virtqueue.c
Normal file
618
source/OpenAMP/open-amp/lib/virtio/virtqueue.c
Normal file
@@ -0,0 +1,618 @@
|
||||
/*-
|
||||
* Copyright (c) 2011, Bryan Venteicher <bryanv@FreeBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <openamp/virtqueue.h>
|
||||
#include <metal/atomic.h>
|
||||
#include <metal/log.h>
|
||||
#include <metal/alloc.h>
|
||||
|
||||
/* Prototype for internal functions. */
|
||||
static void vq_ring_init(struct virtqueue *, void *, int);
|
||||
static void vq_ring_update_avail(struct virtqueue *, uint16_t);
|
||||
static uint16_t vq_ring_add_buffer(struct virtqueue *, struct vring_desc *,
|
||||
uint16_t, struct virtqueue_buf *, int, int);
|
||||
static int vq_ring_enable_interrupt(struct virtqueue *, uint16_t);
|
||||
static void vq_ring_free_chain(struct virtqueue *, uint16_t);
|
||||
static int vq_ring_must_notify_host(struct virtqueue *vq);
|
||||
static void vq_ring_notify_host(struct virtqueue *vq);
|
||||
static int virtqueue_nused(struct virtqueue *vq);
|
||||
|
||||
/* Default implementation of P2V based on libmetal */
|
||||
static inline void *virtqueue_phys_to_virt(struct virtqueue *vq,
|
||||
metal_phys_addr_t phys)
|
||||
{
|
||||
struct metal_io_region *io = vq->shm_io;
|
||||
|
||||
return metal_io_phys_to_virt(io, phys);
|
||||
}
|
||||
|
||||
/* Default implementation of V2P based on libmetal */
|
||||
static inline metal_phys_addr_t virtqueue_virt_to_phys(struct virtqueue *vq,
|
||||
void *buf)
|
||||
{
|
||||
struct metal_io_region *io = vq->shm_io;
|
||||
|
||||
return metal_io_virt_to_phys(io, buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* virtqueue_create - Creates new VirtIO queue
|
||||
*
|
||||
* @param device - Pointer to VirtIO device
|
||||
* @param id - VirtIO queue ID , must be unique
|
||||
* @param name - Name of VirtIO queue
|
||||
* @param ring - Pointer to vring_alloc_info control block
|
||||
* @param callback - Pointer to callback function, invoked
|
||||
* when message is available on VirtIO queue
|
||||
* @param notify - Pointer to notify function, used to notify
|
||||
* other side that there is job available for it
|
||||
* @param vq - Created VirtIO queue.
|
||||
*
|
||||
* @return - Function status
|
||||
*/
|
||||
int virtqueue_create(struct virtio_device *virt_dev, unsigned short id,
|
||||
const char *name, struct vring_alloc_info *ring,
|
||||
void (*callback)(struct virtqueue *vq),
|
||||
void (*notify)(struct virtqueue *vq),
|
||||
struct virtqueue *vq)
|
||||
{
|
||||
int status = VQUEUE_SUCCESS;
|
||||
|
||||
VQ_PARAM_CHK(ring == NULL, status, ERROR_VQUEUE_INVLD_PARAM);
|
||||
VQ_PARAM_CHK(ring->num_descs == 0, status, ERROR_VQUEUE_INVLD_PARAM);
|
||||
VQ_PARAM_CHK(ring->num_descs & (ring->num_descs - 1), status,
|
||||
ERROR_VRING_ALIGN);
|
||||
VQ_PARAM_CHK(vq == NULL, status, ERROR_NO_MEM);
|
||||
|
||||
if (status == VQUEUE_SUCCESS) {
|
||||
vq->vq_dev = virt_dev;
|
||||
vq->vq_name = name;
|
||||
vq->vq_queue_index = id;
|
||||
vq->vq_nentries = ring->num_descs;
|
||||
vq->vq_free_cnt = vq->vq_nentries;
|
||||
vq->callback = callback;
|
||||
vq->notify = notify;
|
||||
|
||||
/* Initialize vring control block in virtqueue. */
|
||||
vq_ring_init(vq, (void *)ring->vaddr, ring->align);
|
||||
|
||||
/* Disable callbacks - will be enabled by the application
|
||||
* once initialization is completed.
|
||||
*/
|
||||
virtqueue_disable_cb(vq);
|
||||
}
|
||||
|
||||
return (status);
|
||||
}
|
||||
|
||||
/**
|
||||
* virtqueue_add_buffer() - Enqueues new buffer in vring for consumption
|
||||
* by other side. Readable buffers are always
|
||||
* inserted before writable buffers
|
||||
*
|
||||
* @param vq - Pointer to VirtIO queue control block.
|
||||
* @param buf_list - Pointer to a list of virtqueue buffers.
|
||||
* @param readable - Number of readable buffers
|
||||
* @param writable - Number of writable buffers
|
||||
* @param cookie - Pointer to hold call back data
|
||||
*
|
||||
* @return - Function status
|
||||
*/
|
||||
int virtqueue_add_buffer(struct virtqueue *vq, struct virtqueue_buf *buf_list,
|
||||
int readable, int writable, void *cookie)
|
||||
{
|
||||
struct vq_desc_extra *dxp = NULL;
|
||||
int status = VQUEUE_SUCCESS;
|
||||
uint16_t head_idx;
|
||||
uint16_t idx;
|
||||
int needed;
|
||||
|
||||
needed = readable + writable;
|
||||
|
||||
VQ_PARAM_CHK(vq == NULL, status, ERROR_VQUEUE_INVLD_PARAM);
|
||||
VQ_PARAM_CHK(needed < 1, status, ERROR_VQUEUE_INVLD_PARAM);
|
||||
VQ_PARAM_CHK(vq->vq_free_cnt == 0, status, ERROR_VRING_FULL);
|
||||
|
||||
VQUEUE_BUSY(vq);
|
||||
|
||||
if (status == VQUEUE_SUCCESS) {
|
||||
VQASSERT(vq, cookie != NULL, "enqueuing with no cookie");
|
||||
|
||||
head_idx = vq->vq_desc_head_idx;
|
||||
VQ_RING_ASSERT_VALID_IDX(vq, head_idx);
|
||||
dxp = &vq->vq_descx[head_idx];
|
||||
|
||||
VQASSERT(vq, dxp->cookie == NULL,
|
||||
"cookie already exists for index");
|
||||
|
||||
dxp->cookie = cookie;
|
||||
dxp->ndescs = needed;
|
||||
|
||||
/* Enqueue buffer onto the ring. */
|
||||
idx = vq_ring_add_buffer(vq, vq->vq_ring.desc, head_idx,
|
||||
buf_list, readable, writable);
|
||||
|
||||
vq->vq_desc_head_idx = idx;
|
||||
vq->vq_free_cnt -= needed;
|
||||
|
||||
if (vq->vq_free_cnt == 0) {
|
||||
VQ_RING_ASSERT_CHAIN_TERM(vq);
|
||||
} else {
|
||||
VQ_RING_ASSERT_VALID_IDX(vq, idx);
|
||||
}
|
||||
|
||||
/*
|
||||
* Update vring_avail control block fields so that other
|
||||
* side can get buffer using it.
|
||||
*/
|
||||
vq_ring_update_avail(vq, head_idx);
|
||||
}
|
||||
|
||||
VQUEUE_IDLE(vq);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* virtqueue_get_buffer - Returns used buffers from VirtIO queue
|
||||
*
|
||||
* @param vq - Pointer to VirtIO queue control block
|
||||
* @param len - Length of conumed buffer
|
||||
* @param idx - index of the buffer
|
||||
*
|
||||
* @return - Pointer to used buffer
|
||||
*/
|
||||
void *virtqueue_get_buffer(struct virtqueue *vq, uint32_t *len, uint16_t *idx)
|
||||
{
|
||||
struct vring_used_elem *uep;
|
||||
void *cookie;
|
||||
uint16_t used_idx, desc_idx;
|
||||
|
||||
if (!vq || vq->vq_used_cons_idx == vq->vq_ring.used->idx)
|
||||
return (NULL);
|
||||
|
||||
VQUEUE_BUSY(vq);
|
||||
|
||||
used_idx = vq->vq_used_cons_idx++ & (vq->vq_nentries - 1);
|
||||
uep = &vq->vq_ring.used->ring[used_idx];
|
||||
|
||||
atomic_thread_fence(memory_order_seq_cst);
|
||||
|
||||
desc_idx = (uint16_t)uep->id;
|
||||
if (len)
|
||||
*len = uep->len;
|
||||
|
||||
vq_ring_free_chain(vq, desc_idx);
|
||||
|
||||
cookie = vq->vq_descx[desc_idx].cookie;
|
||||
vq->vq_descx[desc_idx].cookie = NULL;
|
||||
|
||||
if (idx)
|
||||
*idx = used_idx;
|
||||
VQUEUE_IDLE(vq);
|
||||
|
||||
return cookie;
|
||||
}
|
||||
|
||||
uint32_t virtqueue_get_buffer_length(struct virtqueue *vq, uint16_t idx)
|
||||
{
|
||||
return vq->vq_ring.desc[idx].len;
|
||||
}
|
||||
|
||||
/**
|
||||
* virtqueue_free - Frees VirtIO queue resources
|
||||
*
|
||||
* @param vq - Pointer to VirtIO queue control block
|
||||
*
|
||||
*/
|
||||
void virtqueue_free(struct virtqueue *vq)
|
||||
{
|
||||
if (vq) {
|
||||
if (vq->vq_free_cnt != vq->vq_nentries) {
|
||||
metal_log(METAL_LOG_WARNING,
|
||||
"%s: freeing non-empty virtqueue\r\n",
|
||||
vq->vq_name);
|
||||
}
|
||||
|
||||
metal_free_memory(vq);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* virtqueue_get_available_buffer - Returns buffer available for use in the
|
||||
* VirtIO queue
|
||||
*
|
||||
* @param vq - Pointer to VirtIO queue control block
|
||||
* @param avail_idx - Pointer to index used in vring desc table
|
||||
* @param len - Length of buffer
|
||||
*
|
||||
* @return - Pointer to available buffer
|
||||
*/
|
||||
void *virtqueue_get_available_buffer(struct virtqueue *vq, uint16_t *avail_idx,
|
||||
uint32_t *len)
|
||||
{
|
||||
uint16_t head_idx = 0;
|
||||
void *buffer;
|
||||
|
||||
atomic_thread_fence(memory_order_seq_cst);
|
||||
if (vq->vq_available_idx == vq->vq_ring.avail->idx) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
VQUEUE_BUSY(vq);
|
||||
|
||||
head_idx = vq->vq_available_idx++ & (vq->vq_nentries - 1);
|
||||
*avail_idx = vq->vq_ring.avail->ring[head_idx];
|
||||
|
||||
buffer = virtqueue_phys_to_virt(vq, vq->vq_ring.desc[*avail_idx].addr);
|
||||
*len = vq->vq_ring.desc[*avail_idx].len;
|
||||
|
||||
VQUEUE_IDLE(vq);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* virtqueue_add_consumed_buffer - Returns consumed buffer back to VirtIO queue
|
||||
*
|
||||
* @param vq - Pointer to VirtIO queue control block
|
||||
* @param head_idx - Index of vring desc containing used buffer
|
||||
* @param len - Length of buffer
|
||||
*
|
||||
* @return - Function status
|
||||
*/
|
||||
int virtqueue_add_consumed_buffer(struct virtqueue *vq, uint16_t head_idx,
|
||||
uint32_t len)
|
||||
{
|
||||
struct vring_used_elem *used_desc = NULL;
|
||||
uint16_t used_idx;
|
||||
|
||||
if (head_idx > vq->vq_nentries) {
|
||||
return ERROR_VRING_NO_BUFF;
|
||||
}
|
||||
|
||||
VQUEUE_BUSY(vq);
|
||||
|
||||
used_idx = vq->vq_ring.used->idx & (vq->vq_nentries - 1);
|
||||
used_desc = &vq->vq_ring.used->ring[used_idx];
|
||||
used_desc->id = head_idx;
|
||||
used_desc->len = len;
|
||||
|
||||
atomic_thread_fence(memory_order_seq_cst);
|
||||
|
||||
vq->vq_ring.used->idx++;
|
||||
|
||||
VQUEUE_IDLE(vq);
|
||||
|
||||
return VQUEUE_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* virtqueue_enable_cb - Enables callback generation
|
||||
*
|
||||
* @param vq - Pointer to VirtIO queue control block
|
||||
*
|
||||
* @return - Function status
|
||||
*/
|
||||
int virtqueue_enable_cb(struct virtqueue *vq)
|
||||
{
|
||||
return vq_ring_enable_interrupt(vq, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* virtqueue_enable_cb - Disables callback generation
|
||||
*
|
||||
* @param vq - Pointer to VirtIO queue control block
|
||||
*
|
||||
*/
|
||||
void virtqueue_disable_cb(struct virtqueue *vq)
|
||||
{
|
||||
VQUEUE_BUSY(vq);
|
||||
|
||||
if (vq->vq_flags & VIRTQUEUE_FLAG_EVENT_IDX) {
|
||||
vring_used_event(&vq->vq_ring) =
|
||||
vq->vq_used_cons_idx - vq->vq_nentries - 1;
|
||||
} else {
|
||||
vq->vq_ring.avail->flags |= VRING_AVAIL_F_NO_INTERRUPT;
|
||||
}
|
||||
|
||||
VQUEUE_IDLE(vq);
|
||||
}
|
||||
|
||||
/**
|
||||
* virtqueue_kick - Notifies other side that there is buffer available for it.
|
||||
*
|
||||
* @param vq - Pointer to VirtIO queue control block
|
||||
*/
|
||||
void virtqueue_kick(struct virtqueue *vq)
|
||||
{
|
||||
VQUEUE_BUSY(vq);
|
||||
|
||||
/* Ensure updated avail->idx is visible to host. */
|
||||
atomic_thread_fence(memory_order_seq_cst);
|
||||
|
||||
if (vq_ring_must_notify_host(vq))
|
||||
vq_ring_notify_host(vq);
|
||||
|
||||
vq->vq_queued_cnt = 0;
|
||||
|
||||
VQUEUE_IDLE(vq);
|
||||
}
|
||||
|
||||
/**
|
||||
* virtqueue_dump Dumps important virtqueue fields , use for debugging purposes
|
||||
*
|
||||
* @param vq - Pointer to VirtIO queue control block
|
||||
*/
|
||||
void virtqueue_dump(struct virtqueue *vq)
|
||||
{
|
||||
if (!vq)
|
||||
return;
|
||||
|
||||
metal_log(METAL_LOG_DEBUG,
|
||||
"VQ: %s - size=%d; free=%d; used=%d; queued=%d; "
|
||||
"desc_head_idx=%d; avail.idx=%d; used_cons_idx=%d; "
|
||||
"used.idx=%d; avail.flags=0x%x; used.flags=0x%x\r\n",
|
||||
vq->vq_name, vq->vq_nentries, vq->vq_free_cnt,
|
||||
virtqueue_nused(vq), vq->vq_queued_cnt, vq->vq_desc_head_idx,
|
||||
vq->vq_ring.avail->idx, vq->vq_used_cons_idx,
|
||||
vq->vq_ring.used->idx, vq->vq_ring.avail->flags,
|
||||
vq->vq_ring.used->flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* virtqueue_get_desc_size - Returns vring descriptor size
|
||||
*
|
||||
* @param vq - Pointer to VirtIO queue control block
|
||||
*
|
||||
* @return - Descriptor length
|
||||
*/
|
||||
uint32_t virtqueue_get_desc_size(struct virtqueue *vq)
|
||||
{
|
||||
uint16_t head_idx = 0;
|
||||
uint16_t avail_idx = 0;
|
||||
uint32_t len = 0;
|
||||
|
||||
if (vq->vq_available_idx == vq->vq_ring.avail->idx) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
VQUEUE_BUSY(vq);
|
||||
|
||||
head_idx = vq->vq_available_idx & (vq->vq_nentries - 1);
|
||||
avail_idx = vq->vq_ring.avail->ring[head_idx];
|
||||
len = vq->vq_ring.desc[avail_idx].len;
|
||||
|
||||
VQUEUE_IDLE(vq);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
* Helper Functions *
|
||||
**************************************************************************/
|
||||
|
||||
/**
|
||||
*
|
||||
* vq_ring_add_buffer
|
||||
*
|
||||
*/
|
||||
static uint16_t vq_ring_add_buffer(struct virtqueue *vq,
|
||||
struct vring_desc *desc, uint16_t head_idx,
|
||||
struct virtqueue_buf *buf_list, int readable,
|
||||
int writable)
|
||||
{
|
||||
struct vring_desc *dp;
|
||||
int i, needed;
|
||||
uint16_t idx;
|
||||
|
||||
(void)vq;
|
||||
|
||||
needed = readable + writable;
|
||||
|
||||
for (i = 0, idx = head_idx; i < needed; i++, idx = dp->next) {
|
||||
VQASSERT(vq, idx != VQ_RING_DESC_CHAIN_END,
|
||||
"premature end of free desc chain");
|
||||
|
||||
dp = &desc[idx];
|
||||
dp->addr = virtqueue_virt_to_phys(vq, buf_list[i].buf);
|
||||
dp->len = buf_list[i].len;
|
||||
dp->flags = 0;
|
||||
|
||||
if (i < needed - 1)
|
||||
dp->flags |= VRING_DESC_F_NEXT;
|
||||
|
||||
/*
|
||||
* Readable buffers are inserted into vring before the
|
||||
* writable buffers.
|
||||
*/
|
||||
if (i >= readable)
|
||||
dp->flags |= VRING_DESC_F_WRITE;
|
||||
}
|
||||
|
||||
return (idx);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* vq_ring_free_chain
|
||||
*
|
||||
*/
|
||||
static void vq_ring_free_chain(struct virtqueue *vq, uint16_t desc_idx)
|
||||
{
|
||||
struct vring_desc *dp;
|
||||
struct vq_desc_extra *dxp;
|
||||
|
||||
VQ_RING_ASSERT_VALID_IDX(vq, desc_idx);
|
||||
dp = &vq->vq_ring.desc[desc_idx];
|
||||
dxp = &vq->vq_descx[desc_idx];
|
||||
|
||||
if (vq->vq_free_cnt == 0) {
|
||||
VQ_RING_ASSERT_CHAIN_TERM(vq);
|
||||
}
|
||||
|
||||
vq->vq_free_cnt += dxp->ndescs;
|
||||
dxp->ndescs--;
|
||||
|
||||
if ((dp->flags & VRING_DESC_F_INDIRECT) == 0) {
|
||||
while (dp->flags & VRING_DESC_F_NEXT) {
|
||||
VQ_RING_ASSERT_VALID_IDX(vq, dp->next);
|
||||
dp = &vq->vq_ring.desc[dp->next];
|
||||
dxp->ndescs--;
|
||||
}
|
||||
}
|
||||
|
||||
VQASSERT(vq, (dxp->ndescs == 0),
|
||||
"failed to free entire desc chain, remaining");
|
||||
|
||||
/*
|
||||
* We must append the existing free chain, if any, to the end of
|
||||
* newly freed chain. If the virtqueue was completely used, then
|
||||
* head would be VQ_RING_DESC_CHAIN_END (ASSERTed above).
|
||||
*/
|
||||
dp->next = vq->vq_desc_head_idx;
|
||||
vq->vq_desc_head_idx = desc_idx;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* vq_ring_init
|
||||
*
|
||||
*/
|
||||
static void vq_ring_init(struct virtqueue *vq, void *ring_mem, int alignment)
|
||||
{
|
||||
struct vring *vr;
|
||||
int i, size;
|
||||
|
||||
size = vq->vq_nentries;
|
||||
vr = &vq->vq_ring;
|
||||
|
||||
vring_init(vr, size, (unsigned char *)ring_mem, alignment);
|
||||
|
||||
for (i = 0; i < size - 1; i++)
|
||||
vr->desc[i].next = i + 1;
|
||||
vr->desc[i].next = VQ_RING_DESC_CHAIN_END;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* vq_ring_update_avail
|
||||
*
|
||||
*/
|
||||
static void vq_ring_update_avail(struct virtqueue *vq, uint16_t desc_idx)
|
||||
{
|
||||
uint16_t avail_idx;
|
||||
|
||||
/*
|
||||
* Place the head of the descriptor chain into the next slot and make
|
||||
* it usable to the host. The chain is made available now rather than
|
||||
* deferring to virtqueue_notify() in the hopes that if the host is
|
||||
* currently running on another CPU, we can keep it processing the new
|
||||
* descriptor.
|
||||
*/
|
||||
avail_idx = vq->vq_ring.avail->idx & (vq->vq_nentries - 1);
|
||||
vq->vq_ring.avail->ring[avail_idx] = desc_idx;
|
||||
|
||||
atomic_thread_fence(memory_order_seq_cst);
|
||||
|
||||
vq->vq_ring.avail->idx++;
|
||||
|
||||
/* Keep pending count until virtqueue_notify(). */
|
||||
vq->vq_queued_cnt++;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* vq_ring_enable_interrupt
|
||||
*
|
||||
*/
|
||||
static int vq_ring_enable_interrupt(struct virtqueue *vq, uint16_t ndesc)
|
||||
{
|
||||
/*
|
||||
* Enable interrupts, making sure we get the latest index of
|
||||
* what's already been consumed.
|
||||
*/
|
||||
if (vq->vq_flags & VIRTQUEUE_FLAG_EVENT_IDX) {
|
||||
vring_used_event(&vq->vq_ring) = vq->vq_used_cons_idx + ndesc;
|
||||
} else {
|
||||
vq->vq_ring.avail->flags &= ~VRING_AVAIL_F_NO_INTERRUPT;
|
||||
}
|
||||
|
||||
atomic_thread_fence(memory_order_seq_cst);
|
||||
|
||||
/*
|
||||
* Enough items may have already been consumed to meet our threshold
|
||||
* since we last checked. Let our caller know so it processes the new
|
||||
* entries.
|
||||
*/
|
||||
if (virtqueue_nused(vq) > ndesc) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* virtqueue_interrupt
|
||||
*
|
||||
*/
|
||||
void virtqueue_notification(struct virtqueue *vq)
|
||||
{
|
||||
atomic_thread_fence(memory_order_seq_cst);
|
||||
if (vq->callback)
|
||||
vq->callback(vq);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* vq_ring_must_notify_host
|
||||
*
|
||||
*/
|
||||
static int vq_ring_must_notify_host(struct virtqueue *vq)
|
||||
{
|
||||
uint16_t new_idx, prev_idx, event_idx;
|
||||
|
||||
if (vq->vq_flags & VIRTQUEUE_FLAG_EVENT_IDX) {
|
||||
new_idx = vq->vq_ring.avail->idx;
|
||||
prev_idx = new_idx - vq->vq_queued_cnt;
|
||||
event_idx = vring_avail_event(&vq->vq_ring);
|
||||
|
||||
return (vring_need_event(event_idx, new_idx, prev_idx) != 0);
|
||||
}
|
||||
|
||||
return ((vq->vq_ring.used->flags & VRING_USED_F_NO_NOTIFY) == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* vq_ring_notify_host
|
||||
*
|
||||
*/
|
||||
static void vq_ring_notify_host(struct virtqueue *vq)
|
||||
{
|
||||
if (vq->notify)
|
||||
vq->notify(vq);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* virtqueue_nused
|
||||
*
|
||||
*/
|
||||
static int virtqueue_nused(struct virtqueue *vq)
|
||||
{
|
||||
uint16_t used_idx, nused;
|
||||
|
||||
used_idx = vq->vq_ring.used->idx;
|
||||
|
||||
nused = (uint16_t)(used_idx - vq->vq_used_cons_idx);
|
||||
VQASSERT(vq, nused <= vq->vq_nentries, "used more than available");
|
||||
|
||||
return nused;
|
||||
}
|
||||
Reference in New Issue
Block a user