957 lines
30 KiB
C
957 lines
30 KiB
C
/*
|
|
* Copyright (c) 2007, 2008, 2009, 2010, 2011, 2012, 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 <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <stdbool.h>
|
|
#include <aos/aos.h>
|
|
#include <devif/queue_interface.h>
|
|
#include <devif/queue_interface_backend.h>
|
|
#include <devif/backends/debug.h>
|
|
#include "debug.h"
|
|
|
|
|
|
//#define DQ_ENABLE_HIST
|
|
#define HIST_SIZE 128
|
|
#define MAX_STR_SIZE 128
|
|
|
|
/*
|
|
* This is a debugging interface for the device queue interface that
|
|
* can be used with any existing queue. It can be stacked on top
|
|
* to check for non valid buffer enqueues/deqeues that might happen.
|
|
* A not valid enqueue of a buffer is when the endpoint that enqueues
|
|
* the buffer does not own the buffer.
|
|
*
|
|
* We keep track of the owned buffers as a list of regions which each
|
|
* contains a list of memory chunks.
|
|
* Each chunk specifies a offset within the region and its length.
|
|
*
|
|
* When a region is registered, we add one memory chunk that describes
|
|
* the whole region i.e. offset=0 length= length of region
|
|
*
|
|
* If a a buffer is enqueued, it has to be contained in one of these
|
|
* memory chunks. The memory chunk is then altered according how
|
|
* the buffer is contained in the chunk. If it is at the beginning or
|
|
* end of the chunk, the offset/length of the chunk is changed accordingly
|
|
* If the buffer is in the middle of the chunk, we split the memory chunk
|
|
* into two new memory chunks that do not contain the buffer.
|
|
*
|
|
* If a buffer is dequeued the buffer is added to the existing memory
|
|
* chunks if possible, otherwise a new memory chunk is added to the
|
|
* list of chunks. If a buffer is dequeued that is in between two
|
|
* memory chunks, the memory chunks are merged to one big chunk.
|
|
* We might fail to find the region id in our list of regions. In this
|
|
* case we add the region with the deqeued offset+length as a size.
|
|
* We can be sure that this region exists since the devq library itself
|
|
* does these checks if the region is known to the endpoint. This simply
|
|
* means the debugging queue on top of the other queue does not have a
|
|
* consistant view of the registered regions (but the queue below does)
|
|
*
|
|
* When a region is deregistered, the list of chunks has to only
|
|
* contain a single chunk that descirbes the whole region. Otherwise
|
|
* the call will fail since some of the buffers are still in use.
|
|
*
|
|
*/
|
|
|
|
struct memory_ele {
|
|
genoffset_t offset;
|
|
genoffset_t length;
|
|
struct memory_ele* next;
|
|
struct memory_ele* prev;
|
|
};
|
|
|
|
struct memory_list {
|
|
regionid_t rid;
|
|
genoffset_t length;
|
|
// is a region that we did not register ourselves
|
|
bool not_consistent;
|
|
struct memory_ele* buffers;
|
|
struct memory_list* next; // next in list of lists
|
|
};
|
|
|
|
struct operation {
|
|
char str[MAX_STR_SIZE];
|
|
genoffset_t offset;
|
|
genoffset_t length;
|
|
};
|
|
|
|
struct debug_q {
|
|
struct devq my_q;
|
|
struct devq* q;
|
|
struct memory_list* regions; // list of lists
|
|
struct slab_allocator alloc;
|
|
struct slab_allocator alloc_list;
|
|
uint16_t hist_head;
|
|
struct operation history[HIST_SIZE];
|
|
};
|
|
|
|
static void dump_list(struct memory_list* region)
|
|
{
|
|
struct memory_ele* ele = region->buffers;
|
|
int index = 0;
|
|
printf("================================================ \n");
|
|
while (ele != NULL) {
|
|
printf("Idx=%d offset=%lu length=%lu", index, ele->offset,
|
|
ele->length);
|
|
if (ele->prev != NULL) {
|
|
|
|
printf(" prev->offset=%lu prev->length=%lu", ele->prev->offset,
|
|
ele->prev->length);
|
|
}
|
|
printf(" \n");
|
|
ele = ele->next;
|
|
index++;
|
|
}
|
|
printf("================================================ \n");
|
|
}
|
|
|
|
// checks if there are elements in the list that should be merged
|
|
/*
|
|
static void check_consistency(struct memory_list* region)
|
|
{
|
|
struct memory_ele* ele = region->buffers;
|
|
while (ele->next != NULL) {
|
|
if (ele->offset + ele->length == ele->next->offset) {
|
|
printf("offset=%lu length=%lu \n", ele->offset, ele->length);
|
|
dump_list(region);
|
|
USER_PANIC("Found entry that should be merged \n");
|
|
}
|
|
ele = ele->next;
|
|
}
|
|
}
|
|
*/
|
|
#ifdef DQ_ENABLE_HIST
|
|
static void add_to_history(struct debug_q* q, genoffset_t offset,
|
|
genoffset_t length, char* s)
|
|
{
|
|
q->history[q->hist_head].offset = offset;
|
|
q->history[q->hist_head].length = length;
|
|
strncpy(q->history[q->hist_head].str, s, MAX_STR_SIZE);
|
|
q->hist_head = (q->hist_head + 1) % HIST_SIZE;
|
|
}
|
|
|
|
static void dump_history(struct debug_q* q)
|
|
{
|
|
for (int i = 0; i < HIST_SIZE; i++) {
|
|
printf("offset=%lu length=%lu %s\n", q->history[q->hist_head].offset,
|
|
q->history[q->hist_head].length, q->history[q->hist_head].str);
|
|
|
|
q->hist_head = (q->hist_head + 1) % HIST_SIZE;
|
|
}
|
|
}
|
|
#endif
|
|
/*
|
|
static bool in_list(struct memory_list* region, genoffset_t offset,
|
|
genoffset_t length)
|
|
{
|
|
struct memory_ele* ele = region->buffers;
|
|
while (ele != NULL) {
|
|
if (offset >= ele->offset &&
|
|
offset + length <= ele->offset+ele->length) {
|
|
return true;
|
|
}
|
|
ele = ele->next;
|
|
}
|
|
return false;
|
|
}
|
|
*/
|
|
|
|
|
|
|
|
static errval_t debug_register(struct devq* q, struct capref cap,
|
|
regionid_t rid)
|
|
{
|
|
errval_t err;
|
|
struct debug_q* que = (struct debug_q*) q;
|
|
DEBUG("Register \n");
|
|
struct frame_identity id;
|
|
|
|
err = frame_identify(cap, &id);
|
|
if (err_is_fail(err)) {
|
|
return err;
|
|
}
|
|
|
|
// queue of regions is empty
|
|
if (que->regions == NULL) {
|
|
// add the reigon
|
|
err = que->q->f.reg(que->q, cap, rid);
|
|
if (err_is_fail(err)) {
|
|
return err;
|
|
}
|
|
|
|
que->regions = slab_alloc(&que->alloc_list);
|
|
assert(que->regions != NULL);
|
|
|
|
que->regions->rid = rid;
|
|
que->regions->length = id.bytes;
|
|
que->regions->not_consistent = false;
|
|
que->regions->next = NULL;
|
|
// add the whole regions as a buffer
|
|
que->regions->buffers = slab_alloc(&que->alloc);
|
|
assert(que->regions->buffers != NULL);
|
|
|
|
memset(que->regions->buffers, 0, sizeof(que->regions->buffers));
|
|
que->regions->buffers->offset = 0;
|
|
que->regions->buffers->length = id.bytes;
|
|
que->regions->buffers->next = NULL;
|
|
DEBUG("Register rid=%"PRIu32" size=%"PRIu64" \n", rid,
|
|
id.bytes);
|
|
return SYS_ERR_OK;
|
|
}
|
|
|
|
struct memory_list* ele = que->regions;
|
|
while (ele->next != NULL) {
|
|
ele = ele->next;
|
|
}
|
|
|
|
err = que->q->f.reg(que->q, cap, rid);
|
|
if (err_is_fail(err)) {
|
|
return err;
|
|
}
|
|
|
|
// add the reigon
|
|
ele->next = slab_alloc(&que->alloc_list);
|
|
assert(ele->next != NULL);
|
|
|
|
ele = ele->next;
|
|
ele->rid = rid;
|
|
ele->next = NULL;
|
|
ele->length = id.bytes;
|
|
ele->not_consistent = false;
|
|
// add the whole regions as a buffer
|
|
ele->buffers = slab_alloc(&que->alloc);
|
|
assert(ele->buffers != NULL);
|
|
|
|
memset(ele->buffers, 0, sizeof(ele->buffers));
|
|
ele->buffers->offset = 0;
|
|
ele->buffers->length = id.bytes;
|
|
ele->buffers->next = NULL;
|
|
DEBUG("Register rid=%"PRIu32" size=%"PRIu64" \n", rid,
|
|
id.bytes);
|
|
|
|
return SYS_ERR_OK;
|
|
}
|
|
|
|
static errval_t debug_deregister(struct devq* q, regionid_t rid)
|
|
{
|
|
DEBUG("Deregister \n");
|
|
struct debug_q* que = (struct debug_q*) q;
|
|
errval_t err;
|
|
|
|
struct memory_list* ele = que->regions;
|
|
if (ele == NULL) {
|
|
return DEVQ_ERR_INVALID_REGION_ID;
|
|
}
|
|
|
|
// remove head
|
|
if (ele->rid == rid) {
|
|
// there should only be a single element in the list
|
|
// i.e. the whole region
|
|
if (ele->buffers->offset == 0 &&
|
|
ele->buffers->length == ele->length &&
|
|
ele->buffers->next == NULL) {
|
|
|
|
err = que->q->f.dereg(que->q, rid);
|
|
if (err_is_fail(err)) {
|
|
return err;
|
|
}
|
|
que->regions = ele->next;
|
|
|
|
DEBUG("removed region rid=%"PRIu32" size=%"PRIu64" \n", rid,
|
|
ele->length);
|
|
|
|
slab_free(&que->alloc, ele->buffers);
|
|
slab_free(&que->alloc_list, ele);
|
|
|
|
return SYS_ERR_OK;
|
|
} else {
|
|
|
|
DEBUG("Destroy error rid=%d offset=%"PRIu64" length=%"PRIu64" "
|
|
"should be offset=0 length=%"PRIu64"\n",
|
|
ele->rid, ele->buffers->offset,
|
|
ele->buffers->length, ele->length);
|
|
dump_list(ele);
|
|
return DEVQ_ERR_REGION_DESTROY;
|
|
}
|
|
}
|
|
|
|
while (ele->next != NULL) {
|
|
if (ele->next->rid == rid) {
|
|
if (ele->next->buffers->offset == 0 &&
|
|
ele->next->buffers->length == ele->next->length &&
|
|
ele->next->buffers->next == NULL) {
|
|
|
|
err = que->q->f.dereg(que->q, rid);
|
|
if (err_is_fail(err)) {
|
|
return err;
|
|
}
|
|
// remove from queue
|
|
struct memory_list* next = ele->next;
|
|
ele->next = ele->next->next;
|
|
|
|
DEBUG("removed region rid=%"PRIu32" size=%"PRIu64" \n", rid,
|
|
next->length);
|
|
|
|
slab_free(&que->alloc, next->buffers);
|
|
slab_free(&que->alloc_list, next);
|
|
|
|
return SYS_ERR_OK;
|
|
} else {
|
|
DEBUG("Destroy error rid=%d offset=%"PRIu64" length=%"PRIu64" "
|
|
"should be offset=0 length=%"PRIu64"\n",
|
|
ele->next->rid, ele->next->buffers->offset,
|
|
ele->next->buffers->length, ele->next->length);
|
|
|
|
dump_list(ele);
|
|
return DEVQ_ERR_REGION_DESTROY;
|
|
}
|
|
} else {
|
|
ele = ele->next;
|
|
}
|
|
}
|
|
|
|
|
|
return DEVQ_ERR_INVALID_REGION_ID;
|
|
}
|
|
|
|
|
|
static errval_t debug_control(struct devq* q, uint64_t cmd, uint64_t value,
|
|
uint64_t* result)
|
|
{
|
|
DEBUG("control \n");
|
|
struct debug_q* que = (struct debug_q*) q;
|
|
return que->q->f.ctrl(que->q, cmd, value, result);
|
|
}
|
|
|
|
|
|
static errval_t debug_notify(struct devq* q)
|
|
{
|
|
DEBUG("notify \n");
|
|
struct debug_q* que = (struct debug_q*) q;
|
|
return que->q->f.notify(que->q);
|
|
}
|
|
|
|
|
|
// is b1 in bounds of b2?
|
|
static bool buffer_in_bounds(genoffset_t offset_b1, genoffset_t len_b1,
|
|
genoffset_t offset_b2, genoffset_t len_b2)
|
|
{
|
|
return (offset_b1 >= offset_b2) && (len_b1 <= len_b2) &&
|
|
((offset_b1 + len_b1) <= offset_b2 + len_b2);
|
|
}
|
|
|
|
// assumes that the buffer described by offset and length is contained
|
|
// in the buffer that is given as a struct
|
|
static void remove_split_buffer(struct debug_q* que,
|
|
struct memory_list* region,
|
|
struct memory_ele* buffer,
|
|
genoffset_t offset,
|
|
genoffset_t length)
|
|
{
|
|
// split the buffer
|
|
// insert half before the buffer
|
|
|
|
DEBUG("enqueue offset=%"PRIu64" length=%"PRIu64" buf->offset=%lu "
|
|
"buf->length %lu \n",
|
|
offset, length, buffer->offset, buffer->length);
|
|
|
|
// check if buffer at beginning of region
|
|
if (buffer->offset == offset) {
|
|
buffer->offset += length;
|
|
buffer->length -= length;
|
|
|
|
if (buffer->length == 0) {
|
|
#ifdef DQ_ENABLE_HIST
|
|
add_to_history(que, offset, length, "enq cut of beginning remove");
|
|
#endif
|
|
DEBUG("enqueue remove buffer from list\n");
|
|
// remove
|
|
if (buffer->prev != NULL) {
|
|
buffer->prev->next = buffer->next;
|
|
} else {
|
|
region->buffers = buffer->next;
|
|
}
|
|
|
|
if (buffer->next != NULL) {
|
|
buffer->next->prev = buffer->prev;
|
|
}
|
|
slab_free(&que->alloc, buffer);
|
|
} else {
|
|
#ifdef DQ_ENABLE_HIST
|
|
add_to_history(que, offset, length, "enq cut of beginning");
|
|
#endif
|
|
}
|
|
|
|
DEBUG("enqueue first cut off begining results in offset=%"PRIu64" "
|
|
"length=%"PRIu64"\n",
|
|
buffer->offset, buffer->length);
|
|
return;
|
|
}
|
|
|
|
// check if buffer at end of region
|
|
if ((buffer->offset+buffer->length) == (offset+length)) {
|
|
buffer->length -= length;
|
|
|
|
if (buffer->length == 0) {
|
|
#ifdef DQ_ENABLE_HIST
|
|
add_to_history(que, offset, length, "enq cut of end remove");
|
|
#endif
|
|
// remove
|
|
if (buffer->prev != NULL) {
|
|
buffer->prev = buffer->next;
|
|
}
|
|
|
|
if (buffer->next != NULL) {
|
|
buffer->next->prev = buffer->prev;
|
|
}
|
|
slab_free(&que->alloc, buffer);
|
|
} else {
|
|
#ifdef DQ_ENABLE_HIST
|
|
add_to_history(que, offset, length, "enq cut of end");
|
|
#endif
|
|
}
|
|
|
|
DEBUG("enqueue first cut off end results in offset=%"PRIu64" "
|
|
"length=%"PRIu64"\n",
|
|
buffer->offset, buffer->length);
|
|
return;
|
|
}
|
|
|
|
// now if this did not work need to split buffer that contains the
|
|
// enqueued buffer into two buffers (might also result only in one)
|
|
|
|
// inset half before buffer
|
|
genoffset_t old_len = buffer->length;
|
|
|
|
buffer->length = offset - buffer->offset;
|
|
|
|
struct memory_ele* after = NULL;
|
|
after = slab_alloc(&que->alloc);
|
|
assert(after != NULL);
|
|
|
|
memset(after, 0, sizeof(after));
|
|
after->offset = buffer->offset + buffer->length + length;
|
|
after->length = old_len - buffer->length - length;
|
|
|
|
// insert after buffer
|
|
after->prev = buffer;
|
|
after->next = buffer->next;
|
|
|
|
if (buffer->next != NULL) {
|
|
buffer->next->prev = after;
|
|
}
|
|
|
|
buffer->next = after;
|
|
|
|
#ifdef DQ_ENABLE_HIST
|
|
add_to_history(que, offset, length, "enq split buffer");
|
|
#endif
|
|
|
|
DEBUG("Split buffer length=%lu to "
|
|
"offset=%"PRIu64" length=%"PRIu64" and "
|
|
"offset=%lu length=%lu \n",
|
|
old_len,
|
|
buffer->offset, buffer->length,
|
|
after->offset, after->length);
|
|
|
|
}
|
|
|
|
/*
|
|
* Inserts a buffer either before or after an existing buffer into the queue
|
|
* Checks for merges of prev/next buffer in the queue
|
|
*/
|
|
static void insert_merge_buffer(struct debug_q* que,
|
|
struct memory_list* region,
|
|
struct memory_ele* buffer,
|
|
genoffset_t offset,
|
|
genoffset_t length)
|
|
{
|
|
assert(buffer != NULL);
|
|
assert(region != NULL);
|
|
|
|
if (offset >= buffer->offset+buffer->length) {// insert after
|
|
// buffer is on lower boundary
|
|
//
|
|
if (buffer->offset+length == offset) {
|
|
buffer->length += length;
|
|
DEBUG("dequeue merge after "
|
|
"offset=%"PRIu64" length=%"PRIu64" to offset=%"PRIu64" "
|
|
"length=%"PRIu64"\n",
|
|
offset, length, buffer->offset, buffer->length);
|
|
// check other boundary for merge
|
|
if (buffer->next != NULL &&
|
|
(buffer->offset + buffer->length == buffer->next->offset)) {
|
|
buffer->length += buffer->next->length;
|
|
struct memory_ele* next = buffer->next;
|
|
if (buffer->next->next != NULL) {
|
|
buffer->next = buffer->next->next;
|
|
buffer->next->next->prev = buffer;
|
|
}
|
|
|
|
DEBUG("dequeue merge after more offset=%"PRIu64" "
|
|
"length=%"PRIu64" to offset=%"PRIu64" length=%"PRIu64" \n ",
|
|
next->offset, next->length,
|
|
buffer->offset, buffer->length);
|
|
#ifdef DQ_ENABLE_HIST
|
|
add_to_history(que, offset, length, "deq insert after"
|
|
" on lower boundary and merge");
|
|
#endif
|
|
slab_free(&que->alloc, next);
|
|
} else {
|
|
#ifdef DQ_ENABLE_HIST
|
|
add_to_history(que, offset, length, "deq insert after on lower boundary");
|
|
#endif
|
|
}
|
|
} else {
|
|
// check higher boundary
|
|
if (buffer->next != NULL &&
|
|
buffer->next->offset == offset+length) {
|
|
|
|
buffer->next->offset = offset;
|
|
buffer->next->length += length;
|
|
|
|
DEBUG("dequeue merge after more offset=%"PRIu64" "
|
|
"length=%"PRIu64" to offset=%"PRIu64" length=%"PRIu64" \n ",
|
|
offset, length,
|
|
buffer->next->offset, buffer->next->length);
|
|
|
|
#ifdef DQ_ENABLE_HIST
|
|
add_to_history(que, offset, length, "deq insert after"
|
|
" on higer boundary");
|
|
#endif
|
|
} else {
|
|
// buffer->next can be null and the newly inserted buffer
|
|
// is the inserted at the end -> check boundary
|
|
if (buffer->next == NULL &&
|
|
buffer->offset + buffer->length == offset) {
|
|
|
|
buffer->length += length;
|
|
|
|
#ifdef DQ_ENABLE_HIST
|
|
add_to_history(que, offset, length, "deq insert after"
|
|
" on higer boundary end");
|
|
#endif
|
|
DEBUG("dequeue insert after merged offset=%"PRIu64" "
|
|
"length=%"PRIu64" "
|
|
"to offset=%"PRIu64" length=%"PRIu64" \n",
|
|
offset, length, buffer->offset, buffer->length);
|
|
} else {
|
|
// insert in between
|
|
struct memory_ele* ele = slab_alloc(&que->alloc);
|
|
assert(ele != NULL);
|
|
|
|
ele->offset = offset;
|
|
ele->length = length;
|
|
ele->next = buffer->next;
|
|
ele->prev = buffer;
|
|
|
|
if (buffer->next != NULL) {
|
|
buffer->next->prev = ele;
|
|
}
|
|
|
|
buffer->next = ele;
|
|
|
|
#ifdef DQ_ENABLE_HIST
|
|
add_to_history(que, offset, length, "deq insert after"
|
|
" in between");
|
|
#endif
|
|
DEBUG("dequeue insert after offset=%"PRIu64" length=%"PRIu64" "
|
|
"after offset=%"PRIu64" length=%"PRIu64" \n",
|
|
offset, length, buffer->offset, buffer->length);
|
|
}
|
|
}
|
|
}
|
|
} else { // insert before buffer
|
|
// buffer is on lower boundary
|
|
if (buffer->offset == offset+length) {
|
|
buffer->length += length;
|
|
buffer->offset = offset;
|
|
|
|
// check other boundary
|
|
if (buffer->prev != NULL &&
|
|
(buffer->prev->offset+ buffer->prev->length ==
|
|
buffer->offset)) {
|
|
struct memory_ele* prev = buffer->prev;
|
|
prev->length += buffer->length;
|
|
prev->next = buffer->next;
|
|
|
|
if (buffer->next != NULL) {
|
|
buffer->next->prev = prev;
|
|
}
|
|
|
|
slab_free(&que->alloc, buffer);
|
|
|
|
#ifdef DQ_ENABLE_HIST
|
|
add_to_history(que, offset, length, "deq insert buffer"
|
|
" before lower boundary merge");
|
|
#endif
|
|
DEBUG("dequeue merge before more offset=%"PRIu64" "
|
|
"length=%"PRIu64" to offset=%"PRIu64" length=%"PRIu64" \n ",
|
|
offset, length, prev->offset, prev->length);
|
|
} else {
|
|
#ifdef DQ_ENABLE_HIST
|
|
add_to_history(que, offset, length, "deq insert buffer"
|
|
" before lower boundary");
|
|
#endif
|
|
}
|
|
} else {
|
|
// check lower boundary
|
|
if (buffer->prev != NULL &&
|
|
(buffer->prev->offset+ buffer->prev->length ==
|
|
offset)) {
|
|
if (length == 0) {
|
|
printf("Length is 0 \n");
|
|
buffer->prev->length += 2048;
|
|
}
|
|
|
|
buffer->prev->length += length;
|
|
|
|
#ifdef DQ_ENABLE_HIST
|
|
add_to_history(que, offset, length, "deq insert buffer"
|
|
" before prev lower boundary merge");
|
|
#endif
|
|
DEBUG("dequeue merge before more offset=%"PRIu64" "
|
|
"length=%"PRIu64" to offset=%"PRIu64" length=%"PRIu64" \n ",
|
|
offset, length, buffer->prev->offset, buffer->prev->length);
|
|
} else { // insert in between
|
|
|
|
// insert in between
|
|
struct memory_ele* ele = slab_alloc(&que->alloc);
|
|
assert(ele != NULL);
|
|
|
|
memset(ele, 0, sizeof(ele));
|
|
ele->offset = offset;
|
|
ele->length = length;
|
|
ele->next = buffer;
|
|
ele->prev = buffer->prev;
|
|
|
|
if (buffer->prev != NULL) {
|
|
buffer->prev->next = ele;
|
|
} else {
|
|
region->buffers = ele;
|
|
}
|
|
|
|
buffer->prev = ele;
|
|
|
|
#ifdef DQ_ENABLE_HIST
|
|
add_to_history(que, offset, length, "deq insert buffer"
|
|
" before in between");
|
|
#endif
|
|
DEBUG("dequeue insert before offset=%"PRIu64" length=%"PRIu64" "
|
|
"next is offset=%"PRIu64" length=%"PRIu64" \n",
|
|
offset, length,
|
|
buffer->offset, buffer->length);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static errval_t find_region(struct debug_q* que, struct memory_list** list,
|
|
regionid_t rid)
|
|
{
|
|
// find region
|
|
struct memory_list* region = que->regions;
|
|
|
|
while (region != NULL) {
|
|
if (region->rid == rid) {
|
|
break;
|
|
}
|
|
region = region->next;
|
|
}
|
|
|
|
// check if we found the region
|
|
if (region == NULL) {
|
|
return DEVQ_ERR_INVALID_REGION_ID;
|
|
}
|
|
|
|
*list = region;
|
|
return SYS_ERR_OK;
|
|
}
|
|
|
|
static errval_t debug_enqueue(struct devq* q, regionid_t rid,
|
|
genoffset_t offset, genoffset_t length,
|
|
genoffset_t valid_data, genoffset_t valid_length,
|
|
uint64_t flags)
|
|
{
|
|
assert(length > 0);
|
|
DEBUG("enqueue offset %"PRIu64" \n", offset);
|
|
errval_t err;
|
|
struct debug_q* que = (struct debug_q*) q;
|
|
|
|
// find region
|
|
struct memory_list* region = NULL;
|
|
|
|
err = find_region(que, ®ion, rid);
|
|
if (err_is_fail(err)){
|
|
return err;
|
|
}
|
|
|
|
//check_consistency(region);
|
|
|
|
// find the buffer
|
|
struct memory_ele* buffer = region->buffers;
|
|
|
|
if (region->buffers == NULL) {
|
|
return DEVQ_ERR_BUFFER_ALREADY_IN_USE;
|
|
}
|
|
|
|
// the only buffer
|
|
if (buffer->next == NULL) {
|
|
if (buffer_in_bounds(offset, length,
|
|
buffer->offset, buffer->length)) {
|
|
err = que->q->f.enq(que->q, rid, offset, length, valid_data,
|
|
valid_length, flags);
|
|
if (err_is_fail(err)) {
|
|
return err;
|
|
}
|
|
|
|
remove_split_buffer(que, region, buffer, offset, length);
|
|
return SYS_ERR_OK;
|
|
} else {
|
|
printf("Bounds check failed only buffer offset=%lu length=%lu "
|
|
" buf->offset=%lu buf->len=%lu\n", offset, length,
|
|
buffer->offset, buffer->length);
|
|
#ifdef DQ_ENABLE_HIST
|
|
dump_history(que);
|
|
#endif
|
|
dump_list(region);
|
|
return DEVQ_ERR_INVALID_BUFFER_ARGS;
|
|
}
|
|
}
|
|
|
|
|
|
// more than one buffer
|
|
while (buffer != NULL) {
|
|
if (buffer_in_bounds(offset, length,
|
|
buffer->offset, buffer->length)){
|
|
err = que->q->f.enq(que->q, rid, offset, length, valid_data,
|
|
valid_length, flags);
|
|
if (err_is_fail(err)) {
|
|
return err;
|
|
}
|
|
|
|
remove_split_buffer(que, region, buffer, offset, length);
|
|
return SYS_ERR_OK;
|
|
}
|
|
buffer = buffer->next;
|
|
}
|
|
|
|
printf("Did not find region offset=%ld length=%ld \n", offset, length);
|
|
#ifdef DQ_ENABLE_HIST
|
|
dump_history(que);
|
|
#endif
|
|
dump_list(region);
|
|
|
|
return DEVQ_ERR_INVALID_BUFFER_ARGS;
|
|
}
|
|
|
|
static errval_t debug_dequeue(struct devq* q, regionid_t* rid, genoffset_t* offset,
|
|
genoffset_t* length, genoffset_t* valid_data,
|
|
genoffset_t* valid_length, uint64_t* flags)
|
|
{
|
|
errval_t err;
|
|
struct debug_q* que = (struct debug_q*) q;
|
|
assert(que->q->f.deq != NULL);
|
|
err = que->q->f.deq(que->q, rid, offset, length, valid_data,
|
|
valid_length, flags);
|
|
if (err_is_fail(err)) {
|
|
return err;
|
|
}
|
|
DEBUG("dequeued offset=%lu \n", *offset);
|
|
|
|
struct memory_list* region = NULL;
|
|
|
|
err = find_region(que, ®ion, *rid);
|
|
if (err_is_fail(err)){
|
|
// region ids are checked bythe devq library, if we do not find
|
|
// the region id when dequeueing here we do not have a consistant
|
|
// view of two endpoints
|
|
//
|
|
// Add region
|
|
if (que->regions == NULL) {
|
|
printf("Adding region frirst %lu len \n", *offset + *length);
|
|
|
|
que->regions = slab_alloc(&que->alloc_list);
|
|
assert(que->regions != NULL);
|
|
|
|
que->regions->rid = *rid;
|
|
que->regions->not_consistent = true;
|
|
// region is at least offset + length
|
|
que->regions->length = *offset + *length;
|
|
que->regions->next = NULL;
|
|
// add the whole regions as a buffer
|
|
que->regions->buffers = slab_alloc(&que->alloc);
|
|
assert(que->regions->buffers != NULL);
|
|
|
|
memset(que->regions->buffers, 0, sizeof(que->regions->buffers));
|
|
que->regions->buffers->offset = 0;
|
|
que->regions->buffers->length = *offset + *length;
|
|
que->regions->buffers->next = NULL;
|
|
return SYS_ERR_OK;
|
|
}
|
|
|
|
struct memory_list* ele = que->regions;
|
|
while (ele->next != NULL) {
|
|
ele = ele->next;
|
|
}
|
|
|
|
printf("Adding region second %lu len \n", *offset + *length);
|
|
// add the reigon
|
|
ele->next = slab_alloc(&que->alloc_list);
|
|
assert(ele->next != NULL);
|
|
|
|
memset(que->regions->buffers, 0, sizeof(ele->next));
|
|
ele = ele->next;
|
|
|
|
ele->rid = *rid;
|
|
ele->next = NULL;
|
|
ele->not_consistent = true;
|
|
ele->length = *offset + *length;
|
|
// add the whole regions as a buffer
|
|
ele->buffers = slab_alloc(&que->alloc);
|
|
assert(ele->buffers != NULL);
|
|
|
|
memset(ele->buffers, 0, sizeof(ele->buffers));
|
|
ele->buffers->offset = 0;
|
|
ele->buffers->length = *offset + *length;
|
|
ele->buffers->next = NULL;
|
|
return SYS_ERR_OK;
|
|
}
|
|
|
|
if (region->not_consistent) {
|
|
if ((*offset + *length) > region->length) {
|
|
region->length = *offset + *length;
|
|
}
|
|
}
|
|
|
|
//check_consistency(region);
|
|
|
|
// find the buffer
|
|
struct memory_ele* buffer = region->buffers;
|
|
if (buffer == NULL) {
|
|
region->buffers = slab_alloc(&que->alloc);
|
|
assert(region->buffers != NULL);
|
|
|
|
region->buffers->offset = *offset;
|
|
region->buffers->length = *length;
|
|
region->buffers->next = NULL;
|
|
region->buffers->prev = NULL;
|
|
return SYS_ERR_OK;
|
|
}
|
|
|
|
if (buffer->next == NULL) {
|
|
if (!buffer_in_bounds(*offset, *length, buffer->offset,
|
|
buffer->length)) {
|
|
insert_merge_buffer(que, region, buffer, *offset, *length);
|
|
return SYS_ERR_OK;
|
|
} else {
|
|
return DEVQ_ERR_BUFFER_NOT_IN_USE;
|
|
}
|
|
}
|
|
|
|
|
|
while (buffer->next != NULL) {
|
|
if (*offset >= buffer->offset) {
|
|
buffer = buffer->next;
|
|
} else {
|
|
if (!buffer_in_bounds(*offset, *length, buffer->offset,
|
|
buffer->length)) {
|
|
insert_merge_buffer(que, region, buffer, *offset, *length);
|
|
return SYS_ERR_OK;
|
|
} else {
|
|
return DEVQ_ERR_BUFFER_NOT_IN_USE;
|
|
}
|
|
}
|
|
}
|
|
|
|
// insert after the last buffer
|
|
if (!buffer_in_bounds(*offset, *length, buffer->offset,
|
|
buffer->length)) {
|
|
insert_merge_buffer(que, region, buffer, *offset, *length);
|
|
return SYS_ERR_OK;
|
|
}
|
|
|
|
return DEVQ_ERR_BUFFER_NOT_IN_USE;
|
|
}
|
|
|
|
static errval_t debug_destroy(struct devq* devq)
|
|
{
|
|
// TODO cleanup
|
|
return SYS_ERR_OK;
|
|
}
|
|
|
|
/**
|
|
* Public functions
|
|
*
|
|
*/
|
|
|
|
errval_t debug_create(struct debug_q** q, struct devq* other_q)
|
|
{
|
|
errval_t err;
|
|
struct debug_q* que;
|
|
que = calloc(1, sizeof(struct debug_q));
|
|
assert(que);
|
|
|
|
slab_init(&que->alloc, sizeof(struct memory_ele),
|
|
slab_default_refill);
|
|
|
|
slab_init(&que->alloc_list, sizeof(struct memory_list),
|
|
slab_default_refill);
|
|
|
|
que->q = other_q;
|
|
err = devq_init(&que->my_q, false);
|
|
if (err_is_fail(err)) {
|
|
return err;
|
|
}
|
|
|
|
que->my_q.f.reg = debug_register;
|
|
que->my_q.f.dereg = debug_deregister;
|
|
que->my_q.f.ctrl = debug_control;
|
|
que->my_q.f.notify = debug_notify;
|
|
que->my_q.f.enq = debug_enqueue;
|
|
que->my_q.f.deq = debug_dequeue;
|
|
que->my_q.f.destroy = debug_destroy;
|
|
*q = que;
|
|
return SYS_ERR_OK;
|
|
}
|
|
|
|
errval_t debug_dump_region(struct debug_q* que, regionid_t rid)
|
|
{
|
|
errval_t err;
|
|
// find region
|
|
struct memory_list* region = NULL;
|
|
|
|
err = find_region(que, ®ion, rid);
|
|
if (err_is_fail(err)){
|
|
return err;
|
|
}
|
|
|
|
dump_list(region);
|
|
return SYS_ERR_OK;
|
|
}
|
|
|
|
|
|
void debug_dump_history(struct debug_q* q)
|
|
{
|
|
#ifdef DQ_ENABLE_HIST
|
|
dump_history(q);
|
|
#endif
|
|
}
|
|
|
|
errval_t debug_add_region(struct debug_q* q, struct capref cap,
|
|
regionid_t rid)
|
|
{
|
|
return devq_add_region((struct devq*) q, cap, rid);
|
|
}
|
|
|
|
errval_t debug_remove_region(struct debug_q* q, regionid_t rid)
|
|
{
|
|
return devq_remove_region((struct devq*) q, rid);
|
|
}
|