A DMABuffer doesn't have a fixed bounce buffer assigned anymore. We do

dynamically assign one when needed. Under the assumption that in most
cases a bounce buffer isn't needed, we can thus prepare a lot more
operations.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@27185 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold
2008-08-24 16:57:31 +00:00
parent fb3edfeb27
commit 025f7c3289
6 changed files with 146 additions and 90 deletions
@@ -860,7 +860,8 @@ cd_set_capacity(cd_driver_info* info, uint64 capacity, uint32 blockSize)
}
// TODO: we need to replace the DMAResource in our IOScheduler
status_t status = info->dma_resource->Init(info->node, blockSize, 32);
status_t status = info->dma_resource->Init(info->node, blockSize, 1024,
32);
if (status != B_OK)
panic("initializing DMAResource failed: %s", strerror(status));
@@ -411,7 +411,8 @@ das_set_capacity(das_driver_info* info, uint64 capacity, uint32 blockSize)
}
// TODO: we need to replace the DMAResource in our IOScheduler
status_t status = info->dma_resource->Init(info->node, blockSize, 32);
status_t status = info->dma_resource->Init(info->node, blockSize, 1024,
32);
if (status != B_OK)
panic("initializing DMAResource failed: %s", strerror(status));
@@ -28,17 +28,13 @@ const size_t kMaxBounceBufferSize = 4 * B_PAGE_SIZE;
DMABuffer*
DMABuffer::Create(size_t count, void* bounceBuffer, addr_t physicalBounceBuffer,
size_t bounceBufferSize)
DMABuffer::Create(size_t count)
{
DMABuffer* buffer = (DMABuffer*)malloc(
sizeof(DMABuffer) + sizeof(iovec) * (count - 1));
if (buffer == NULL)
return NULL;
buffer->fBounceBuffer = bounceBuffer;
buffer->fPhysicalBounceBuffer = physicalBounceBuffer;
buffer->fBounceBufferSize = bounceBufferSize;
buffer->fVecCount = count;
return buffer;
@@ -61,24 +57,15 @@ DMABuffer::AddVec(void* base, size_t size)
}
void
DMABuffer::SetToBounceBuffer(size_t length)
{
fVecs[0].iov_base = (void*)fPhysicalBounceBuffer;
fVecs[0].iov_len = length;
fVecCount = 1;
}
bool
DMABuffer::UsesBounceBufferAt(uint32 index)
{
if (index >= fVecCount)
if (index >= fVecCount || fBounceBuffer == NULL)
return false;
return (addr_t)fVecs[index].iov_base >= fPhysicalBounceBuffer
return (addr_t)fVecs[index].iov_base >= fBounceBuffer->physical_address
&& (addr_t)fVecs[index].iov_base
< fPhysicalBounceBuffer + fBounceBufferSize;
< fBounceBuffer->physical_address + fBounceBuffer->size;
}
@@ -87,9 +74,9 @@ DMABuffer::Dump() const
{
kprintf("DMABuffer at %p\n", this);
kprintf(" bounce buffer: %p (physical %#lx)\n", fBounceBuffer,
fPhysicalBounceBuffer);
kprintf(" bounce buffer size: %lu\n", fBounceBufferSize);
kprintf(" bounce buffer: %p (physical %#lx)\n",
fBounceBuffer->address, fBounceBuffer->physical_address);
kprintf(" bounce buffer size: %lu\n", fBounceBuffer->size);
kprintf(" vecs: %lu\n", fVecCount);
for (uint32 i = 0; i < fVecCount; i++) {
@@ -111,11 +98,14 @@ DMAResource::~DMAResource()
{
mutex_destroy(&fLock);
free(fScratchVecs);
// TODO: Delete DMABuffers and BounceBuffers!
}
status_t
DMAResource::Init(device_node* node, size_t blockSize, uint32 bufferCount)
DMAResource::Init(device_node* node, size_t blockSize, uint32 bufferCount,
uint32 bounceBufferCount)
{
dma_restrictions restrictions;
memset(&restrictions, 0, sizeof(dma_restrictions));
@@ -143,17 +133,18 @@ DMAResource::Init(device_node* node, size_t blockSize, uint32 bufferCount)
B_DMA_MAX_SEGMENT_COUNT, &value, true) == B_OK)
restrictions.max_segment_count = value;
return Init(restrictions, blockSize, bufferCount);
return Init(restrictions, blockSize, bufferCount, bounceBufferCount);
}
status_t
DMAResource::Init(const dma_restrictions& restrictions, size_t blockSize,
uint32 bufferCount)
uint32 bufferCount, uint32 bounceBufferCount)
{
fRestrictions = restrictions;
fBlockSize = blockSize == 0 ? 1 : blockSize;
fBufferCount = bufferCount;
fBounceBufferCount = bounceBufferCount;
fBounceBufferSize = 0;
if (fRestrictions.high_address == 0)
@@ -188,64 +179,85 @@ DMAResource::Init(const dma_restrictions& restrictions, size_t blockSize,
if (fScratchVecs == NULL)
return B_NO_MEMORY;
// TODO: create bounce buffers in as few areas as feasible
for (size_t i = 0; i < fBufferCount; i++) {
DMABuffer* buffer;
status_t error = CreateBuffer(fBounceBufferSize, &buffer);
status_t error = CreateBuffer(&buffer);
if (error != B_OK)
return error;
fDMABuffers.Add(buffer);
}
// TODO: create bounce buffers in as few areas as feasible
for (size_t i = 0; i < fBounceBufferCount; i++) {
DMABounceBuffer* buffer;
status_t error = CreateBounceBuffer(&buffer);
if (error != B_OK)
return error;
fBounceBuffers.Add(buffer);
}
return B_OK;
}
status_t
DMAResource::CreateBuffer(size_t size, DMABuffer** _buffer)
DMAResource::CreateBuffer(DMABuffer** _buffer)
{
DMABuffer* buffer = DMABuffer::Create(fRestrictions.max_segment_count);
if (buffer == NULL)
return B_NO_MEMORY;
*_buffer = buffer;
return B_OK;
}
status_t
DMAResource::CreateBounceBuffer(DMABounceBuffer** _buffer)
{
void* bounceBuffer = NULL;
addr_t physicalBase = 0;
area_id area = -1;
size_t size = ROUNDUP(fBounceBufferSize, B_PAGE_SIZE);
if (size != 0) {
if (fRestrictions.alignment > B_PAGE_SIZE)
dprintf("dma buffer restrictions not yet implemented: alignment %lu\n", fRestrictions.alignment);
if (fRestrictions.boundary > B_PAGE_SIZE)
dprintf("dma buffer restrictions not yet implemented: boundary %lu\n", fRestrictions.boundary);
if (fRestrictions.alignment > B_PAGE_SIZE)
dprintf("dma buffer restrictions not yet implemented: alignment %lu\n", fRestrictions.alignment);
if (fRestrictions.boundary > B_PAGE_SIZE)
dprintf("dma buffer restrictions not yet implemented: boundary %lu\n", fRestrictions.boundary);
size = ROUNDUP(size, B_PAGE_SIZE);
bounceBuffer = (void*)fRestrictions.low_address;
bounceBuffer = (void*)fRestrictions.low_address;
// TODO: We also need to enforce the boundary restrictions.
area = create_area("dma buffer", &bounceBuffer, B_PHYSICAL_BASE_ADDRESS,
size, B_CONTIGUOUS, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
if (area < B_OK)
return area;
area = create_area("dma buffer", &bounceBuffer, B_PHYSICAL_BASE_ADDRESS,
size, B_CONTIGUOUS, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
if (area < B_OK)
return area;
physical_entry entry;
if (get_memory_map(bounceBuffer, size, &entry, 1) != B_OK) {
panic("get_memory_map() failed.");
delete_area(area);
return B_ERROR;
}
physicalBase = (addr_t)entry.address;
if (fRestrictions.high_address < physicalBase + size) {
delete_area(area);
return B_NO_MEMORY;
}
physical_entry entry;
if (get_memory_map(bounceBuffer, size, &entry, 1) != B_OK) {
panic("get_memory_map() failed.");
delete_area(area);
return B_ERROR;
}
DMABuffer* buffer = DMABuffer::Create(fRestrictions.max_segment_count,
bounceBuffer, physicalBase, fBounceBufferSize);
physicalBase = (addr_t)entry.address;
if (fRestrictions.high_address < physicalBase + size) {
delete_area(area);
return B_NO_MEMORY;
}
DMABounceBuffer* buffer = new(std::nothrow) DMABounceBuffer;
if (buffer == NULL) {
delete_area(area);
return B_NO_MEMORY;
}
buffer->address = bounceBuffer;
buffer->physical_address = physicalBase;
buffer->size = size;
*_buffer = buffer;
return B_OK;
}
@@ -458,8 +470,18 @@ DMAResource::TranslateNext(IORequest* request, IOOperation* operation)
// check alignment, boundaries, etc. and set vecs in DMA buffer
// Fetch a bounce buffer we can use for the DMABuffer.
// TODO: We should do that lazily when needed!
DMABounceBuffer* bounceBuffer = NULL;
if (_NeedsBoundsBuffers()) {
bounceBuffer = fBounceBuffers.Head();
if (bounceBuffer == NULL)
return B_BUSY;
}
dmaBuffer->SetBounceBuffer(bounceBuffer);
size_t dmaLength = 0;
addr_t physicalBounceBuffer = dmaBuffer->PhysicalBounceBuffer();
addr_t physicalBounceBuffer = dmaBuffer->PhysicalBounceBufferAddress();
size_t bounceLeft = fBounceBufferSize;
size_t transferLeft = totalLength;
@@ -593,9 +615,11 @@ DMAResource::TranslateNext(IORequest* request, IOOperation* operation)
// entirely.
if (diff == 0 && offset + dmaLength > requestEnd) {
const iovec& dmaVec = dmaBuffer->VecAt(dmaBuffer->VecCount() - 1);
ASSERT((addr_t)dmaVec.iov_base >= dmaBuffer->PhysicalBounceBuffer()
ASSERT((addr_t)dmaVec.iov_base
>= dmaBuffer->PhysicalBounceBufferAddress()
&& (addr_t)dmaVec.iov_base
< dmaBuffer->PhysicalBounceBuffer() + fBounceBufferSize);
< dmaBuffer->PhysicalBounceBufferAddress()
+ fBounceBufferSize);
// We can be certain that the last vec is a bounce buffer vec,
// since otherwise the DMA buffer couldn't exceed the end of the
// request data.
@@ -647,7 +671,7 @@ DMAResource::TranslateNext(IORequest* request, IOOperation* operation)
// and hit the max segment count. In this case we just use the
// bounce buffer for as much as possible of the total length.
dmaBuffer->SetVecCount(0);
addr_t base = dmaBuffer->PhysicalBounceBuffer();
addr_t base = dmaBuffer->PhysicalBounceBufferAddress();
dmaLength = min_c(totalLength, fBounceBufferSize)
& ~(max_c(fBlockSize, fRestrictions.alignment) - 1);
_RestrictBoundaryAndSegmentSize(base, dmaLength);
@@ -676,7 +700,14 @@ DMAResource::TranslateNext(IORequest* request, IOOperation* operation)
min_c(offset + dmaLength, requestEnd) - originalOffset);
operation->SetRange(offset, dmaLength);
operation->SetPartial(partialBegin != 0, offset + dmaLength > requestEnd);
// If we don't need the bounce buffer, we put it back, otherwise
operation->SetUsesBounceBuffer(bounceLeft < fBounceBufferSize);
if (operation->UsesBounceBuffer())
fBounceBuffers.RemoveHead();
else
dmaBuffer->SetBounceBuffer(NULL);
status_t error = operation->Prepare(request);
if (error != B_OK)
@@ -696,6 +727,10 @@ DMAResource::RecycleBuffer(DMABuffer* buffer)
MutexLocker _(fLock);
fDMABuffers.Add(buffer);
if (buffer->BounceBuffer() != NULL) {
fBounceBuffers.Add(buffer->BounceBuffer());
buffer->SetBounceBuffer(NULL);
}
}
@@ -29,11 +29,18 @@ struct dma_restrictions {
};
struct DMABounceBuffer : public DoublyLinkedListLinkImpl<DMABounceBuffer> {
void* address;
addr_t physical_address;
size_t size;
};
typedef DoublyLinkedList<DMABounceBuffer> DMABounceBufferList;
class DMABuffer : public DoublyLinkedListLinkImpl<DMABuffer> {
public:
static DMABuffer* Create(size_t count, void* bounceBuffer,
addr_t physicalBounceBuffer,
size_t bounceBufferSize);
static DMABuffer* Create(size_t count);
iovec* Vecs() { return fVecs; }
iovec& VecAt(size_t index) { return fVecs[index]; }
@@ -42,21 +49,27 @@ public:
void AddVec(void* base, size_t size);
void* BounceBuffer() const { return fBounceBuffer; }
addr_t PhysicalBounceBuffer() const
{ return fPhysicalBounceBuffer; }
void SetBounceBuffer(DMABounceBuffer* bounceBuffer)
{ fBounceBuffer = bounceBuffer; }
DMABounceBuffer* BounceBuffer() const { return fBounceBuffer; }
void* BounceBufferAddress() const
{ return fBounceBuffer
? fBounceBuffer->address : NULL; }
addr_t PhysicalBounceBufferAddress() const
{ return fBounceBuffer
? fBounceBuffer->physical_address
: 0; }
size_t BounceBufferSize() const
{ return fBounceBufferSize; }
{ return fBounceBuffer
? fBounceBuffer->size : 0; }
bool UsesBounceBufferAt(uint32 index);
void SetToBounceBuffer(size_t length);
void Dump() const;
private:
void* fBounceBuffer;
addr_t fPhysicalBounceBuffer;
size_t fBounceBufferSize;
DMABounceBuffer* fBounceBuffer;
uint32 fVecCount;
iovec fVecs[1];
};
@@ -71,13 +84,14 @@ public:
~DMAResource();
status_t Init(const dma_restrictions& restrictions,
size_t blockSize, uint32 bufferCount);
size_t blockSize, uint32 bufferCount,
uint32 bounceBufferCount);
status_t Init(device_node* node, size_t blockSize,
uint32 bufferCount);
uint32 bufferCount,
uint32 bounceBufferCount);
status_t CreateBuffer(DMABuffer** _buffer)
{ return CreateBuffer(0, _buffer); }
status_t CreateBuffer(size_t size, DMABuffer** _buffer);
status_t CreateBuffer(DMABuffer** _buffer);
status_t CreateBounceBuffer(DMABounceBuffer** _buffer);
status_t TranslateNext(IORequest* request,
IOOperation* operation);
@@ -101,8 +115,10 @@ private:
dma_restrictions fRestrictions;
size_t fBlockSize;
uint32 fBufferCount;
uint32 fBounceBufferCount;
size_t fBounceBufferSize;
DMABufferList fDMABuffers;
DMABounceBufferList fBounceBuffers;
iovec* fScratchVecs;
};
@@ -236,8 +236,8 @@ IOOperation::Finish()
if (fParent->IsRead() && UsesBounceBuffer()) {
TRACE(" read with bounce buffer\n");
// copy the bounce buffer segments to the final location
uint8* bounceBuffer = (uint8*)fDMABuffer->BounceBuffer();
addr_t bounceBufferStart = fDMABuffer->PhysicalBounceBuffer();
uint8* bounceBuffer = (uint8*)fDMABuffer->BounceBufferAddress();
addr_t bounceBufferStart = fDMABuffer->PhysicalBounceBufferAddress();
addr_t bounceBufferEnd = bounceBufferStart
+ fDMABuffer->BounceBufferSize();
@@ -308,8 +308,9 @@ IOOperation::Prepare(IORequest* request)
// which will be copied after their respective read phase.
if (UsesBounceBuffer()) {
TRACE(" write with bounce buffer\n");
uint8* bounceBuffer = (uint8*)fDMABuffer->BounceBuffer();
addr_t bounceBufferStart = fDMABuffer->PhysicalBounceBuffer();
uint8* bounceBuffer = (uint8*)fDMABuffer->BounceBufferAddress();
addr_t bounceBufferStart
= fDMABuffer->PhysicalBounceBufferAddress();
addr_t bounceBufferEnd = bounceBufferStart
+ fDMABuffer->BounceBufferSize();
@@ -522,10 +523,10 @@ IOOperation::_CopyPartialBegin(bool isWrite, bool& singleBlockOnly)
if (isWrite) {
return fParent->CopyData(OriginalOffset(),
(uint8*)fDMABuffer->BounceBuffer() + relativeOffset, length);
(uint8*)fDMABuffer->BounceBufferAddress() + relativeOffset, length);
} else {
return fParent->CopyData(
(uint8*)fDMABuffer->BounceBuffer() + relativeOffset,
(uint8*)fDMABuffer->BounceBufferAddress() + relativeOffset,
OriginalOffset(), length);
}
}
@@ -538,8 +539,9 @@ IOOperation::_CopyPartialEnd(bool isWrite)
const iovec& lastVec = fDMABuffer->VecAt(fDMABuffer->VecCount() - 1);
off_t lastVecPos = fOffset + fLength - fBlockSize;
uint8* base = (uint8*)fDMABuffer->BounceBuffer() + ((addr_t)lastVec.iov_base
+ lastVec.iov_len - fBlockSize - fDMABuffer->PhysicalBounceBuffer());
uint8* base = (uint8*)fDMABuffer->BounceBufferAddress()
+ ((addr_t)lastVec.iov_base + lastVec.iov_len - fBlockSize
- fDMABuffer->PhysicalBounceBufferAddress());
// NOTE: this won't work if we don't use the bounce buffer contiguously
// (because of boundary alignments).
size_t length = OriginalOffset() + OriginalLength() - lastVecPos;
@@ -16,8 +16,9 @@
#include "IOScheduler.h"
#define DMA_TEST_BLOCK_SIZE 512
#define DMA_TEST_BUFFER_COUNT 10
#define DMA_TEST_BLOCK_SIZE 512
#define DMA_TEST_BUFFER_COUNT 10
#define DMA_TEST_BOUNCE_BUFFER_COUNT 128
class TestSuite;
@@ -120,7 +121,7 @@ public:
restrictions.max_segment_count, restrictions.max_segment_size,
restrictions.flags);
status_t status = fDMAResource.Init(restrictions, blockSize, 10);
status_t status = fDMAResource.Init(restrictions, blockSize, 10, 10);
if (status != B_OK)
panic("initializing DMA resource failed: %s\n", strerror(status));
}
@@ -589,8 +590,8 @@ Test::Run(DMAResource& resource)
operation.Offset(), operation.Length(), operation.OriginalOffset(),
operation.OriginalLength());
dprintf(" DMABuffer %p, %lu vecs, bounce buffer: %p (%p) %s\n", buffer,
buffer->VecCount(), buffer->BounceBuffer(),
(void*)buffer->PhysicalBounceBuffer(),
buffer->VecCount(), buffer->BounceBufferAddress(),
(void*)buffer->PhysicalBounceBufferAddress(),
operation.UsesBounceBuffer() ? "used" : "unused");
for (uint32 i = 0; i < buffer->VecCount(); i++) {
dprintf(" [%lu] base %p, length %lu%s\n", i,
@@ -616,7 +617,7 @@ Test::Run(DMAResource& resource)
void* address;
if (target.uses_bounce_buffer) {
address = (void*)(target.address
+ (addr_t)buffer->PhysicalBounceBuffer());
+ (addr_t)buffer->PhysicalBounceBufferAddress());
} else if (fSuite.IsContiguous() || target.address < B_PAGE_SIZE) {
address = (void*)(target.address + fSuite.PhysicalDataBase());
} else {
@@ -1183,7 +1184,7 @@ dma_test_init_device(void *driverCookie, void **_deviceCookie)
return B_NO_MEMORY;
status_t status = sDMAResource->Init(restrictions, DMA_TEST_BLOCK_SIZE,
DMA_TEST_BUFFER_COUNT);
DMA_TEST_BUFFER_COUNT, DMA_TEST_BOUNCE_BUFFER_COUNT);
if (status != B_OK) {
delete sDMAResource;
return status;