ramfs: Overhaul block allocation to use a VMCache and physical pages.
This is a massive efficiency improvement as well as a large address space usage savings. It also paves the way for file_map() support...
This commit is contained in:
@@ -1,420 +1,359 @@
|
||||
/*
|
||||
* Copyright 2007, Ingo Weinhold, [email protected].
|
||||
* Copyright 2019, Haiku, Inc.
|
||||
* All rights reserved. Distributed under the terms of the MIT license.
|
||||
*/
|
||||
#include "DataContainer.h"
|
||||
|
||||
#include <AutoDeleter.h>
|
||||
#include <util/AutoLock.h>
|
||||
|
||||
#include <vm/VMCache.h>
|
||||
#include <vm/vm_page.h>
|
||||
|
||||
#include "AllocationInfo.h"
|
||||
#include "Attribute.h" // for debugging only
|
||||
#include "Block.h"
|
||||
#include "DataContainer.h"
|
||||
#include "DebugSupport.h"
|
||||
#include "Misc.h"
|
||||
#include "Node.h" // for debugging only
|
||||
#include "Volume.h"
|
||||
|
||||
// constructor
|
||||
DataContainer::DataContainer(Volume *volume)
|
||||
: fVolume(volume),
|
||||
fSize(0)
|
||||
fSize(0),
|
||||
fCache(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
// destructor
|
||||
DataContainer::~DataContainer()
|
||||
{
|
||||
Resize(0);
|
||||
if (fCache != NULL) {
|
||||
fCache->Lock();
|
||||
fCache->ReleaseRefAndUnlock();
|
||||
fCache = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// InitCheck
|
||||
status_t
|
||||
DataContainer::InitCheck() const
|
||||
{
|
||||
return (fVolume ? B_OK : B_ERROR);
|
||||
return (fVolume != NULL ? B_OK : B_ERROR);
|
||||
}
|
||||
|
||||
// Resize
|
||||
status_t
|
||||
DataContainer::Resize(off_t newSize)
|
||||
{
|
||||
// PRINT("DataContainer::Resize(%Ld), fSize: %Ld\n", newSize, fSize);
|
||||
|
||||
status_t error = B_OK;
|
||||
if (newSize < 0)
|
||||
newSize = 0;
|
||||
if (newSize != fSize) {
|
||||
// Shrinking should never fail. Growing can fail, if we run out of
|
||||
// memory. Then we try to shrink back to the original size.
|
||||
off_t oldSize = fSize;
|
||||
error = _Resize(newSize);
|
||||
if (error == B_NO_MEMORY && newSize > fSize)
|
||||
_Resize(oldSize);
|
||||
if (newSize < fSize) {
|
||||
// shrink
|
||||
if (_IsCacheMode()) {
|
||||
// resize the VMCache, which will automatically free pages
|
||||
AutoLocker<VMCache> _(fCache);
|
||||
error = fCache->Resize(newSize, VM_PRIORITY_SYSTEM);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
} else {
|
||||
// small buffer mode: just set the new size (done below)
|
||||
}
|
||||
} else if (newSize > fSize) {
|
||||
// grow
|
||||
if (_RequiresCacheMode(newSize)) {
|
||||
if (!_IsCacheMode())
|
||||
error = _SwitchToCacheMode(fSize);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
AutoLocker<VMCache> _(fCache);
|
||||
fCache->Resize(newSize, VM_PRIORITY_SYSTEM);
|
||||
|
||||
// pages will be added as they are written to; so nothing else
|
||||
// needs to be done here.
|
||||
} else {
|
||||
// no need to switch to cache mode: just set the new size
|
||||
// (done below)
|
||||
}
|
||||
}
|
||||
|
||||
fSize = newSize;
|
||||
|
||||
// PRINT("DataContainer::Resize() done: %lx, fSize: %Ld\n", error, fSize);
|
||||
return error;
|
||||
}
|
||||
|
||||
// ReadAt
|
||||
status_t
|
||||
DataContainer::ReadAt(off_t offset, void *_buffer, size_t size,
|
||||
size_t *bytesRead)
|
||||
size_t *bytesRead)
|
||||
{
|
||||
uint8 *buffer = (uint8*)_buffer;
|
||||
status_t error = (buffer && offset >= 0 && bytesRead ? B_OK : B_BAD_VALUE);
|
||||
if (error == B_OK) {
|
||||
// read not more than we have to offer
|
||||
offset = min(offset, fSize);
|
||||
size = min(size, size_t(fSize - offset));
|
||||
// iterate through the blocks, reading as long as there's something
|
||||
// left to read
|
||||
size_t blockSize = fVolume->GetBlockSize();
|
||||
*bytesRead = 0;
|
||||
while (size > 0) {
|
||||
size_t inBlockOffset = offset % blockSize;
|
||||
size_t toRead = min(size, size_t(blockSize - inBlockOffset));
|
||||
void *blockData = _GetBlockDataAt(offset / blockSize,
|
||||
inBlockOffset, toRead);
|
||||
D(
|
||||
if (!blockData) {
|
||||
Node *node = NULL;
|
||||
if (Attribute *attribute = dynamic_cast<Attribute*>(this)) {
|
||||
FATAL("attribute `%s' of\n", attribute->GetName());
|
||||
node = attribute->GetNode();
|
||||
} else {
|
||||
node = dynamic_cast<Node*>(this);
|
||||
}
|
||||
if (node)
|
||||
// FATAL(("node `%s'\n", node->GetName()));
|
||||
FATAL("container size: %Ld, offset: %Ld, buffer size: %lu\n",
|
||||
fSize, offset, size);
|
||||
return B_ERROR;
|
||||
}
|
||||
);
|
||||
memcpy(buffer, blockData, toRead);
|
||||
buffer += toRead;
|
||||
size -= toRead;
|
||||
offset += toRead;
|
||||
*bytesRead += toRead;
|
||||
}
|
||||
status_t error = (buffer && offset >= 0 &&
|
||||
bytesRead ? B_OK : B_BAD_VALUE);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
// read not more than we have to offer
|
||||
offset = min(offset, fSize);
|
||||
size = min(size, size_t(fSize - offset));
|
||||
|
||||
if (!_IsCacheMode()) {
|
||||
// in non-cache mode, we just copy the data directly
|
||||
memcpy(buffer, fSmallBuffer + offset, size);
|
||||
if (bytesRead != NULL)
|
||||
*bytesRead = size;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
// cache mode
|
||||
error = _DoCacheIO(offset, buffer, size, bytesRead, false);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
// WriteAt
|
||||
status_t
|
||||
DataContainer::WriteAt(off_t offset, const void *_buffer, size_t size,
|
||||
size_t *bytesWritten)
|
||||
size_t *bytesWritten)
|
||||
{
|
||||
//PRINT(("DataContainer::WriteAt(%Ld, %p, %lu, %p), fSize: %Ld\n", offset, _buffer, size, bytesWritten, fSize));
|
||||
PRINT("DataContainer::WriteAt(%Ld, %p, %lu, %p), fSize: %Ld\n", offset, _buffer, size, bytesWritten, fSize);
|
||||
|
||||
const uint8 *buffer = (const uint8*)_buffer;
|
||||
status_t error = (buffer && offset >= 0 && bytesWritten
|
||||
? B_OK : B_BAD_VALUE);
|
||||
// resize the container, if necessary
|
||||
if (error == B_OK) {
|
||||
off_t newSize = offset + size;
|
||||
off_t oldSize = fSize;
|
||||
if (newSize > fSize) {
|
||||
error = Resize(newSize);
|
||||
// pad with zero, if necessary
|
||||
if (error == B_OK && offset > oldSize)
|
||||
_ClearArea(offset, oldSize - offset);
|
||||
}
|
||||
}
|
||||
if (error == B_OK) {
|
||||
// iterate through the blocks, writing as long as there's something
|
||||
// left to write
|
||||
size_t blockSize = fVolume->GetBlockSize();
|
||||
*bytesWritten = 0;
|
||||
while (size > 0) {
|
||||
size_t inBlockOffset = offset % blockSize;
|
||||
size_t toWrite = min(size, size_t(blockSize - inBlockOffset));
|
||||
void *blockData = _GetBlockDataAt(offset / blockSize,
|
||||
inBlockOffset, toWrite);
|
||||
D(if (!blockData) return B_ERROR;);
|
||||
memcpy(blockData, buffer, toWrite);
|
||||
buffer += toWrite;
|
||||
size -= toWrite;
|
||||
offset += toWrite;
|
||||
*bytesWritten += toWrite;
|
||||
}
|
||||
}
|
||||
//PRINT(("DataContainer::WriteAt() done: %lx, fSize: %Ld\n", error, fSize));
|
||||
return error;
|
||||
}
|
||||
? B_OK : B_BAD_VALUE);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
// GetFirstDataBlock
|
||||
void
|
||||
DataContainer::GetFirstDataBlock(const uint8 **data, size_t *length)
|
||||
{
|
||||
if (data && length) {
|
||||
if (_IsBlockMode()) {
|
||||
BlockReference *block = _GetBlockList()->ItemAt(0);
|
||||
*data = (const uint8*)block->GetData();
|
||||
*length = min(fSize, fVolume->GetBlockSize());
|
||||
} else {
|
||||
*data = fSmallBuffer;
|
||||
*length = fSize;
|
||||
}
|
||||
// resize the container, if necessary
|
||||
if ((offset + size) > fSize)
|
||||
error = Resize(offset + size);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
if (!_IsCacheMode()) {
|
||||
// in non-cache mode, we just copy the data directly
|
||||
memcpy(fSmallBuffer + offset, buffer, size);
|
||||
if (bytesWritten != NULL)
|
||||
*bytesWritten = size;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
// cache mode
|
||||
error = _DoCacheIO(offset, (uint8*)buffer, size, bytesWritten, true);
|
||||
|
||||
PRINT("DataContainer::WriteAt() done: %lx, fSize: %Ld\n", error, fSize);
|
||||
return error;
|
||||
}
|
||||
|
||||
// GetAllocationInfo
|
||||
void
|
||||
DataContainer::GetAllocationInfo(AllocationInfo &info)
|
||||
{
|
||||
if (_IsBlockMode()) {
|
||||
BlockList *blocks = _GetBlockList();
|
||||
info.AddListAllocation(blocks->GetCapacity(), sizeof(BlockReference*));
|
||||
int32 blockCount = blocks->CountItems();
|
||||
for (int32 i = 0; i < blockCount; i++)
|
||||
info.AddBlockAllocation(blocks->ItemAt(i)->GetBlock()->GetSize());
|
||||
if (_IsCacheMode()) {
|
||||
info.AddAreaAllocation(fCache->committed_size);
|
||||
} else {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
// _RequiresBlockMode
|
||||
inline
|
||||
bool
|
||||
DataContainer::_RequiresBlockMode(size_t size)
|
||||
// _RequiresCacheMode
|
||||
inline bool
|
||||
DataContainer::_RequiresCacheMode(size_t size)
|
||||
{
|
||||
return (size > kSmallDataContainerSize);
|
||||
// we cannot back out of cache mode after entering it,
|
||||
// as there may be other consumers of our VMCache
|
||||
return _IsCacheMode() || (size > kSmallDataContainerSize);
|
||||
}
|
||||
|
||||
// _IsBlockMode
|
||||
inline
|
||||
bool
|
||||
DataContainer::_IsBlockMode() const
|
||||
// _IsCacheMode
|
||||
inline bool
|
||||
DataContainer::_IsCacheMode() const
|
||||
{
|
||||
return (fSize > kSmallDataContainerSize);
|
||||
}
|
||||
|
||||
// _Resize
|
||||
status_t
|
||||
DataContainer::_Resize(off_t newSize)
|
||||
{
|
||||
//PRINT(("DataContainer::_Resize(%Ld), fSize: %Ld\n", newSize, fSize));
|
||||
status_t error = B_OK;
|
||||
if (newSize != fSize) {
|
||||
size_t blockSize = fVolume->GetBlockSize();
|
||||
int32 blockCount = _CountBlocks();
|
||||
int32 newBlockCount = (newSize + blockSize - 1) / blockSize;
|
||||
if (newBlockCount == blockCount) {
|
||||
// only the last block needs to be resized
|
||||
if (_IsBlockMode() && _RequiresBlockMode(newSize)) {
|
||||
// keep block mode
|
||||
error = _ResizeLastBlock((newSize - 1) % blockSize + 1);
|
||||
} else if (!_IsBlockMode() && !_RequiresBlockMode(newSize)) {
|
||||
// keep small buffer mode
|
||||
fSize = newSize;
|
||||
} else if (fSize < newSize) {
|
||||
// switch to block mode
|
||||
_SwitchToBlockMode(newSize);
|
||||
} else {
|
||||
// switch to small buffer mode
|
||||
_SwitchToSmallBufferMode(newSize);
|
||||
}
|
||||
} else if (newBlockCount < blockCount) {
|
||||
// shrink
|
||||
if (_IsBlockMode()) {
|
||||
// remove the last blocks
|
||||
BlockList *blocks = _GetBlockList();
|
||||
for (int32 i = blockCount - 1; i >= newBlockCount; i--) {
|
||||
BlockReference *block = blocks->ItemAt(i);
|
||||
blocks->RemoveItem(i);
|
||||
fVolume->FreeBlock(block);
|
||||
fSize = (fSize - 1) / blockSize * blockSize;
|
||||
}
|
||||
// resize the last block to the correct size, respectively
|
||||
// switch to small buffer mode
|
||||
if (_RequiresBlockMode(newSize))
|
||||
error = _ResizeLastBlock((newSize - 1) % blockSize + 1);
|
||||
else
|
||||
_SwitchToSmallBufferMode(newSize);
|
||||
} else {
|
||||
// small buffer mode: just set the new size
|
||||
fSize = newSize;
|
||||
}
|
||||
} else {
|
||||
// grow
|
||||
if (_RequiresBlockMode(newSize)) {
|
||||
// resize the first block to the correct size, respectively
|
||||
// switch to block mode
|
||||
if (_IsBlockMode())
|
||||
error = _ResizeLastBlock(blockSize);
|
||||
else {
|
||||
error = _SwitchToBlockMode(min((size_t)newSize,
|
||||
blockSize));
|
||||
}
|
||||
// add new blocks
|
||||
BlockList *blocks = _GetBlockList();
|
||||
while (error == B_OK && fSize < newSize) {
|
||||
size_t newBlockSize = min(size_t(newSize - fSize),
|
||||
blockSize);
|
||||
BlockReference *block = NULL;
|
||||
error = fVolume->AllocateBlock(newBlockSize, &block);
|
||||
if (error == B_OK) {
|
||||
if (blocks->AddItem(block))
|
||||
fSize += newBlockSize;
|
||||
else {
|
||||
SET_ERROR(error, B_NO_MEMORY);
|
||||
fVolume->FreeBlock(block);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// no need to switch to block mode: just set the new size
|
||||
fSize = newSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
//PRINT(("DataContainer::_Resize() done: %lx, fSize: %Ld\n", error, fSize));
|
||||
return error;
|
||||
}
|
||||
|
||||
// _GetBlockList
|
||||
inline
|
||||
DataContainer::BlockList *
|
||||
DataContainer::_GetBlockList()
|
||||
{
|
||||
return (BlockList*)fBlocks;
|
||||
}
|
||||
|
||||
// _GetBlockList
|
||||
inline
|
||||
const DataContainer::BlockList *
|
||||
DataContainer::_GetBlockList() const
|
||||
{
|
||||
return (BlockList*)fBlocks;
|
||||
return fCache != NULL;
|
||||
}
|
||||
|
||||
// _CountBlocks
|
||||
inline
|
||||
int32
|
||||
inline int32
|
||||
DataContainer::_CountBlocks() const
|
||||
{
|
||||
if (_IsBlockMode())
|
||||
return _GetBlockList()->CountItems();
|
||||
if (_IsCacheMode())
|
||||
return fCache->page_count;
|
||||
else if (fSize == 0) // small buffer mode, empty buffer
|
||||
return 0;
|
||||
return 1; // small buffer mode, non-empty buffer
|
||||
}
|
||||
|
||||
// _GetBlockDataAt
|
||||
inline
|
||||
void *
|
||||
DataContainer::_GetBlockDataAt(int32 index, size_t offset, size_t DARG(size))
|
||||
{
|
||||
if (_IsBlockMode()) {
|
||||
BlockReference *block = _GetBlockList()->ItemAt(index);
|
||||
D(if (!fVolume->CheckBlock(block, offset + size)) return NULL;);
|
||||
return block->GetDataAt(offset);
|
||||
} else {
|
||||
D(
|
||||
if (offset + size > kSmallDataContainerSize) {
|
||||
FATAL("DataContainer: Data access exceeds small buffer.\n");
|
||||
PANIC("DataContainer: Data access exceeds small buffer.");
|
||||
return NULL;
|
||||
}
|
||||
);
|
||||
return fSmallBuffer + offset;
|
||||
}
|
||||
}
|
||||
|
||||
// _ClearArea
|
||||
void
|
||||
DataContainer::_ClearArea(off_t offset, off_t size)
|
||||
{
|
||||
// constrain the area to the data area
|
||||
offset = min(offset, fSize);
|
||||
size = min(size, fSize - offset);
|
||||
// iterate through the blocks, clearing as long as there's something
|
||||
// left to clear
|
||||
size_t blockSize = fVolume->GetBlockSize();
|
||||
while (size > 0) {
|
||||
size_t inBlockOffset = offset % blockSize;
|
||||
size_t toClear = min(size_t(size), blockSize - inBlockOffset);
|
||||
void *blockData = _GetBlockDataAt(offset / blockSize, inBlockOffset,
|
||||
toClear);
|
||||
D(if (!blockData) return;);
|
||||
memset(blockData, 0, toClear);
|
||||
size -= toClear;
|
||||
offset += toClear;
|
||||
}
|
||||
}
|
||||
|
||||
// _ResizeLastBlock
|
||||
// _SwitchToCacheMode
|
||||
status_t
|
||||
DataContainer::_ResizeLastBlock(size_t newSize)
|
||||
DataContainer::_SwitchToCacheMode(size_t newBlockSize)
|
||||
{
|
||||
//PRINT(("DataContainer::_ResizeLastBlock(%lu), fSize: %Ld\n", newSize, fSize));
|
||||
int32 blockCount = _CountBlocks();
|
||||
status_t error = (fSize > 0 && blockCount > 0 && newSize > 0
|
||||
? B_OK : B_BAD_VALUE);
|
||||
D(
|
||||
if (!_IsBlockMode()) {
|
||||
FATAL("Call of _ResizeLastBlock() in small buffer mode.\n");
|
||||
PANIC("Call of _ResizeLastBlock() in small buffer mode.");
|
||||
return B_ERROR;
|
||||
}
|
||||
);
|
||||
if (error == B_OK) {
|
||||
size_t blockSize = fVolume->GetBlockSize();
|
||||
size_t oldSize = (fSize - 1) % blockSize + 1;
|
||||
if (newSize != oldSize) {
|
||||
BlockList *blocks = _GetBlockList();
|
||||
BlockReference *block = blocks->ItemAt(blockCount - 1);
|
||||
BlockReference *newBlock = fVolume->ResizeBlock(block, newSize);
|
||||
if (newBlock) {
|
||||
if (newBlock != block)
|
||||
blocks->ReplaceItem(blockCount - 1, newBlock);
|
||||
fSize += off_t(newSize) - oldSize;
|
||||
} else
|
||||
SET_ERROR(error, B_NO_MEMORY);
|
||||
}
|
||||
}
|
||||
//PRINT(("DataContainer::_ResizeLastBlock() done: %lx, fSize: %Ld\n", error, fSize));
|
||||
status_t error = VMCacheFactory::CreateAnonymousCache(fCache, false, 0,
|
||||
0, false, VM_PRIORITY_SYSTEM);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
fCache->temporary = 1;
|
||||
fCache->virtual_end = newBlockSize;
|
||||
|
||||
error = fCache->Commit(newBlockSize, VM_PRIORITY_SYSTEM);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
if (fSize != 0)
|
||||
error = _DoCacheIO(0, fSmallBuffer, fSize, NULL, true);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
// _SwitchToBlockMode
|
||||
// _DoCacheIO
|
||||
status_t
|
||||
DataContainer::_SwitchToBlockMode(size_t newBlockSize)
|
||||
DataContainer::_DoCacheIO(const off_t offset, uint8* buffer, ssize_t length,
|
||||
size_t* bytesProcessed, bool isWrite)
|
||||
{
|
||||
// allocate a new block
|
||||
BlockReference *block = NULL;
|
||||
status_t error = fVolume->AllocateBlock(newBlockSize, &block);
|
||||
if (error == B_OK) {
|
||||
// copy the data from the small buffer into the block
|
||||
if (fSize > 0)
|
||||
memcpy(block->GetData(), fSmallBuffer, fSize);
|
||||
// construct the block list and add the block
|
||||
new (fBlocks) BlockList(10);
|
||||
BlockList *blocks = _GetBlockList();
|
||||
if (blocks->AddItem(block)) {
|
||||
fSize = newBlockSize;
|
||||
const size_t originalLength = length;
|
||||
const bool user = IS_USER_ADDRESS(buffer);
|
||||
|
||||
const off_t rounded_offset = ROUNDDOWN(offset, B_PAGE_SIZE);
|
||||
const size_t rounded_len = ROUNDUP((length) + (offset - rounded_offset),
|
||||
B_PAGE_SIZE);
|
||||
vm_page** pages = new(std::nothrow) vm_page*[rounded_len / B_PAGE_SIZE];
|
||||
if (pages == NULL)
|
||||
return B_NO_MEMORY;
|
||||
ArrayDeleter<vm_page*> pagesDeleter(pages);
|
||||
|
||||
_GetPages(rounded_offset, rounded_len, isWrite, pages);
|
||||
|
||||
status_t error = B_OK;
|
||||
size_t index = 0;
|
||||
|
||||
while (length > 0) {
|
||||
vm_page* page = pages[index];
|
||||
phys_addr_t at = (page->physical_page_number * B_PAGE_SIZE);
|
||||
ssize_t bytes = B_PAGE_SIZE;
|
||||
if (index == 0) {
|
||||
const uint32 pageoffset = (offset % B_PAGE_SIZE);
|
||||
at += pageoffset;
|
||||
bytes -= pageoffset;
|
||||
}
|
||||
bytes = min(length, bytes);
|
||||
|
||||
if (isWrite) {
|
||||
page->modified = true;
|
||||
error = vm_memcpy_to_physical(at, buffer, bytes, user);
|
||||
} else {
|
||||
// error: destroy the block list and free the block
|
||||
SET_ERROR(error, B_NO_MEMORY);
|
||||
blocks->~BlockList();
|
||||
if (fSize > 0)
|
||||
memcpy(fSmallBuffer, block->GetData(), fSize);
|
||||
fVolume->FreeBlock(block);
|
||||
if (page != NULL) {
|
||||
error = vm_memcpy_from_physical(buffer, at, bytes, user);
|
||||
} else {
|
||||
if (user) {
|
||||
error = user_memset(buffer, 0, bytes);
|
||||
} else {
|
||||
memset(buffer, 0, bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (error != B_OK)
|
||||
break;
|
||||
|
||||
buffer += bytes;
|
||||
length -= bytes;
|
||||
index++;
|
||||
}
|
||||
|
||||
_PutPages(rounded_offset, rounded_len, pages, error == B_OK);
|
||||
|
||||
if (bytesProcessed != NULL)
|
||||
*bytesProcessed = length > 0 ? originalLength - length : originalLength;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
// _SwitchToSmallBufferMode
|
||||
// _GetPages
|
||||
void
|
||||
DataContainer::_SwitchToSmallBufferMode(size_t newSize)
|
||||
DataContainer::_GetPages(off_t offset, off_t length, bool isWrite,
|
||||
vm_page** pages)
|
||||
{
|
||||
// remove the first (and only) block
|
||||
BlockList *blocks = _GetBlockList();
|
||||
BlockReference *block = blocks->ItemAt(0);
|
||||
blocks->RemoveItem((int32)0L);
|
||||
// destroy the block list and copy the data into the small buffer
|
||||
blocks->~BlockList();
|
||||
if (newSize > 0)
|
||||
memcpy(fSmallBuffer, block->GetData(), newSize);
|
||||
// free the block and set the new size
|
||||
fVolume->FreeBlock(block);
|
||||
fSize = newSize;
|
||||
// TODO: This method is duplicated in the ram_disk. Perhaps it
|
||||
// should be put into a common location?
|
||||
|
||||
// get the pages, we already have
|
||||
AutoLocker<VMCache> locker(fCache);
|
||||
|
||||
size_t pageCount = length / B_PAGE_SIZE;
|
||||
size_t index = 0;
|
||||
size_t missingPages = 0;
|
||||
|
||||
while (length > 0) {
|
||||
vm_page* page = fCache->LookupPage(offset);
|
||||
if (page != NULL) {
|
||||
if (page->busy) {
|
||||
fCache->WaitForPageEvents(page, PAGE_EVENT_NOT_BUSY, true);
|
||||
continue;
|
||||
}
|
||||
|
||||
DEBUG_PAGE_ACCESS_START(page);
|
||||
page->busy = true;
|
||||
} else
|
||||
missingPages++;
|
||||
|
||||
pages[index++] = page;
|
||||
offset += B_PAGE_SIZE;
|
||||
length -= B_PAGE_SIZE;
|
||||
}
|
||||
|
||||
locker.Unlock();
|
||||
|
||||
// For a write we need to reserve the missing pages.
|
||||
if (isWrite && missingPages > 0) {
|
||||
vm_page_reservation reservation;
|
||||
vm_page_reserve_pages(&reservation, missingPages,
|
||||
VM_PRIORITY_SYSTEM);
|
||||
|
||||
for (size_t i = 0; i < pageCount; i++) {
|
||||
if (pages[i] != NULL)
|
||||
continue;
|
||||
|
||||
pages[i] = vm_page_allocate_page(&reservation,
|
||||
PAGE_STATE_WIRED | VM_PAGE_ALLOC_BUSY);
|
||||
|
||||
if (--missingPages == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
vm_page_unreserve_pages(&reservation);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DataContainer::_PutPages(off_t offset, off_t length, vm_page** pages,
|
||||
bool success)
|
||||
{
|
||||
// TODO: This method is duplicated in the ram_disk. Perhaps it
|
||||
// should be put into a common location?
|
||||
|
||||
AutoLocker<VMCache> locker(fCache);
|
||||
|
||||
// Mark all pages unbusy. On error free the newly allocated pages.
|
||||
size_t index = 0;
|
||||
|
||||
while (length > 0) {
|
||||
vm_page* page = pages[index++];
|
||||
if (page != NULL) {
|
||||
if (page->CacheRef() == NULL) {
|
||||
if (success) {
|
||||
fCache->InsertPage(page, offset);
|
||||
fCache->MarkPageUnbusy(page);
|
||||
DEBUG_PAGE_ACCESS_END(page);
|
||||
} else
|
||||
vm_page_free(NULL, page);
|
||||
} else {
|
||||
fCache->MarkPageUnbusy(page);
|
||||
DEBUG_PAGE_ACCESS_END(page);
|
||||
}
|
||||
}
|
||||
|
||||
offset += B_PAGE_SIZE;
|
||||
length -= B_PAGE_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
/*
|
||||
* Copyright 2007, Ingo Weinhold, [email protected].
|
||||
* Copyright 2019, Haiku, Inc.
|
||||
* All rights reserved. Distributed under the terms of the MIT license.
|
||||
*/
|
||||
#ifndef DATA_CONTAINER_H
|
||||
#define DATA_CONTAINER_H
|
||||
|
||||
#include "List.h"
|
||||
#include <OS.h>
|
||||
|
||||
struct vm_page;
|
||||
class VMCache;
|
||||
class AllocationInfo;
|
||||
class BlockReference;
|
||||
class Volume;
|
||||
|
||||
// Size of the DataContainer's small buffer. If it contains data up to this
|
||||
@@ -49,38 +51,25 @@ public:
|
||||
virtual status_t WriteAt(off_t offset, const void *buffer, size_t size,
|
||||
size_t *bytesWritten);
|
||||
|
||||
void GetFirstDataBlock(const uint8 **data, size_t *length);
|
||||
|
||||
// debugging
|
||||
void GetAllocationInfo(AllocationInfo &info);
|
||||
|
||||
private:
|
||||
typedef List<BlockReference*> BlockList;
|
||||
inline bool _RequiresCacheMode(size_t size);
|
||||
inline bool _IsCacheMode() const;
|
||||
status_t _SwitchToCacheMode(size_t newBlockSize);
|
||||
void _GetPages(off_t offset, off_t length, bool isWrite, vm_page** pages);
|
||||
void _PutPages(off_t offset, off_t length, vm_page** pages, bool success);
|
||||
status_t _DoCacheIO(const off_t offset, uint8* buffer, ssize_t length,
|
||||
size_t* bytesProcessed, bool isWrite);
|
||||
|
||||
private:
|
||||
static inline bool _RequiresBlockMode(size_t size);
|
||||
inline bool _IsBlockMode() const;
|
||||
|
||||
inline BlockList *_GetBlockList();
|
||||
inline const BlockList *_GetBlockList() const;
|
||||
inline int32 _CountBlocks() const;
|
||||
inline void *_GetBlockDataAt(int32 index, size_t offset, size_t size);
|
||||
|
||||
void _ClearArea(off_t offset, off_t size);
|
||||
|
||||
status_t _Resize(off_t newSize);
|
||||
status_t _ResizeLastBlock(size_t newSize);
|
||||
|
||||
status_t _SwitchToBlockMode(size_t newBlockSize);
|
||||
void _SwitchToSmallBufferMode(size_t newSize);
|
||||
|
||||
private:
|
||||
Volume *fVolume;
|
||||
off_t fSize;
|
||||
union {
|
||||
uint8 fBlocks[sizeof(BlockList)];
|
||||
uint8 fSmallBuffer[kSmallDataContainerSize];
|
||||
};
|
||||
Volume *fVolume;
|
||||
off_t fSize;
|
||||
VMCache* fCache;
|
||||
uint8 fSmallBuffer[kSmallDataContainerSize];
|
||||
};
|
||||
|
||||
#endif // DATA_CONTAINER_H
|
||||
|
||||
@@ -12,10 +12,6 @@ KernelAddon ramfs
|
||||
AttributeIndex.cpp
|
||||
AttributeIndexImpl.cpp
|
||||
AttributeIterator.cpp
|
||||
BlockAllocator.cpp
|
||||
BlockAllocatorArea.cpp
|
||||
BlockAllocatorAreaBucket.cpp
|
||||
BlockReferenceManager.cpp
|
||||
DataContainer.cpp
|
||||
DebugSupport.cpp
|
||||
Directory.cpp
|
||||
|
||||
@@ -26,8 +26,8 @@
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "Block.h"
|
||||
#include "BlockAllocator.h"
|
||||
#include <vm/vm_page.h>
|
||||
|
||||
#include "DebugSupport.h"
|
||||
#include "Directory.h"
|
||||
#include "DirectoryEntryTable.h"
|
||||
@@ -43,11 +43,6 @@
|
||||
#include "TwoKeyAVLTree.h"
|
||||
#include "Volume.h"
|
||||
|
||||
// default block size
|
||||
static const off_t kDefaultBlockSize = 4096;
|
||||
|
||||
static const size_t kDefaultAreaSize = kDefaultBlockSize * 128;
|
||||
|
||||
// default volume name
|
||||
static const char *kDefaultVolumeName = "RAM FS";
|
||||
|
||||
@@ -143,9 +138,6 @@ Volume::Volume(fs_volume* volume)
|
||||
fAnyNodeListeners(),
|
||||
fEntryListeners(NULL),
|
||||
fAnyEntryListeners(),
|
||||
fBlockAllocator(NULL),
|
||||
fBlockSize(kDefaultBlockSize),
|
||||
fAllocatedBlocks(0),
|
||||
fAccessTime(0),
|
||||
fMounted(false)
|
||||
{
|
||||
@@ -173,14 +165,6 @@ Volume::Mount(uint32 flags)
|
||||
Unmount();
|
||||
|
||||
status_t error = B_OK;
|
||||
// create a block allocator
|
||||
if (error == B_OK) {
|
||||
fBlockAllocator = new(nothrow) BlockAllocator(kDefaultAreaSize);
|
||||
if (fBlockAllocator)
|
||||
error = fBlockAllocator->InitCheck();
|
||||
else
|
||||
SET_ERROR(error, B_NO_MEMORY);
|
||||
}
|
||||
// create the listener trees
|
||||
if (error == B_OK) {
|
||||
fNodeListeners = new(nothrow) NodeListenerTree;
|
||||
@@ -267,41 +251,22 @@ Volume::Unmount()
|
||||
delete fNodeTable;
|
||||
fNodeTable = NULL;
|
||||
}
|
||||
// delete the block allocator
|
||||
if (fBlockAllocator) {
|
||||
delete fBlockAllocator;
|
||||
fBlockAllocator = NULL;
|
||||
}
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
// GetBlockSize
|
||||
off_t
|
||||
Volume::GetBlockSize() const
|
||||
{
|
||||
return fBlockSize;
|
||||
}
|
||||
|
||||
// CountBlocks
|
||||
off_t
|
||||
Volume::CountBlocks() const
|
||||
{
|
||||
size_t bytes = 0;
|
||||
system_info sysInfo;
|
||||
if (get_system_info(&sysInfo) == B_OK) {
|
||||
int32 freePages = sysInfo.max_pages - sysInfo.used_pages;
|
||||
bytes = (uint32)freePages * B_PAGE_SIZE
|
||||
+ fBlockAllocator->GetAvailableBytes();
|
||||
}
|
||||
return bytes / kDefaultBlockSize;
|
||||
// TODO: Compute how many pages we are using across all DataContainers.
|
||||
return 0;
|
||||
}
|
||||
|
||||
// CountFreeBlocks
|
||||
off_t
|
||||
Volume::CountFreeBlocks() const
|
||||
{
|
||||
// TODO:...
|
||||
return CountBlocks() - fBlockAllocator->GetUsedBytes() / kDefaultBlockSize;
|
||||
return vm_page_num_available_pages();
|
||||
}
|
||||
|
||||
// SetName
|
||||
@@ -752,54 +717,6 @@ Volume::UpdateLiveQueries(Entry *entry, Node* node, const char *attribute,
|
||||
}
|
||||
}
|
||||
|
||||
// AllocateBlock
|
||||
status_t
|
||||
Volume::AllocateBlock(size_t size, BlockReference **block)
|
||||
{
|
||||
status_t error = (size > 0 && size <= fBlockSize && block
|
||||
? B_OK : B_BAD_VALUE);
|
||||
if (error == B_OK) {
|
||||
*block = fBlockAllocator->AllocateBlock(size);
|
||||
if (*block)
|
||||
fAllocatedBlocks++;
|
||||
else
|
||||
SET_ERROR(error, B_NO_MEMORY);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// FreeBlock
|
||||
void
|
||||
Volume::FreeBlock(BlockReference *block)
|
||||
{
|
||||
if (block) {
|
||||
fBlockAllocator->FreeBlock(block);
|
||||
fAllocatedBlocks--;
|
||||
}
|
||||
}
|
||||
|
||||
// ResizeBlock
|
||||
BlockReference *
|
||||
Volume::ResizeBlock(BlockReference *block, size_t size)
|
||||
{
|
||||
BlockReference *newBlock = NULL;
|
||||
if (size <= fBlockSize && block) {
|
||||
if (size == 0) {
|
||||
fBlockAllocator->FreeBlock(block);
|
||||
fAllocatedBlocks--;
|
||||
} else
|
||||
newBlock = fBlockAllocator->ResizeBlock(block, size);
|
||||
}
|
||||
return newBlock;
|
||||
}
|
||||
|
||||
// CheckBlock
|
||||
bool
|
||||
Volume::CheckBlock(BlockReference *block, size_t size)
|
||||
{
|
||||
return fBlockAllocator->CheckBlock(block, size);
|
||||
}
|
||||
|
||||
// GetAllocationInfo
|
||||
void
|
||||
Volume::GetAllocationInfo(AllocationInfo &info)
|
||||
@@ -813,9 +730,6 @@ Volume::GetAllocationInfo(AllocationInfo &info)
|
||||
fRootDirectory->GetAllocationInfo(info);
|
||||
// name
|
||||
info.AddStringAllocation(fName.GetLength());
|
||||
// block allocator
|
||||
info.AddOtherAllocation(sizeof(BlockAllocator));
|
||||
fBlockAllocator->GetAllocationInfo(info);
|
||||
}
|
||||
|
||||
// ReadLock
|
||||
|
||||
@@ -34,9 +34,8 @@
|
||||
#include "String.h"
|
||||
|
||||
class AllocationInfo;
|
||||
class Block;
|
||||
class BlockAllocator;
|
||||
class BlockReference;
|
||||
class Attribute;
|
||||
class AttributeIndex;
|
||||
class Directory;
|
||||
class DirectoryEntryTable;
|
||||
class Entry;
|
||||
@@ -101,7 +100,6 @@ public:
|
||||
dev_t GetID() const { return fVolume != NULL ? fVolume->id : -1; }
|
||||
fs_volume* FSVolume() const { return fVolume; }
|
||||
|
||||
off_t GetBlockSize() const;
|
||||
off_t CountBlocks() const;
|
||||
off_t CountFreeBlocks() const;
|
||||
|
||||
@@ -156,11 +154,6 @@ public:
|
||||
|
||||
ino_t NextNodeID() { return fNextNodeID++; }
|
||||
|
||||
status_t AllocateBlock(size_t size, BlockReference **block);
|
||||
void FreeBlock(BlockReference *block);
|
||||
BlockReference *ResizeBlock(BlockReference *block, size_t size);
|
||||
// debugging only
|
||||
bool CheckBlock(BlockReference *block, size_t size = 0);
|
||||
void GetAllocationInfo(AllocationInfo &info);
|
||||
|
||||
bigtime_t GetAccessTime() const { return fAccessTime; }
|
||||
@@ -194,9 +187,6 @@ private:
|
||||
EntryListenerTree *fEntryListeners;
|
||||
EntryListenerList fAnyEntryListeners;
|
||||
QueryList fQueries;
|
||||
BlockAllocator *fBlockAllocator;
|
||||
off_t fBlockSize;
|
||||
off_t fAllocatedBlocks;
|
||||
bigtime_t fAccessTime;
|
||||
bool fMounted;
|
||||
};
|
||||
|
||||
@@ -137,7 +137,7 @@ ramfs_read_fs_info(fs_volume* _volume, struct fs_info *info)
|
||||
if (VolumeReadLocker locker = volume) {
|
||||
info->flags = B_FS_IS_PERSISTENT | B_FS_HAS_ATTR | B_FS_HAS_MIME
|
||||
| B_FS_HAS_QUERY;
|
||||
info->block_size = volume->GetBlockSize();
|
||||
info->block_size = B_PAGE_SIZE;
|
||||
info->io_size = kOptimalIOSize;
|
||||
info->total_blocks = volume->CountBlocks();
|
||||
info->free_blocks = volume->CountFreeBlocks();
|
||||
|
||||
Reference in New Issue
Block a user