/* * 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 #include #include #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; }