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:
Augustin Cavalier
2019-08-31 20:38:18 -04:00
parent c8f0cc1afa
commit cbc0726819
6 changed files with 287 additions and 459 deletions
@@ -1,420 +1,359 @@
/* /*
* Copyright 2007, Ingo Weinhold, [email protected]. * Copyright 2007, Ingo Weinhold, [email protected].
* Copyright 2019, Haiku, Inc.
* All rights reserved. Distributed under the terms of the MIT license. * 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 "AllocationInfo.h"
#include "Attribute.h" // for debugging only
#include "Block.h"
#include "DataContainer.h"
#include "DebugSupport.h" #include "DebugSupport.h"
#include "Misc.h" #include "Misc.h"
#include "Node.h" // for debugging only
#include "Volume.h" #include "Volume.h"
// constructor // constructor
DataContainer::DataContainer(Volume *volume) DataContainer::DataContainer(Volume *volume)
: fVolume(volume), : fVolume(volume),
fSize(0) fSize(0),
fCache(NULL)
{ {
} }
// destructor // destructor
DataContainer::~DataContainer() DataContainer::~DataContainer()
{ {
Resize(0); if (fCache != NULL) {
fCache->Lock();
fCache->ReleaseRefAndUnlock();
fCache = NULL;
}
} }
// InitCheck // InitCheck
status_t status_t
DataContainer::InitCheck() const DataContainer::InitCheck() const
{ {
return (fVolume ? B_OK : B_ERROR); return (fVolume != NULL ? B_OK : B_ERROR);
} }
// Resize // Resize
status_t status_t
DataContainer::Resize(off_t newSize) DataContainer::Resize(off_t newSize)
{ {
// PRINT("DataContainer::Resize(%Ld), fSize: %Ld\n", newSize, fSize);
status_t error = B_OK; status_t error = B_OK;
if (newSize < 0) if (newSize < fSize) {
newSize = 0; // shrink
if (newSize != fSize) { if (_IsCacheMode()) {
// Shrinking should never fail. Growing can fail, if we run out of // resize the VMCache, which will automatically free pages
// memory. Then we try to shrink back to the original size. AutoLocker<VMCache> _(fCache);
off_t oldSize = fSize; error = fCache->Resize(newSize, VM_PRIORITY_SYSTEM);
error = _Resize(newSize); if (error != B_OK)
if (error == B_NO_MEMORY && newSize > fSize) return error;
_Resize(oldSize); } 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; return error;
} }
// ReadAt // ReadAt
status_t status_t
DataContainer::ReadAt(off_t offset, void *_buffer, size_t size, DataContainer::ReadAt(off_t offset, void *_buffer, size_t size,
size_t *bytesRead) size_t *bytesRead)
{ {
uint8 *buffer = (uint8*)_buffer; uint8 *buffer = (uint8*)_buffer;
status_t error = (buffer && offset >= 0 && bytesRead ? B_OK : B_BAD_VALUE); status_t error = (buffer && offset >= 0 &&
if (error == B_OK) { bytesRead ? B_OK : B_BAD_VALUE);
// read not more than we have to offer if (error != B_OK)
offset = min(offset, fSize); return error;
size = min(size, size_t(fSize - offset));
// iterate through the blocks, reading as long as there's something // read not more than we have to offer
// left to read offset = min(offset, fSize);
size_t blockSize = fVolume->GetBlockSize(); size = min(size, size_t(fSize - offset));
*bytesRead = 0;
while (size > 0) { if (!_IsCacheMode()) {
size_t inBlockOffset = offset % blockSize; // in non-cache mode, we just copy the data directly
size_t toRead = min(size, size_t(blockSize - inBlockOffset)); memcpy(buffer, fSmallBuffer + offset, size);
void *blockData = _GetBlockDataAt(offset / blockSize, if (bytesRead != NULL)
inBlockOffset, toRead); *bytesRead = size;
D( return B_OK;
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;
}
} }
// cache mode
error = _DoCacheIO(offset, buffer, size, bytesRead, false);
return error; return error;
} }
// WriteAt // WriteAt
status_t status_t
DataContainer::WriteAt(off_t offset, const void *_buffer, size_t size, 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; const uint8 *buffer = (const uint8*)_buffer;
status_t error = (buffer && offset >= 0 && bytesWritten status_t error = (buffer && offset >= 0 && bytesWritten
? B_OK : B_BAD_VALUE); ? B_OK : B_BAD_VALUE);
// resize the container, if necessary if (error != B_OK)
if (error == B_OK) { return error;
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;
}
// GetFirstDataBlock // resize the container, if necessary
void if ((offset + size) > fSize)
DataContainer::GetFirstDataBlock(const uint8 **data, size_t *length) error = Resize(offset + size);
{ if (error != B_OK)
if (data && length) { return error;
if (_IsBlockMode()) {
BlockReference *block = _GetBlockList()->ItemAt(0); if (!_IsCacheMode()) {
*data = (const uint8*)block->GetData(); // in non-cache mode, we just copy the data directly
*length = min(fSize, fVolume->GetBlockSize()); memcpy(fSmallBuffer + offset, buffer, size);
} else { if (bytesWritten != NULL)
*data = fSmallBuffer; *bytesWritten = size;
*length = fSize; 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 // GetAllocationInfo
void void
DataContainer::GetAllocationInfo(AllocationInfo &info) DataContainer::GetAllocationInfo(AllocationInfo &info)
{ {
if (_IsBlockMode()) { if (_IsCacheMode()) {
BlockList *blocks = _GetBlockList(); info.AddAreaAllocation(fCache->committed_size);
info.AddListAllocation(blocks->GetCapacity(), sizeof(BlockReference*));
int32 blockCount = blocks->CountItems();
for (int32 i = 0; i < blockCount; i++)
info.AddBlockAllocation(blocks->ItemAt(i)->GetBlock()->GetSize());
} else { } else {
// ... // ...
} }
} }
// _RequiresBlockMode // _RequiresCacheMode
inline inline bool
bool DataContainer::_RequiresCacheMode(size_t size)
DataContainer::_RequiresBlockMode(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 // _IsCacheMode
inline inline bool
bool DataContainer::_IsCacheMode() const
DataContainer::_IsBlockMode() const
{ {
return (fSize > kSmallDataContainerSize); return fCache != NULL;
}
// _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;
} }
// _CountBlocks // _CountBlocks
inline inline int32
int32
DataContainer::_CountBlocks() const DataContainer::_CountBlocks() const
{ {
if (_IsBlockMode()) if (_IsCacheMode())
return _GetBlockList()->CountItems(); return fCache->page_count;
else if (fSize == 0) // small buffer mode, empty buffer else if (fSize == 0) // small buffer mode, empty buffer
return 0; return 0;
return 1; // small buffer mode, non-empty buffer return 1; // small buffer mode, non-empty buffer
} }
// _GetBlockDataAt // _SwitchToCacheMode
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
status_t status_t
DataContainer::_ResizeLastBlock(size_t newSize) DataContainer::_SwitchToCacheMode(size_t newBlockSize)
{ {
//PRINT(("DataContainer::_ResizeLastBlock(%lu), fSize: %Ld\n", newSize, fSize)); status_t error = VMCacheFactory::CreateAnonymousCache(fCache, false, 0,
int32 blockCount = _CountBlocks(); 0, false, VM_PRIORITY_SYSTEM);
status_t error = (fSize > 0 && blockCount > 0 && newSize > 0 if (error != B_OK)
? B_OK : B_BAD_VALUE); return error;
D(
if (!_IsBlockMode()) { fCache->temporary = 1;
FATAL("Call of _ResizeLastBlock() in small buffer mode.\n"); fCache->virtual_end = newBlockSize;
PANIC("Call of _ResizeLastBlock() in small buffer mode.");
return B_ERROR; error = fCache->Commit(newBlockSize, VM_PRIORITY_SYSTEM);
} if (error != B_OK)
); return error;
if (error == B_OK) {
size_t blockSize = fVolume->GetBlockSize(); if (fSize != 0)
size_t oldSize = (fSize - 1) % blockSize + 1; error = _DoCacheIO(0, fSmallBuffer, fSize, NULL, true);
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));
return error; return error;
} }
// _SwitchToBlockMode // _DoCacheIO
status_t 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 const size_t originalLength = length;
BlockReference *block = NULL; const bool user = IS_USER_ADDRESS(buffer);
status_t error = fVolume->AllocateBlock(newBlockSize, &block);
if (error == B_OK) { const off_t rounded_offset = ROUNDDOWN(offset, B_PAGE_SIZE);
// copy the data from the small buffer into the block const size_t rounded_len = ROUNDUP((length) + (offset - rounded_offset),
if (fSize > 0) B_PAGE_SIZE);
memcpy(block->GetData(), fSmallBuffer, fSize); vm_page** pages = new(std::nothrow) vm_page*[rounded_len / B_PAGE_SIZE];
// construct the block list and add the block if (pages == NULL)
new (fBlocks) BlockList(10); return B_NO_MEMORY;
BlockList *blocks = _GetBlockList(); ArrayDeleter<vm_page*> pagesDeleter(pages);
if (blocks->AddItem(block)) {
fSize = newBlockSize; _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 { } else {
// error: destroy the block list and free the block if (page != NULL) {
SET_ERROR(error, B_NO_MEMORY); error = vm_memcpy_from_physical(buffer, at, bytes, user);
blocks->~BlockList(); } else {
if (fSize > 0) if (user) {
memcpy(fSmallBuffer, block->GetData(), fSize); error = user_memset(buffer, 0, bytes);
fVolume->FreeBlock(block); } 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; return error;
} }
// _SwitchToSmallBufferMode // _GetPages
void 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 // TODO: This method is duplicated in the ram_disk. Perhaps it
BlockList *blocks = _GetBlockList(); // should be put into a common location?
BlockReference *block = blocks->ItemAt(0);
blocks->RemoveItem((int32)0L); // get the pages, we already have
// destroy the block list and copy the data into the small buffer AutoLocker<VMCache> locker(fCache);
blocks->~BlockList();
if (newSize > 0) size_t pageCount = length / B_PAGE_SIZE;
memcpy(fSmallBuffer, block->GetData(), newSize); size_t index = 0;
// free the block and set the new size size_t missingPages = 0;
fVolume->FreeBlock(block);
fSize = newSize; 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 2007, Ingo Weinhold, [email protected].
* Copyright 2019, Haiku, Inc.
* All rights reserved. Distributed under the terms of the MIT license. * All rights reserved. Distributed under the terms of the MIT license.
*/ */
#ifndef DATA_CONTAINER_H #ifndef DATA_CONTAINER_H
#define DATA_CONTAINER_H #define DATA_CONTAINER_H
#include "List.h" #include <OS.h>
struct vm_page;
class VMCache;
class AllocationInfo; class AllocationInfo;
class BlockReference;
class Volume; class Volume;
// Size of the DataContainer's small buffer. If it contains data up to this // 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, virtual status_t WriteAt(off_t offset, const void *buffer, size_t size,
size_t *bytesWritten); size_t *bytesWritten);
void GetFirstDataBlock(const uint8 **data, size_t *length);
// debugging // debugging
void GetAllocationInfo(AllocationInfo &info); void GetAllocationInfo(AllocationInfo &info);
private: 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 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: private:
Volume *fVolume; Volume *fVolume;
off_t fSize; off_t fSize;
union { VMCache* fCache;
uint8 fBlocks[sizeof(BlockList)]; uint8 fSmallBuffer[kSmallDataContainerSize];
uint8 fSmallBuffer[kSmallDataContainerSize];
};
}; };
#endif // DATA_CONTAINER_H #endif // DATA_CONTAINER_H
@@ -12,10 +12,6 @@ KernelAddon ramfs
AttributeIndex.cpp AttributeIndex.cpp
AttributeIndexImpl.cpp AttributeIndexImpl.cpp
AttributeIterator.cpp AttributeIterator.cpp
BlockAllocator.cpp
BlockAllocatorArea.cpp
BlockAllocatorAreaBucket.cpp
BlockReferenceManager.cpp
DataContainer.cpp DataContainer.cpp
DebugSupport.cpp DebugSupport.cpp
Directory.cpp Directory.cpp
@@ -26,8 +26,8 @@
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include "Block.h" #include <vm/vm_page.h>
#include "BlockAllocator.h"
#include "DebugSupport.h" #include "DebugSupport.h"
#include "Directory.h" #include "Directory.h"
#include "DirectoryEntryTable.h" #include "DirectoryEntryTable.h"
@@ -43,11 +43,6 @@
#include "TwoKeyAVLTree.h" #include "TwoKeyAVLTree.h"
#include "Volume.h" #include "Volume.h"
// default block size
static const off_t kDefaultBlockSize = 4096;
static const size_t kDefaultAreaSize = kDefaultBlockSize * 128;
// default volume name // default volume name
static const char *kDefaultVolumeName = "RAM FS"; static const char *kDefaultVolumeName = "RAM FS";
@@ -143,9 +138,6 @@ Volume::Volume(fs_volume* volume)
fAnyNodeListeners(), fAnyNodeListeners(),
fEntryListeners(NULL), fEntryListeners(NULL),
fAnyEntryListeners(), fAnyEntryListeners(),
fBlockAllocator(NULL),
fBlockSize(kDefaultBlockSize),
fAllocatedBlocks(0),
fAccessTime(0), fAccessTime(0),
fMounted(false) fMounted(false)
{ {
@@ -173,14 +165,6 @@ Volume::Mount(uint32 flags)
Unmount(); Unmount();
status_t error = B_OK; 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 // create the listener trees
if (error == B_OK) { if (error == B_OK) {
fNodeListeners = new(nothrow) NodeListenerTree; fNodeListeners = new(nothrow) NodeListenerTree;
@@ -267,41 +251,22 @@ Volume::Unmount()
delete fNodeTable; delete fNodeTable;
fNodeTable = NULL; fNodeTable = NULL;
} }
// delete the block allocator
if (fBlockAllocator) {
delete fBlockAllocator;
fBlockAllocator = NULL;
}
return B_OK; return B_OK;
} }
// GetBlockSize
off_t
Volume::GetBlockSize() const
{
return fBlockSize;
}
// CountBlocks // CountBlocks
off_t off_t
Volume::CountBlocks() const Volume::CountBlocks() const
{ {
size_t bytes = 0; // TODO: Compute how many pages we are using across all DataContainers.
system_info sysInfo; return 0;
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;
} }
// CountFreeBlocks // CountFreeBlocks
off_t off_t
Volume::CountFreeBlocks() const Volume::CountFreeBlocks() const
{ {
// TODO:... return vm_page_num_available_pages();
return CountBlocks() - fBlockAllocator->GetUsedBytes() / kDefaultBlockSize;
} }
// SetName // 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 // GetAllocationInfo
void void
Volume::GetAllocationInfo(AllocationInfo &info) Volume::GetAllocationInfo(AllocationInfo &info)
@@ -813,9 +730,6 @@ Volume::GetAllocationInfo(AllocationInfo &info)
fRootDirectory->GetAllocationInfo(info); fRootDirectory->GetAllocationInfo(info);
// name // name
info.AddStringAllocation(fName.GetLength()); info.AddStringAllocation(fName.GetLength());
// block allocator
info.AddOtherAllocation(sizeof(BlockAllocator));
fBlockAllocator->GetAllocationInfo(info);
} }
// ReadLock // ReadLock
+2 -12
View File
@@ -34,9 +34,8 @@
#include "String.h" #include "String.h"
class AllocationInfo; class AllocationInfo;
class Block; class Attribute;
class BlockAllocator; class AttributeIndex;
class BlockReference;
class Directory; class Directory;
class DirectoryEntryTable; class DirectoryEntryTable;
class Entry; class Entry;
@@ -101,7 +100,6 @@ public:
dev_t GetID() const { return fVolume != NULL ? fVolume->id : -1; } dev_t GetID() const { return fVolume != NULL ? fVolume->id : -1; }
fs_volume* FSVolume() const { return fVolume; } fs_volume* FSVolume() const { return fVolume; }
off_t GetBlockSize() const;
off_t CountBlocks() const; off_t CountBlocks() const;
off_t CountFreeBlocks() const; off_t CountFreeBlocks() const;
@@ -156,11 +154,6 @@ public:
ino_t NextNodeID() { return fNextNodeID++; } 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); void GetAllocationInfo(AllocationInfo &info);
bigtime_t GetAccessTime() const { return fAccessTime; } bigtime_t GetAccessTime() const { return fAccessTime; }
@@ -194,9 +187,6 @@ private:
EntryListenerTree *fEntryListeners; EntryListenerTree *fEntryListeners;
EntryListenerList fAnyEntryListeners; EntryListenerList fAnyEntryListeners;
QueryList fQueries; QueryList fQueries;
BlockAllocator *fBlockAllocator;
off_t fBlockSize;
off_t fAllocatedBlocks;
bigtime_t fAccessTime; bigtime_t fAccessTime;
bool fMounted; bool fMounted;
}; };
@@ -137,7 +137,7 @@ ramfs_read_fs_info(fs_volume* _volume, struct fs_info *info)
if (VolumeReadLocker locker = volume) { if (VolumeReadLocker locker = volume) {
info->flags = B_FS_IS_PERSISTENT | B_FS_HAS_ATTR | B_FS_HAS_MIME info->flags = B_FS_IS_PERSISTENT | B_FS_HAS_ATTR | B_FS_HAS_MIME
| B_FS_HAS_QUERY; | B_FS_HAS_QUERY;
info->block_size = volume->GetBlockSize(); info->block_size = B_PAGE_SIZE;
info->io_size = kOptimalIOSize; info->io_size = kOptimalIOSize;
info->total_blocks = volume->CountBlocks(); info->total_blocks = volume->CountBlocks();
info->free_blocks = volume->CountFreeBlocks(); info->free_blocks = volume->CountFreeBlocks();