360 lines
9.7 KiB
C
360 lines
9.7 KiB
C
/*
|
|
* Copyright (c) 2016 ETH Zurich.
|
|
* All rights reserved.
|
|
*
|
|
* This file is distributed under the terms in the attached LICENSE file.
|
|
* If you do not find this file, copies can be found by writing to:
|
|
* ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
|
|
*/
|
|
|
|
#include <aos/aos.h>
|
|
#include <devif/queue_interface.h>
|
|
#include <devif/queue_interface_backend.h>
|
|
|
|
#include "region_pool.h"
|
|
#include "dqi_debug.h"
|
|
|
|
//#define BENCH_DEVQ
|
|
|
|
#ifdef BENCH_DEVQ
|
|
#define NUM_ROUNDS 100000
|
|
static cycles_t enq[NUM_ROUNDS];
|
|
static cycles_t deq[NUM_ROUNDS];
|
|
static cycles_t reg[NUM_ROUNDS];
|
|
static cycles_t dereg[NUM_ROUNDS];
|
|
static bench_ctl_t ctl_enq;
|
|
static bench_ctl_t ctl_deq;
|
|
static bench_ctl_t ctl_reg;
|
|
static bench_ctl_t ctl_dereg;
|
|
static uint64_t start;
|
|
static uint64_t end;
|
|
static bool init_done;
|
|
|
|
static void add_bench_entry(bench_ctl_t* ctl, cycles_t diff, char* prefix) {
|
|
|
|
if (init_done) {
|
|
if (ctl->result_count == NUM_ROUNDS) {
|
|
bench_ctl_dump_analysis(ctl, 1, prefix, 1);
|
|
ctl->result_count = 0;
|
|
memset(ctl->data, 0, sizeof(ctl->data));
|
|
}
|
|
bench_ctl_add_run(ctl, &diff);
|
|
} else {
|
|
memset(enq, 0, sizeof(enq));
|
|
memset(deq, 0, sizeof(deq));
|
|
memset(reg, 0, sizeof(reg));
|
|
memset(dereg, 0, sizeof(dereg));
|
|
|
|
memset(&ctl_enq, 0, sizeof(ctl_enq));
|
|
memset(&ctl_deq, 0, sizeof(ctl_deq));
|
|
memset(&ctl_reg, 0, sizeof(ctl_reg));
|
|
memset(&ctl_dereg, 0, sizeof(ctl_dereg));
|
|
|
|
ctl_enq.mode = BENCH_MODE_FIXEDRUNS;
|
|
ctl_enq.result_dimensions = 1;
|
|
ctl_enq.min_runs = NUM_ROUNDS;
|
|
ctl_enq.data = enq;
|
|
|
|
ctl_deq.mode = BENCH_MODE_FIXEDRUNS;
|
|
ctl_deq.result_dimensions = 1;
|
|
ctl_deq.min_runs = NUM_ROUNDS;
|
|
ctl_deq.data = deq;
|
|
|
|
ctl_reg.mode = BENCH_MODE_FIXEDRUNS;
|
|
ctl_reg.result_dimensions = 1;
|
|
ctl_reg.min_runs = NUM_ROUNDS;
|
|
ctl_reg.data = reg;
|
|
|
|
ctl_dereg.mode = BENCH_MODE_FIXEDRUNS;
|
|
ctl_dereg.result_dimensions = 1;
|
|
ctl_dereg.min_runs = NUM_ROUNDS;
|
|
ctl_dereg.data = dereg;
|
|
|
|
init_done = true;
|
|
|
|
add_bench_entry(ctl, diff, prefix);
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
/*
|
|
* ===========================================================================
|
|
* Datapath functions
|
|
* ===========================================================================
|
|
*/
|
|
|
|
/*
|
|
*
|
|
* @brief enqueue a buffer into the device queue
|
|
*
|
|
* @param q The device queue to call the operation on
|
|
* @param region_id Id of the memory region the buffer belongs to
|
|
* @param offset Offset into the region i.e. where the buffer starts
|
|
* that is enqueued
|
|
* @param lenght Lenght of the enqueued buffer
|
|
* @param valid_data Offset into the region where the valid data of this buffer
|
|
* starts
|
|
* @param valid_length Length of the valid data of this buffer
|
|
* @param misc_flags Any other argument that makes sense to the device queue
|
|
*
|
|
* @returns error on failure or SYS_ERR_OK on success
|
|
*
|
|
*/
|
|
errval_t devq_enqueue(struct devq *q,
|
|
regionid_t region_id,
|
|
genoffset_t offset,
|
|
genoffset_t length,
|
|
genoffset_t valid_data,
|
|
genoffset_t valid_length,
|
|
uint64_t misc_flags)
|
|
{
|
|
assert(q != NULL);
|
|
errval_t err;
|
|
|
|
// check if the buffer to enqueue is valid
|
|
if (!region_pool_buffer_check_bounds(q->pool, region_id, offset,
|
|
length, valid_data, valid_length)) {
|
|
return DEVQ_ERR_INVALID_BUFFER_ARGS;
|
|
}
|
|
|
|
#ifdef BENCH_DEVQ
|
|
start = bench_tsc();
|
|
#endif
|
|
err = q->f.enq(q, region_id, offset, length, valid_data,
|
|
valid_length, misc_flags);
|
|
#ifdef BENCH_DEVQ
|
|
end = bench_tsc();
|
|
add_bench_entry(&ctl_enq, end - start, "backend_enqueue");
|
|
#endif
|
|
DQI_DEBUG("Enqueue q=%p rid=%d, offset=%lu, lenght=%lu, err=%s \n",
|
|
q, region_id, offset, length, err_getstring(err));
|
|
|
|
return err;
|
|
}
|
|
|
|
/**
|
|
* @brief dequeue a buffer from the device queue
|
|
*
|
|
* @param q The device queue to call the operation on
|
|
* @param region_id Return pointer to the id of the memory
|
|
* region the buffer belongs to
|
|
* @param region_offset Return pointer to the offset into the region where
|
|
* this buffer starts.
|
|
* @param lenght Return pointer to the lenght of the dequeue buffer
|
|
* @param valid_data Return pointer to an offset into the region where the
|
|
* valid data of this buffer starts
|
|
* @param valid_length Return pointer to the length of the valid data of
|
|
* this buffer
|
|
* @param misc_flags Return value from other endpoint
|
|
*
|
|
* @returns error on failure or SYS_ERR_OK on success
|
|
*
|
|
*/
|
|
errval_t devq_dequeue(struct devq *q,
|
|
regionid_t* region_id,
|
|
genoffset_t* offset,
|
|
genoffset_t* length,
|
|
genoffset_t* valid_data,
|
|
genoffset_t* valid_length,
|
|
uint64_t* misc_flags)
|
|
{
|
|
errval_t err;
|
|
|
|
assert(q != NULL);
|
|
assert(offset != NULL);
|
|
assert(length != NULL);
|
|
#ifdef BENCH_DEVQ
|
|
start = bench_tsc();
|
|
#endif
|
|
err = q->f.deq(q, region_id, offset, length, valid_data,
|
|
valid_length, misc_flags);
|
|
if (err_is_fail(err)) {
|
|
return err;
|
|
}
|
|
#ifdef BENCH_DEVQ
|
|
end = bench_tsc();
|
|
add_bench_entry(&ctl_deq, end - start, "backend_dequeue");
|
|
#endif
|
|
// check if the dequeue buffer is valid
|
|
if (!region_pool_buffer_check_bounds(q->pool, *region_id, *offset,
|
|
*length, *valid_data, *valid_length)) {
|
|
return DEVQ_ERR_INVALID_BUFFER_ARGS;
|
|
}
|
|
|
|
DQI_DEBUG("Dequeue q=%p rid=%u, offset=%lu \n", q, *region_id, *offset);
|
|
|
|
return SYS_ERR_OK;
|
|
}
|
|
|
|
/*
|
|
* ===========================================================================
|
|
* Control Path
|
|
* ===========================================================================
|
|
*/
|
|
|
|
/**
|
|
* @brief Add a memory region that can be used as buffers to
|
|
* the device queue
|
|
*
|
|
* @param q The device queue to call the operation on
|
|
* @param cap A Capability for some memory
|
|
* @param region_id Return pointer to a region id that is assigned
|
|
* to the memory
|
|
*
|
|
* @returns error on failure or SYS_ERR_OK on success
|
|
*
|
|
*/
|
|
errval_t devq_register(struct devq *q,
|
|
struct capref cap,
|
|
regionid_t* region_id)
|
|
{
|
|
errval_t err;
|
|
|
|
err = region_pool_add_region(q->pool, cap, region_id);
|
|
if (err_is_fail(err)) {
|
|
return err;
|
|
}
|
|
|
|
DQI_DEBUG("register q=%p, cap=%p, regionid=%d \n", (void*) q,
|
|
(void*) &cap, *region_id);
|
|
|
|
#ifdef BENCH_DEVQ
|
|
start = bench_tsc();
|
|
#endif
|
|
err = q->f.reg(q, cap, *region_id);
|
|
|
|
#ifdef BENCH_DEVQ
|
|
end = bench_tsc();
|
|
add_bench_entry(&ctl_reg, end - start, "backend_register");
|
|
#endif
|
|
return err;
|
|
}
|
|
|
|
/**
|
|
* @brief Remove a memory region
|
|
*
|
|
* @param q The device queue to call the operation on
|
|
* @param region_id The region id to remove from the device
|
|
* queues memory
|
|
* @param cap The capability to the removed memory
|
|
*
|
|
* @returns error on failure or SYS_ERR_OK on success
|
|
*
|
|
*/
|
|
errval_t devq_deregister(struct devq *q,
|
|
regionid_t region_id,
|
|
struct capref* cap)
|
|
{
|
|
errval_t err;
|
|
|
|
err = region_pool_remove_region(q->pool, region_id, cap);
|
|
if (err_is_fail(err)) {
|
|
return err;
|
|
}
|
|
DQI_DEBUG("deregister q=%p, cap=%p, regionid=%d \n", (void*) q,
|
|
(void*) cap, region_id);
|
|
|
|
#ifdef BENCH_DEVQ
|
|
start = bench_tsc();
|
|
#endif
|
|
err = q->f.dereg(q, region_id);
|
|
#ifdef BENCH_DEVQ
|
|
end = bench_tsc();
|
|
add_bench_entry(&ctl_dereg, end - start, "backend_deregister");
|
|
#endif
|
|
return err;
|
|
}
|
|
|
|
/**
|
|
* @brief Send a notification about new buffers on the queue
|
|
* Does nothing for direct queues.
|
|
*
|
|
* @param q The device queue to call the operation on
|
|
*
|
|
* @returns error on failure or SYS_ERR_OK on success
|
|
*
|
|
*/
|
|
errval_t devq_notify(struct devq *q)
|
|
{
|
|
errval_t err;
|
|
|
|
err = q->f.notify(q);
|
|
|
|
return err;
|
|
}
|
|
|
|
/**
|
|
* @brief Enforce coherency between of the buffers in the queue
|
|
* by either flushing the cache or invalidating it
|
|
*
|
|
* @param q The device queue to call the operation on
|
|
*
|
|
* @returns error on failure or SYS_ERR_OK on success
|
|
*
|
|
*/
|
|
errval_t devq_prepare(struct devq *q)
|
|
{
|
|
USER_PANIC("NIY\n");
|
|
return SYS_ERR_OK;
|
|
}
|
|
|
|
/**
|
|
* @brief Send a control message to the device queue
|
|
*
|
|
* @param q The device queue to call the operation on
|
|
* @param request The type of the control message*
|
|
* @param value The value for the request
|
|
*
|
|
* @returns error on failure or SYS_ERR_OK on success
|
|
*
|
|
*/
|
|
errval_t devq_control(struct devq *q,
|
|
uint64_t request,
|
|
uint64_t value,
|
|
uint64_t *result)
|
|
{
|
|
errval_t err;
|
|
|
|
err = q->f.ctrl(q, request, value, result);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
/**
|
|
* @brief destroys the device queue
|
|
*
|
|
* @param q The queue state to free (and the device queue to be
|
|
shut down)
|
|
*
|
|
* @returns error on failure or SYS_ERR_OK on success
|
|
*/
|
|
errval_t devq_destroy(struct devq *q)
|
|
{
|
|
errval_t err;
|
|
|
|
err = region_pool_destroy(q->pool);
|
|
if (err_is_fail(err)) {
|
|
return err;
|
|
}
|
|
|
|
return q->f.destroy(q);
|
|
}
|
|
|
|
|
|
void devq_set_state(struct devq *q, void *state)
|
|
{
|
|
q->state = state;
|
|
}
|
|
|
|
void * devq_get_state(struct devq *q)
|
|
{
|
|
return q->state;
|
|
}
|
|
|
|
struct iommu_client * devq_get_iommu_client(struct devq *q)
|
|
{
|
|
return q->iommu;
|
|
}
|