diff --git a/src/add-ons/kernel/file_systems/bfs/BufferPool.cpp b/src/add-ons/kernel/file_systems/bfs/BufferPool.cpp new file mode 100644 index 0000000000..e6182e73aa --- /dev/null +++ b/src/add-ons/kernel/file_systems/bfs/BufferPool.cpp @@ -0,0 +1,151 @@ +/* BufferPool - a buffer pool for uncached file access +** +** Initial version by Axel Dörfler, axeld@pinc-software.de +** This file may be used under the terms of the OpenBeOS License. +*/ + + +#include "BufferPool.h" +#include "cpp.h" + +#include + + +const uint32 kNumBuffers = 8; + + +BufferPool::BufferPool() + : + fFirstFree(NULL) +{ + fLock = create_sem(1, "buffer lock"); + fFreeBuffers = create_sem(0, "free buffers"); +} + + +BufferPool::~BufferPool() +{ + delete_sem(fLock); + delete_sem(fFreeBuffers); + + void **buffer = fFirstFree; + while (buffer != NULL) { + void **nextBuffer = (void **)*buffer; + free(buffer); + buffer = nextBuffer; + } +} + + +status_t +BufferPool::InitCheck() +{ + if (fLock < B_OK + || fFreeBuffers < B_OK) + return B_ERROR; + + return B_OK; +} + + +status_t +BufferPool::RequestBuffers(uint32 blockSize) +{ + void **buffers[kNumBuffers]; + + // allocate and connect buffers + + for (int32 i = 0; i < kNumBuffers; i++) { + buffers[i] = (void **)malloc(blockSize); + if (buffers[i] == NULL) { + // free already allocated buffers + for (;i-- > 0; i++) + free(buffers[i]); + return B_NO_MEMORY; + } + if (i > 0) + *(buffers[i]) = buffers[i - 1]; + } + + // add the buffers to the free buffers queue + + status_t status = acquire_sem(fLock); + if (status == B_OK) { + *(buffers[0]) = fFirstFree; + fFirstFree = buffers[kNumBuffers - 1]; + release_sem(fLock); + release_sem_etc(fFreeBuffers, kNumBuffers, B_DO_NOT_RESCHEDULE); + } else { + for (int32 i = 0; i < kNumBuffers; i++) + free(buffers[i]); + } + + return status; +} + + +status_t +BufferPool::ReleaseBuffers() +{ + status_t status = acquire_sem_etc(fFreeBuffers, kNumBuffers, 0, 0); + if (status < B_OK) + return status; + + status = acquire_sem(fLock); + if (status < B_OK) + return status; + + void **buffer = fFirstFree; + for (int32 i = 0; i < kNumBuffers && buffer; i++) { + void **nextBuffer = (void **)*buffer; + + free(buffer); + buffer = nextBuffer; + } + + release_sem(fLock); +} + + +status_t +BufferPool::GetBuffer(void **_buffer) +{ + status_t status = acquire_sem(fFreeBuffers); + if (status < B_OK) + return status; + + if ((status = acquire_sem(fLock)) < B_OK) { + release_sem(fFreeBuffers); + return status; + } + + void **buffer = fFirstFree; + fFirstFree = (void **)*buffer; + + release_sem(fLock); + + *_buffer = (void *)buffer; + return B_OK; +} + + +status_t +BufferPool::PutBuffer(void *_buffer) +{ + void **buffer = (void **)_buffer; + if (buffer == NULL) + return B_BAD_VALUE; + + status_t status = acquire_sem(fLock); + if (status < B_OK) + return status; + + *buffer = fFirstFree; + fFirstFree = buffer; + + release_sem(fLock); + release_sem(fFreeBuffers); + + return B_OK; +} + diff --git a/src/add-ons/kernel/file_systems/bfs/BufferPool.h b/src/add-ons/kernel/file_systems/bfs/BufferPool.h new file mode 100644 index 0000000000..9e18ca27d2 --- /dev/null +++ b/src/add-ons/kernel/file_systems/bfs/BufferPool.h @@ -0,0 +1,31 @@ +#ifndef BUFFER_POOL_H +#define BUFFER_POOL_H +/* BufferPool - a buffer pool for uncached file access +** +** Initial version by Axel Dörfler, axeld@pinc-software.de +** This file may be used under the terms of the OpenBeOS License. +*/ + + +#include + + +class BufferPool { + public: + BufferPool(); + ~BufferPool(); + + status_t InitCheck(); + + status_t RequestBuffers(uint32 blockSize); + status_t ReleaseBuffers(); + + status_t GetBuffer(void **_buffer); + status_t PutBuffer(void *buffer); + + private: + sem_id fLock, fFreeBuffers; + void **fFirstFree; +}; + +#endif /* BUFFER_POOL_H */ diff --git a/src/add-ons/kernel/file_systems/bfs/Jamfile b/src/add-ons/kernel/file_systems/bfs/Jamfile index c7ec61aac5..427f5feb35 100644 --- a/src/add-ons/kernel/file_systems/bfs/Jamfile +++ b/src/add-ons/kernel/file_systems/bfs/Jamfile @@ -19,5 +19,6 @@ R5KernelAddon obfs : [ FDirName kernel file_systems bfs ] : Query.cpp Utility.cpp Volume.cpp + BufferPool.cpp ; diff --git a/src/add-ons/kernel/file_systems/bfs/Volume.h b/src/add-ons/kernel/file_systems/bfs/Volume.h index 1c6a143e10..aab8fcd147 100644 --- a/src/add-ons/kernel/file_systems/bfs/Volume.h +++ b/src/add-ons/kernel/file_systems/bfs/Volume.h @@ -20,6 +20,7 @@ extern "C" { #include "bfs.h" #include "BlockAllocator.h" +#include "BufferPool.h" #include "Chain.h" class Journal; @@ -86,6 +87,7 @@ class Volume { #ifdef DEBUG BlockAllocator &Allocator() { return fBlockAllocator; } #endif + BufferPool &Pool() { return fBufferPool; } status_t Sync(); Journal *GetJournal(off_t /*refBlock*/) const { return fJournal; } @@ -121,6 +123,8 @@ class Volume { int32 fUniqueID; uint32 fFlags; + + BufferPool fBufferPool; }; // inline functions diff --git a/src/add-ons/kernel/file_systems/bfs/kernel_interface.cpp b/src/add-ons/kernel/file_systems/bfs/kernel_interface.cpp index 5dcaab7628..887a4ba44a 100644 --- a/src/add-ons/kernel/file_systems/bfs/kernel_interface.cpp +++ b/src/add-ons/kernel/file_systems/bfs/kernel_interface.cpp @@ -524,9 +524,27 @@ bfs_ioctl(void *_ns, void *_node, void *_cookie, int cmd, void *buffer, size_t b switch (cmd) { case IOCTL_FILE_UNCACHED_IO: + { + // if the inode is already set up for uncached access, bail out + if (inode->Flags() & INODE_NO_CACHE) { + FATAL(("File %Ld is already uncached\n", inode->ID())); + return B_ERROR; + } if (inode != NULL) - PRINT(("trying to make access to inode %lx uncached. Not yet implemented!\n",inode->ID())); + PRINT(("trying to make access to inode %Ld uncached. Not yet implemented!\n",inode->ID())); + + // ToDo: sync the cache for this file! + // Unfortunately, we can't remove any blocks from the cache in BeOS, + // that means we can't guarantee consistency for the file contents + // spanning over both access modes. + + // request buffers for being able to access the file without + // using the cache or allocating memory + status_t status = volume->Pool().RequestBuffers(volume->BlockSize()); + if (status == B_OK) + inode->Node()->flags |= INODE_NO_CACHE; return B_ERROR; + } #ifdef DEBUG case 56742: { @@ -1015,6 +1033,10 @@ bfs_open(void *_ns, void *_node, int omode, void **_cookie) Volume *volume = (Volume *)_ns; Inode *inode = (Inode *)_node; + // we can't open a file which uses uncached access twice + if (inode->Flags() & INODE_NO_CACHE) + return B_BUSY; + // opening a directory read-only is allowed, although you can't read // any data from it. if (inode->IsDirectory() && omode & O_RWMASK) { @@ -1151,11 +1173,12 @@ bfs_close(void *_ns, void *_node, void *_cookie) file_cookie *cookie = (file_cookie *)_cookie; + Volume *volume = (Volume *)_ns; + Inode *inode = (Inode *)_node; + if (cookie->open_mode & O_RWMASK) { // trim the preallocated blocks and update the size, // and last_modified indices if needed - Volume *volume = (Volume *)_ns; - Inode *inode = (Inode *)_node; Transaction transaction(volume,inode->BlockNumber()); @@ -1173,6 +1196,11 @@ bfs_close(void *_ns, void *_node, void *_cookie) notify_listener(B_STAT_CHANGED,volume->ID(),0,0,inode->ID(),NULL); } + if (inode->Flags() & INODE_NO_CACHE) { + volume->Pool().ReleaseBuffers(); + inode->Node().flags &= ~INODE_NO_CACHE; + } + return B_OK; }