2d690920ac
git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@12360 a95241bf-73f2-0310-859d-f6bbb57e9c96
995 lines
20 KiB
C
995 lines
20 KiB
C
/*
|
|
* Copyright 2002-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
|
|
* Distributed under the terms of the MIT License.
|
|
*
|
|
* Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
|
|
* Distributed under the terms of the NewOS License.
|
|
*/
|
|
|
|
|
|
/* This file contains the cbuf functions. Cbuf is a memory allocator,
|
|
* currently not used for anything in kernel land other than ports.
|
|
*/
|
|
|
|
|
|
#include <OS.h>
|
|
#include <KernelExport.h>
|
|
#include <cbuf.h>
|
|
#include <vm.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
#define CBUF_LENGTH 2048
|
|
|
|
#define CBUF_FLAG_CHAIN_HEAD 1
|
|
#define CBUF_FLAG_CHAIN_TAIL 2
|
|
|
|
struct cbuf {
|
|
cbuf *next;
|
|
size_t length;
|
|
size_t total_length;
|
|
void *data;
|
|
int flags;
|
|
|
|
// fill the bytes to let this structure be exactly CBUF_LENGTH bytes large
|
|
char dat[CBUF_LENGTH - sizeof(struct cbuf *) -
|
|
2*sizeof(int) - sizeof(void *) - sizeof(int)];
|
|
};
|
|
|
|
#define ALLOCATE_CHUNK (B_PAGE_SIZE * 16)
|
|
#define CBUF_REGION_SIZE (4*1024*1024)
|
|
#define CBUF_BITMAP_SIZE (CBUF_REGION_SIZE / CBUF_LENGTH)
|
|
|
|
static cbuf *sFreeBufferList;
|
|
static mutex sFreeBufferListMutex;
|
|
static cbuf *sFreeBufferNoBlockList;
|
|
static spinlock sNoBlockSpinlock;
|
|
|
|
static spinlock sLowlevelSpinlock;
|
|
static area_id sBufferArea;
|
|
static cbuf *sBuffer;
|
|
static area_id sBitmapArea;
|
|
static uint8 *sBitmap;
|
|
|
|
|
|
/* Declarations we need that aren't in header files */
|
|
//uint16 ones_sum16(uint32, const void *, int);
|
|
|
|
|
|
// private part of the API implementation
|
|
|
|
|
|
static void
|
|
initialize_cbuf(cbuf *buf)
|
|
{
|
|
buf->length = sizeof(buf->dat);
|
|
buf->data = buf->dat;
|
|
buf->flags = 0;
|
|
buf->total_length = 0;
|
|
}
|
|
|
|
|
|
static void *
|
|
allocate_cbuf(size_t *_size)
|
|
{
|
|
size_t lengthFound = 0;
|
|
void *buffer;
|
|
int state;
|
|
int i;
|
|
int start = -1;
|
|
|
|
// dprintf("cbuf_alloc: asked to allocate size %d\n", *size);
|
|
|
|
state = disable_interrupts();
|
|
acquire_spinlock(&sLowlevelSpinlock);
|
|
|
|
// scan through the allocation bitmap, looking for the first free block
|
|
// XXX not optimal
|
|
for (i = 0; i < CBUF_BITMAP_SIZE; i++) {
|
|
if (!CHECK_BIT(sBitmap[i/8], i%8)) {
|
|
sBitmap[i/8] = SET_BIT(sBitmap[i/8], i%8);
|
|
if (start < 0)
|
|
start = i;
|
|
|
|
lengthFound += CBUF_LENGTH;
|
|
if (lengthFound >= *_size) {
|
|
// we're done
|
|
break;
|
|
}
|
|
} else if (start >= 0) {
|
|
// we've found a start of a run before, so we're done now
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (start < 0) {
|
|
// couldn't find any memory
|
|
buffer = NULL;
|
|
*_size = 0;
|
|
} else {
|
|
buffer = &sBuffer[start];
|
|
*_size = lengthFound;
|
|
}
|
|
|
|
release_spinlock(&sLowlevelSpinlock);
|
|
restore_interrupts(state);
|
|
|
|
return buffer;
|
|
}
|
|
|
|
|
|
static cbuf *
|
|
allocate_cbuf_mem(size_t size)
|
|
{
|
|
cbuf *buffer = NULL;
|
|
cbuf *lastBuffer = NULL;
|
|
cbuf *headBuffer = NULL;
|
|
int i;
|
|
int count;
|
|
size_t foundSize;
|
|
|
|
size = PAGE_ALIGN(size);
|
|
|
|
while (size > 0) {
|
|
foundSize = size;
|
|
buffer = (cbuf *)allocate_cbuf(&foundSize);
|
|
if (buffer == NULL) {
|
|
// couldn't allocate, lets bail with what we have
|
|
break;
|
|
}
|
|
|
|
size -= foundSize;
|
|
|
|
if (headBuffer == NULL)
|
|
headBuffer = lastBuffer = buffer;
|
|
|
|
while (foundSize > 0) {
|
|
initialize_cbuf(buffer);
|
|
|
|
headBuffer->total_length += buffer->length;
|
|
lastBuffer->next = buffer;
|
|
|
|
lastBuffer = buffer;
|
|
buffer++;
|
|
foundSize -= CBUF_LENGTH;
|
|
}
|
|
}
|
|
|
|
if (headBuffer) {
|
|
headBuffer->flags |= CBUF_FLAG_CHAIN_HEAD;
|
|
lastBuffer->flags |= CBUF_FLAG_CHAIN_TAIL;
|
|
lastBuffer->next = NULL;
|
|
}
|
|
|
|
return headBuffer;
|
|
}
|
|
|
|
|
|
static void
|
|
clear_chain(cbuf *head, cbuf **tail)
|
|
{
|
|
cbuf *buffer;
|
|
|
|
buffer = head;
|
|
*tail = NULL;
|
|
while (buffer) {
|
|
initialize_cbuf(buffer); // doesn't touch the next ptr
|
|
*tail = buffer;
|
|
buffer = buffer->next;
|
|
}
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
// public part of the API
|
|
|
|
|
|
/** Frees the specified buffer chain. Unlike cbuf_free_chain(),
|
|
* it doesn't block on a semaphore, but disables interrupts
|
|
* to update the non-block list.
|
|
*/
|
|
|
|
void
|
|
cbuf_free_chain_noblock(cbuf *buffer)
|
|
{
|
|
cbuf *head, *last;
|
|
int state;
|
|
|
|
if (buffer == NULL)
|
|
return;
|
|
|
|
head = buffer;
|
|
clear_chain(head, &last);
|
|
|
|
state = disable_interrupts();
|
|
acquire_spinlock(&sNoBlockSpinlock);
|
|
|
|
last->next = sFreeBufferNoBlockList;
|
|
sFreeBufferNoBlockList = head;
|
|
|
|
release_spinlock(&sNoBlockSpinlock);
|
|
restore_interrupts(state);
|
|
}
|
|
|
|
|
|
void
|
|
cbuf_free_chain(cbuf *buffer)
|
|
{
|
|
cbuf *head, *last;
|
|
|
|
if (buffer == NULL)
|
|
return;
|
|
|
|
head = buffer;
|
|
clear_chain(head, &last);
|
|
|
|
mutex_lock(&sFreeBufferListMutex);
|
|
|
|
last->next = sFreeBufferList;
|
|
sFreeBufferList = head;
|
|
|
|
mutex_unlock(&sFreeBufferListMutex);
|
|
}
|
|
|
|
|
|
cbuf *
|
|
cbuf_get_chain(size_t length)
|
|
{
|
|
size_t chainLength = 0;
|
|
cbuf *chain = NULL;
|
|
cbuf *tail = NULL;
|
|
|
|
if (length == 0)
|
|
panic("cbuf_get_chain(): passed size 0\n");
|
|
|
|
mutex_lock(&sFreeBufferListMutex);
|
|
|
|
while (chainLength < length) {
|
|
cbuf *tempBuffer;
|
|
|
|
if (sFreeBufferList == NULL) {
|
|
// we need to allocate some more cbufs
|
|
mutex_unlock(&sFreeBufferListMutex);
|
|
|
|
tempBuffer = allocate_cbuf_mem(ALLOCATE_CHUNK);
|
|
if (tempBuffer == NULL) {
|
|
// no more ram
|
|
if (chain)
|
|
cbuf_free_chain(chain);
|
|
|
|
return NULL;
|
|
}
|
|
cbuf_free_chain(tempBuffer);
|
|
|
|
mutex_lock(&sFreeBufferListMutex);
|
|
continue;
|
|
}
|
|
|
|
tempBuffer = sFreeBufferList;
|
|
sFreeBufferList = sFreeBufferList->next;
|
|
tempBuffer->next = chain;
|
|
if (chain == NULL)
|
|
tail = tempBuffer;
|
|
chain = tempBuffer;
|
|
|
|
chainLength += chain->length;
|
|
}
|
|
mutex_unlock(&sFreeBufferListMutex);
|
|
|
|
// now we have a chain, fixup the first and last entry
|
|
chain->total_length = length;
|
|
chain->flags |= CBUF_FLAG_CHAIN_HEAD;
|
|
tail->length -= chainLength - length;
|
|
tail->flags |= CBUF_FLAG_CHAIN_TAIL;
|
|
|
|
return chain;
|
|
}
|
|
|
|
|
|
cbuf *
|
|
cbuf_get_chain_noblock(size_t length)
|
|
{
|
|
size_t chainLength = 0;
|
|
cbuf *chain = NULL;
|
|
cbuf *tail = NULL;
|
|
int state;
|
|
|
|
state = disable_interrupts();
|
|
acquire_spinlock(&sNoBlockSpinlock);
|
|
|
|
while (chainLength < length) {
|
|
cbuf *tempBuffer;
|
|
|
|
if (sFreeBufferNoBlockList == NULL) {
|
|
dprintf("cbuf_get_chain_noblock: not enough cbufs\n");
|
|
release_spinlock(&sNoBlockSpinlock);
|
|
restore_interrupts(state);
|
|
|
|
if (chain != NULL)
|
|
cbuf_free_chain_noblock(chain);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
tempBuffer = sFreeBufferNoBlockList;
|
|
sFreeBufferNoBlockList = sFreeBufferNoBlockList->next;
|
|
tempBuffer->next = chain;
|
|
if (chain == NULL)
|
|
tail = tempBuffer;
|
|
chain = tempBuffer;
|
|
|
|
chainLength += chain->length;
|
|
}
|
|
release_spinlock(&sNoBlockSpinlock);
|
|
restore_interrupts(state);
|
|
|
|
// now we have a chain, fixup the first and last entry
|
|
chain->total_length = length;
|
|
chain->flags |= CBUF_FLAG_CHAIN_HEAD;
|
|
tail->length -= chainLength - length;
|
|
tail->flags |= CBUF_FLAG_CHAIN_TAIL;
|
|
|
|
return chain;
|
|
}
|
|
|
|
|
|
status_t
|
|
cbuf_memcpy_to_chain(cbuf *chain, size_t offset, const void *_source, size_t length)
|
|
{
|
|
cbuf *buffer;
|
|
char *source = (char *)_source;
|
|
int bufferOffset;
|
|
|
|
if (chain == NULL)
|
|
return B_BAD_VALUE;
|
|
|
|
if ((chain->flags & CBUF_FLAG_CHAIN_HEAD) == 0) {
|
|
dprintf("cbuf_memcpy_to_chain: chain at %p not head\n", chain);
|
|
return B_BAD_VALUE;
|
|
}
|
|
|
|
if (length + offset > chain->total_length) {
|
|
dprintf("cbuf_memcpy_to_chain: length + offset > size of cbuf chain\n");
|
|
return B_BAD_VALUE;
|
|
}
|
|
|
|
// find the starting cbuf in the chain to copy to
|
|
buffer = chain;
|
|
bufferOffset = 0;
|
|
|
|
while (offset > 0) {
|
|
if (buffer == NULL) {
|
|
dprintf("cbuf_memcpy_to_chain: end of chain reached too early!\n");
|
|
return B_ERROR;
|
|
}
|
|
|
|
if (offset < buffer->length) {
|
|
// this is the one
|
|
bufferOffset = offset;
|
|
break;
|
|
}
|
|
|
|
offset -= buffer->length;
|
|
buffer = buffer->next;
|
|
}
|
|
|
|
while (length > 0) {
|
|
int toCopy;
|
|
|
|
if (buffer == NULL) {
|
|
dprintf("cbuf_memcpy_to_chain: end of chain reached too early!\n");
|
|
return B_ERROR;
|
|
}
|
|
|
|
toCopy = min(length, buffer->length - bufferOffset);
|
|
memcpy((char *)buffer->data + bufferOffset, source, toCopy);
|
|
|
|
bufferOffset = 0;
|
|
length -= toCopy;
|
|
source += toCopy;
|
|
buffer = buffer->next;
|
|
}
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
status_t
|
|
cbuf_user_memcpy_to_chain(cbuf *chain, size_t offset, const void *_source, size_t length)
|
|
{
|
|
cbuf *buffer;
|
|
char *source = (char *)_source;
|
|
int bufferOffset;
|
|
int err;
|
|
|
|
if (chain == NULL)
|
|
return B_BAD_VALUE;
|
|
|
|
if ((chain->flags & CBUF_FLAG_CHAIN_HEAD) == 0) {
|
|
dprintf("cbuf_memcpy_to_chain: chain at %p not head\n", chain);
|
|
return B_BAD_VALUE;
|
|
}
|
|
|
|
if (length + offset > chain->total_length) {
|
|
dprintf("cbuf_memcpy_to_chain: length + offset > size of cbuf chain\n");
|
|
return B_BAD_VALUE;
|
|
}
|
|
|
|
// find the starting cbuf in the chain to copy to
|
|
buffer = chain;
|
|
bufferOffset = 0;
|
|
|
|
while (offset > 0) {
|
|
if (buffer == NULL) {
|
|
dprintf("cbuf_memcpy_to_chain: end of chain reached too early!\n");
|
|
return B_ERROR;
|
|
}
|
|
|
|
if (offset < buffer->length) {
|
|
// this is the one
|
|
bufferOffset = offset;
|
|
break;
|
|
}
|
|
|
|
offset -= buffer->length;
|
|
buffer = buffer->next;
|
|
}
|
|
|
|
err = B_NO_ERROR;
|
|
|
|
while (length > 0) {
|
|
int toCopy;
|
|
|
|
if (buffer == NULL) {
|
|
dprintf("cbuf_memcpy_to_chain: end of chain reached too early!\n");
|
|
return B_ERROR;
|
|
}
|
|
toCopy = min(length, buffer->length - bufferOffset);
|
|
|
|
err = user_memcpy((char *)buffer->data + bufferOffset, source, toCopy);
|
|
if (err < 0)
|
|
break; // memory exception
|
|
|
|
bufferOffset = 0;
|
|
length -= toCopy;
|
|
source += toCopy;
|
|
buffer = buffer->next;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
status_t
|
|
cbuf_memcpy_from_chain(void *_dest, cbuf *chain, size_t offset, size_t length)
|
|
{
|
|
cbuf *buffer;
|
|
char *dest = (char *)_dest;
|
|
int bufferOffset;
|
|
|
|
if (chain == NULL)
|
|
return B_BAD_VALUE;
|
|
|
|
if ((chain->flags & CBUF_FLAG_CHAIN_HEAD) == 0) {
|
|
dprintf("cbuf_memcpy_from_chain: chain at %p not head\n", chain);
|
|
return B_BAD_VALUE;
|
|
}
|
|
|
|
if (length + offset > chain->total_length) {
|
|
dprintf("cbuf_memcpy_from_chain: length + offset > size of cbuf chain\n");
|
|
return B_BAD_VALUE;
|
|
}
|
|
|
|
// find the starting cbuf in the chain to copy from
|
|
buffer = chain;
|
|
bufferOffset = 0;
|
|
|
|
while (offset > 0) {
|
|
if (buffer == NULL) {
|
|
dprintf("cbuf_memcpy_from_chain: end of chain reached too early!\n");
|
|
return B_ERROR;
|
|
}
|
|
|
|
if (offset < buffer->length) {
|
|
// this is the one
|
|
bufferOffset = offset;
|
|
break;
|
|
}
|
|
offset -= buffer->length;
|
|
buffer = buffer->next;
|
|
}
|
|
|
|
while (length > 0) {
|
|
int toCopy;
|
|
|
|
if (buffer == NULL) {
|
|
dprintf("cbuf_memcpy_from_chain: end of chain reached too early!\n");
|
|
return B_ERROR;
|
|
}
|
|
|
|
toCopy = min(length, buffer->length - bufferOffset);
|
|
memcpy(dest, (char *)buffer->data + bufferOffset, toCopy);
|
|
|
|
bufferOffset = 0;
|
|
length -= toCopy;
|
|
dest += toCopy;
|
|
buffer = buffer->next;
|
|
}
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
status_t
|
|
cbuf_user_memcpy_from_chain(void *_dest, cbuf *chain, size_t offset, size_t length)
|
|
{
|
|
cbuf *buffer;
|
|
char *dest = (char *)_dest;
|
|
int bufferOffset;
|
|
int err;
|
|
|
|
if (chain == NULL)
|
|
return B_BAD_VALUE;
|
|
|
|
if ((chain->flags & CBUF_FLAG_CHAIN_HEAD) == 0) {
|
|
dprintf("cbuf_memcpy_from_chain: chain at %p not head\n", chain);
|
|
return B_BAD_VALUE;
|
|
}
|
|
|
|
if (length + offset > chain->total_length) {
|
|
dprintf("cbuf_memcpy_from_chain: length + offset > size of cbuf chain\n");
|
|
return B_BAD_VALUE;
|
|
}
|
|
|
|
// find the starting cbuf in the chain to copy from
|
|
buffer = chain;
|
|
bufferOffset = 0;
|
|
|
|
while (offset > 0) {
|
|
if (buffer == NULL) {
|
|
dprintf("cbuf_memcpy_from_chain: end of chain reached too early!\n");
|
|
return B_ERROR;
|
|
}
|
|
|
|
if (offset < buffer->length) {
|
|
// this is the one
|
|
bufferOffset = offset;
|
|
break;
|
|
}
|
|
offset -= buffer->length;
|
|
buffer = buffer->next;
|
|
}
|
|
|
|
err = B_NO_ERROR;
|
|
|
|
while (length > 0) {
|
|
int toCopy;
|
|
|
|
if (buffer == NULL) {
|
|
dprintf("cbuf_memcpy_from_chain: end of chain reached too early!\n");
|
|
return B_ERROR;
|
|
}
|
|
|
|
toCopy = min(length, buffer->length - bufferOffset);
|
|
|
|
err = user_memcpy(dest, (char *)buffer->data + bufferOffset, toCopy);
|
|
if (err < 0)
|
|
break;
|
|
|
|
bufferOffset = 0;
|
|
length -= toCopy;
|
|
dest += toCopy;
|
|
buffer = buffer->next;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
cbuf *
|
|
cbuf_duplicate_chain(cbuf *chain, size_t offset, size_t length)
|
|
{
|
|
cbuf *buffer;
|
|
cbuf *newBuffer;
|
|
cbuf *destBuffer;
|
|
int destBufferOffset;
|
|
int bufferOffset;
|
|
|
|
if (chain == NULL)
|
|
return NULL;
|
|
|
|
if ((chain->flags & CBUF_FLAG_CHAIN_HEAD) == 0)
|
|
return NULL;
|
|
if (offset >= chain->total_length)
|
|
return NULL;
|
|
|
|
length = min(length, chain->total_length - offset);
|
|
|
|
newBuffer = cbuf_get_chain(length);
|
|
if (!newBuffer)
|
|
return NULL;
|
|
|
|
// find the starting cbuf in the chain to copy from
|
|
buffer = chain;
|
|
bufferOffset = 0;
|
|
while (offset > 0) {
|
|
if (buffer == NULL) {
|
|
cbuf_free_chain(newBuffer);
|
|
dprintf("cbuf_duplicate_chain: end of chain reached too early!\n");
|
|
return NULL;
|
|
}
|
|
if (offset < buffer->length) {
|
|
// this is the one
|
|
bufferOffset = offset;
|
|
break;
|
|
}
|
|
offset -= buffer->length;
|
|
buffer = buffer->next;
|
|
}
|
|
|
|
destBuffer = newBuffer;
|
|
destBufferOffset = 0;
|
|
|
|
while (length > 0) {
|
|
size_t toCopy;
|
|
|
|
if (buffer == NULL) {
|
|
cbuf_free_chain(newBuffer);
|
|
dprintf("cbuf_duplicate_chain: end of source chain reached too early!\n");
|
|
return NULL;
|
|
}
|
|
if (destBuffer == NULL) {
|
|
cbuf_free_chain(newBuffer);
|
|
dprintf("cbuf_duplicate_chain: end of destination chain reached too early!\n");
|
|
return NULL;
|
|
}
|
|
|
|
toCopy = min(destBuffer->length - destBufferOffset, buffer->length - bufferOffset);
|
|
toCopy = min(toCopy, length);
|
|
memcpy((char *)destBuffer->data + destBufferOffset, (char *)buffer->data + bufferOffset, toCopy);
|
|
|
|
length -= toCopy;
|
|
if (toCopy + bufferOffset == buffer->length) {
|
|
buffer = buffer->next;
|
|
bufferOffset = 0;
|
|
} else
|
|
bufferOffset += toCopy;
|
|
|
|
if (toCopy + destBufferOffset == destBuffer->length) {
|
|
destBuffer = destBuffer->next;
|
|
destBufferOffset = 0;
|
|
} else
|
|
destBufferOffset += toCopy;
|
|
}
|
|
|
|
return newBuffer;
|
|
}
|
|
|
|
|
|
cbuf *
|
|
cbuf_merge_chains(cbuf *chain1, cbuf *chain2)
|
|
{
|
|
cbuf *buffer;
|
|
|
|
if (!chain1 && !chain2)
|
|
return NULL;
|
|
|
|
if (!chain1)
|
|
return chain2;
|
|
if (!chain2)
|
|
return chain1;
|
|
|
|
if ((chain1->flags & CBUF_FLAG_CHAIN_HEAD) == 0) {
|
|
dprintf("cbuf_merge_chain: chain at %p not head\n", chain1);
|
|
return NULL;
|
|
}
|
|
|
|
if ((chain2->flags & CBUF_FLAG_CHAIN_HEAD) == 0) {
|
|
dprintf("cbuf_merge_chain: chain at %p not head\n", chain2);
|
|
return NULL;
|
|
}
|
|
|
|
// walk to the end of the first chain and tag the second one on
|
|
buffer = chain1;
|
|
while (buffer->next)
|
|
buffer = buffer->next;
|
|
|
|
buffer->next = chain2;
|
|
|
|
// modify the flags on the chain headers
|
|
buffer->flags &= ~CBUF_FLAG_CHAIN_TAIL;
|
|
chain1->total_length += chain2->total_length;
|
|
chain2->flags &= ~CBUF_FLAG_CHAIN_HEAD;
|
|
|
|
return chain1;
|
|
}
|
|
|
|
|
|
size_t
|
|
cbuf_get_length(cbuf *buffer)
|
|
{
|
|
if (buffer == NULL)
|
|
return 0;
|
|
|
|
if (buffer->flags & CBUF_FLAG_CHAIN_HEAD) {
|
|
return buffer->total_length;
|
|
} else {
|
|
int length = 0;
|
|
while (buffer) {
|
|
length += buffer->length;
|
|
buffer = buffer->next;
|
|
}
|
|
return length;
|
|
}
|
|
}
|
|
|
|
|
|
void *
|
|
cbuf_get_ptr(cbuf *buffer, size_t offset)
|
|
{
|
|
while (buffer) {
|
|
if (buffer->length > offset)
|
|
return (void *)((int)buffer->data + offset);
|
|
|
|
offset -= buffer->length;
|
|
buffer = buffer->next;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/** Returns true if the buffer chain is contiguous over
|
|
* the specified range.
|
|
*/
|
|
|
|
bool
|
|
cbuf_is_contig_region(cbuf *buffer, size_t start, size_t end)
|
|
{
|
|
while (buffer) {
|
|
if (buffer->length > start)
|
|
return buffer->length - start >= end;
|
|
|
|
start -= buffer->length;
|
|
end -= buffer->length;
|
|
buffer = buffer->next;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#if 0
|
|
uint16
|
|
cbuf_ones_cksum16(cbuf *buffer, size_t offset, size_t length)
|
|
{
|
|
uint16 sum = 0;
|
|
int swapped = 0;
|
|
|
|
if (buffer == NULL
|
|
|| (buffer->flags & CBUF_FLAG_CHAIN_HEAD) == 0)
|
|
return 0;
|
|
|
|
// find the start ptr
|
|
while (buffer) {
|
|
if (buffer->length > offset)
|
|
break;
|
|
|
|
offset -= buffer->length;
|
|
buffer = buffer->next;
|
|
}
|
|
|
|
// start checksumming
|
|
while (buffer && length > 0) {
|
|
void *ptr = (void *)((addr_t)buffer->data + offset);
|
|
size_t plen = min(length, buffer->length - offset);
|
|
|
|
sum = ones_sum16(sum, ptr, plen);
|
|
|
|
length -= plen;
|
|
buffer = buffer->next;
|
|
|
|
// if the pointer was odd, or the length was odd, but not both,
|
|
// the checksum was swapped
|
|
if ((buffer && length > 0)
|
|
&& (((offset % 2) && (plen % 2) == 0) || (((offset % 2) == 0) && (plen % 2)))) {
|
|
swapped ^= 1;
|
|
sum = ((sum & 0xff) << 8) | ((sum >> 8) & 0xff);
|
|
}
|
|
offset = 0;
|
|
}
|
|
|
|
if (swapped)
|
|
sum = ((sum & 0xff) << 8) | ((sum >> 8) & 0xff);
|
|
|
|
return ~sum;
|
|
}
|
|
#endif
|
|
|
|
/** Truncates the head of the buffer chain about truncBytes.
|
|
*/
|
|
|
|
status_t
|
|
cbuf_truncate_head(cbuf *buffer, size_t truncBytes)
|
|
{
|
|
cbuf *head = buffer;
|
|
|
|
if (!buffer || (buffer->flags & CBUF_FLAG_CHAIN_HEAD) == 0)
|
|
return B_BAD_VALUE;
|
|
|
|
while (buffer && truncBytes > 0) {
|
|
int toTrunc = min(truncBytes, buffer->length);
|
|
|
|
buffer->length -= toTrunc;
|
|
buffer->data = (void *)((int)buffer->data + toTrunc);
|
|
|
|
truncBytes -= toTrunc;
|
|
head->total_length -= toTrunc;
|
|
buffer = buffer->next;
|
|
}
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
/** Truncate the tail of the buffer chain about truncBytes.
|
|
*/
|
|
|
|
status_t
|
|
cbuf_truncate_tail(cbuf *buffer, size_t truncBytes)
|
|
{
|
|
cbuf *head = buffer;
|
|
size_t offset;
|
|
|
|
if (!buffer || (buffer->flags & CBUF_FLAG_CHAIN_HEAD) == 0)
|
|
return B_BAD_VALUE;
|
|
|
|
// we can't remove more than there is
|
|
if (truncBytes > head->total_length)
|
|
truncBytes = head->total_length;
|
|
|
|
offset = buffer->total_length - truncBytes;
|
|
|
|
while (buffer) {
|
|
if (offset < buffer->length)
|
|
break;
|
|
|
|
offset -= buffer->length;
|
|
buffer = buffer->next;
|
|
}
|
|
if (!buffer)
|
|
return B_ERROR;
|
|
|
|
head->total_length -= buffer->length - offset;
|
|
buffer->length = offset;
|
|
|
|
// clear out the rest of the buffers in this chain
|
|
while ((buffer = buffer->next) != NULL) {
|
|
head->total_length -= buffer->length;
|
|
buffer->length = 0;
|
|
}
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
|
|
static int
|
|
dbg_dump_cbuf_freelists(int argc, char **argv)
|
|
{
|
|
cbuf *buffer;
|
|
|
|
dprintf("sFreeBufferList:\n");
|
|
for (buffer = sFreeBufferList; buffer; buffer = buffer->next)
|
|
dprintf("%p ", buffer);
|
|
dprintf("\n");
|
|
|
|
dprintf("sFreeBufferNoBlockList:\n");
|
|
for (buffer = sFreeBufferNoBlockList; buffer; buffer = buffer->next)
|
|
dprintf("%p ", buffer);
|
|
dprintf("\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
void
|
|
cbuf_test(void)
|
|
{
|
|
cbuf *buffer, *buffer2;
|
|
char temp[1024];
|
|
unsigned int i;
|
|
|
|
dprintf("starting cbuffer test\n");
|
|
|
|
buffer = cbuf_get_chain(32);
|
|
if (!buffer)
|
|
panic("cbuf_test: failed allocation of 32\n");
|
|
|
|
buffer2 = cbuf_get_chain(3*1024*1024);
|
|
if (!buffer2)
|
|
panic("cbuf_test: failed allocation of 3mb\n");
|
|
|
|
buffer = cbuf_merge_chains(buffer2, buffer);
|
|
|
|
cbuf_free_chain(buffer);
|
|
|
|
dprintf("allocating too much...\n");
|
|
|
|
buffer = cbuf_get_chain(128*1024*1024);
|
|
if (buffer)
|
|
panic("cbuf_test: should have failed to allocate 128mb\n");
|
|
|
|
dprintf("touching memory allocated by cbuf\n");
|
|
|
|
buffer = cbuf_get_chain(7*1024*1024);
|
|
if (!buffer)
|
|
panic("cbuf_test: failed allocation of 7mb\n");
|
|
|
|
for (i = 0; i < sizeof(temp); i++)
|
|
temp[i] = i;
|
|
for (i = 0; i < 7*1024*1024 / sizeof(temp); i++) {
|
|
if (i % 128 == 0)
|
|
dprintf("%Lud\n", (long long)(i*sizeof(temp)));
|
|
cbuf_memcpy_to_chain(buffer, i*sizeof(temp), temp, sizeof(temp));
|
|
}
|
|
cbuf_free_chain(buffer);
|
|
|
|
dprintf("finished cbuffer test\n");
|
|
}
|
|
|
|
|
|
status_t
|
|
cbuf_init(void)
|
|
{
|
|
cbuf *buffer;
|
|
int i;
|
|
|
|
/*
|
|
sFreeBufferList = NULL;
|
|
sFreeBufferNoBlockList = NULL;
|
|
sNoBlockSpinlock = 0;
|
|
sLowlevelSpinlock = 0;
|
|
*/
|
|
|
|
// add the debug command
|
|
add_debugger_command("cbuf_freelist", &dbg_dump_cbuf_freelists, "Dumps the cbuf free lists");
|
|
|
|
if (mutex_init(&sFreeBufferListMutex, "cbuf_free_list") < B_OK) {
|
|
panic("cbuf_init: error creating cbuf_free_list mutex\n");
|
|
return B_NO_MEMORY;
|
|
}
|
|
|
|
// errors are fatal, that's why we don't clean up here
|
|
|
|
sBufferArea = create_area("cbuf region", (void **)&sBuffer, B_ANY_KERNEL_ADDRESS,
|
|
CBUF_REGION_SIZE, B_NO_LOCK, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
|
|
if (sBufferArea < 0) {
|
|
panic("cbuf_init: error creating cbuf region\n");
|
|
return B_NO_MEMORY;
|
|
}
|
|
|
|
sBitmapArea = create_area("cbuf bitmap region", (void **)&sBitmap, B_ANY_KERNEL_ADDRESS,
|
|
CBUF_BITMAP_SIZE / 8, B_FULL_LOCK, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
|
|
if (sBitmapArea < 0) {
|
|
panic("cbuf_init: error creating cbuf bitmap region\n");
|
|
return B_NO_MEMORY;
|
|
}
|
|
|
|
// initialize the bitmap
|
|
for (i = 0; i < CBUF_BITMAP_SIZE / 8; i++)
|
|
sBitmap[i] = 0;
|
|
|
|
buffer = allocate_cbuf_mem(ALLOCATE_CHUNK);
|
|
if (buffer == NULL)
|
|
return B_NO_MEMORY;
|
|
cbuf_free_chain_noblock(buffer);
|
|
|
|
buffer = allocate_cbuf_mem(ALLOCATE_CHUNK);
|
|
if (buffer == NULL)
|
|
return B_NO_MEMORY;
|
|
cbuf_free_chain(buffer);
|
|
|
|
return B_OK;
|
|
}
|