diff --git a/src/kernel/core/vm/Jamfile b/src/kernel/core/vm/Jamfile index c5a410ca18..866294ae05 100644 --- a/src/kernel/core/vm/Jamfile +++ b/src/kernel/core/vm/Jamfile @@ -1,15 +1,16 @@ SubDir OBOS_TOP src kernel core vm ; -KernelStaticLibrary libvm : - <$(SOURCE_GRIST)>vm.c - <$(SOURCE_GRIST)>vm_cache.c - <$(SOURCE_GRIST)>vm_daemons.c - <$(SOURCE_GRIST)>vm_page.c - <$(SOURCE_GRIST)>vm_store_anonymous_noswap.c - <$(SOURCE_GRIST)>vm_store_device.c - <$(SOURCE_GRIST)>vm_store_null.c - <$(SOURCE_GRIST)>vm_store_vnode.c - <$(SOURCE_GRIST)>vm_tests.c +KernelMergeObject kernel_vm.o : + <$(SOURCE_GRIST)>cache.cpp + <$(SOURCE_GRIST)>vm.c + <$(SOURCE_GRIST)>vm_cache.c + <$(SOURCE_GRIST)>vm_daemons.c + <$(SOURCE_GRIST)>vm_page.c + <$(SOURCE_GRIST)>vm_store_anonymous_noswap.c + <$(SOURCE_GRIST)>vm_store_device.c + <$(SOURCE_GRIST)>vm_store_null.c + <$(SOURCE_GRIST)>vm_store_vnode.c + <$(SOURCE_GRIST)>vm_tests.c : -fno-pic -Wno-unused -D_KERNEL_ ; diff --git a/src/kernel/core/vm/cache.cpp b/src/kernel/core/vm/cache.cpp new file mode 100644 index 0000000000..0983de7503 --- /dev/null +++ b/src/kernel/core/vm/cache.cpp @@ -0,0 +1,348 @@ +/* +** Copyright 2004, Axel Dörfler, axeld@pinc-software.de. All rights reserved. +** Distributed under the terms of the OpenBeOS License. +*/ + + +#include + +#include +#include +#include + +#include +#include +#include + + +/* Note, this is just a dump and simple cache implementation targeted to be + * compatible with the one found in BeOS. + * This won't be the cache we end up using in R1, seriously :-) + */ + + +struct cache_entry { + cache_entry *next; // next in hash + off_t block_number; + void *data; + int32 ref_count; + int32 lock; + bool is_dirty; +}; + +struct cache { + hash_table *hash; + mutex lock; + off_t max_blocks; + size_t block_size; +}; + +static const int32 kNumCaches = 16; +struct cache sCaches[kNumCaches]; + // we can cache the first 16 fds (I said we were dumb, right?) + + +class MutexLocker { + public: + MutexLocker(int fd) + : fMutex(NULL) + { + if (fd < 0 || fd >= kNumCaches) + return; + + fMutex = &sCaches[fd].lock; + mutex_lock(fMutex); + } + + ~MutexLocker() + { + if (fMutex != NULL) + mutex_unlock(fMutex); + } + + status_t InitCheck() + { + return fMutex != NULL ? B_OK : B_ERROR; + } + + private: + mutex *fMutex; +}; + + +static int +cache_entry_compare(void *_cacheEntry, const void *_block) +{ + cache_entry *cacheEntry = (cache_entry *)_cacheEntry; + const off_t *block = (const off_t *)_block; + + return cacheEntry->block_number - *block; +} + + +static uint32 +cache_entry_hash(void *_cacheEntry, const void *_block, uint32 range) +{ + cache_entry *cacheEntry = (cache_entry *)_cacheEntry; + const off_t *block = (const off_t *)_block; + + if (cacheEntry != NULL) + return cacheEntry->block_number % range; + + return *block % range; +} + + +static status_t +write_cache_entry(int fd, cache_entry *entry) +{ + ssize_t blockSize = sCaches[fd].block_size; + + return write_pos(fd, entry->block_number * blockSize, + entry->data, blockSize) < blockSize ? B_ERROR : B_OK; +} + + +void +put_cache_entry(int fd, cache_entry *entry) +{ + if (entry == NULL) + return; + + if (--entry->ref_count > 0) + return; + + // write out entry if necessary + if (entry->is_dirty + && write_cache_entry(fd, entry) != B_OK) + panic("could not write back entry\n"); + + hash_remove(sCaches[fd].hash, entry); + free(entry->data); + free(entry); +} + + +cache_entry * +new_cache_entry(int fd, off_t blockNumber, size_t blockSize) +{ + cache_entry *entry = (cache_entry *)malloc(sizeof(cache_entry)); + if (entry == NULL) + return NULL; + + entry->data = malloc(blockSize); + if (entry->data == NULL) { + free(entry); + return NULL; + } + + sCaches[fd].block_size = blockSize; + // store block size (once would be sufficient, though) + + entry->block_number = blockNumber; + entry->ref_count = 1; + entry->lock = 0; + entry->is_dirty = false; + + hash_insert(sCaches[fd].hash, entry); + + return entry; +} + + +cache_entry * +lookup_cache_entry(int fd, off_t blockNumber) +{ + cache_entry *entry = (cache_entry *)hash_lookup(sCaches[fd].hash, &blockNumber); + if (entry == NULL) + return NULL; + + entry->ref_count++; + return entry; +} + + +// #pragma mark - +// public BeOS compatible interface + + +extern "C" void +force_cache_flush(int dev, int prefer_log_blocks) +{ +} + + +extern "C" int +flush_blocks(int dev, off_t bnum, int nblocks) +{ + return 0; +} + + +extern "C" int +flush_device(int dev, int warn_locked) +{ + return 0; +} + + +extern "C" int +init_cache_for_device(int fd, off_t max_blocks) +{ + if (fd < 0 || fd >= kNumCaches) + return B_ERROR; + if (sCaches[fd].hash != NULL) + return B_BUSY; + + sCaches[fd].hash = hash_init(32, 0, &cache_entry_compare, &cache_entry_hash); + return mutex_init(&sCaches[fd].lock, "block cache"); +} + + +extern "C" int +remove_cached_device_blocks(int fd, int allowWrite) +{ + if (fd < 0 || fd >= kNumCaches || sCaches[fd].hash == NULL) + return B_ERROR; + + if (allowWrite) { + hash_iterator iterator; + hash_open(sCaches[fd].hash, &iterator); + + cache_entry *entry; + while ((entry = (cache_entry *)hash_next(sCaches[fd].hash, &iterator)) != NULL) { + // write out entry if necessary + if (entry->is_dirty + && write_cache_entry(fd, entry) != B_OK) + panic("write failed!\n"); + } + hash_close(sCaches[fd].hash, &iterator, false); + } + + hash_uninit(sCaches[fd].hash); + mutex_destroy(&sCaches[fd].lock); + + return 0; +} + + +extern "C" void * +get_block(int fd, off_t blockNumber, int blockSize) +{ + MutexLocker locker(fd); + if (locker.InitCheck() != B_OK) + return NULL; + + cache_entry *entry = lookup_cache_entry(fd, blockNumber); + if (entry == NULL) { + // read entry into cache + entry = new_cache_entry(fd, blockNumber, blockSize); + if (entry == NULL) + return NULL; + + if (read_pos(fd, blockNumber * blockSize, entry->data, blockSize) < blockSize) { + free(entry->data); + free(entry); + return NULL; + } + } + + entry->lock++; + + return entry->data; +} + + +extern "C" void * +get_empty_block(int fd, off_t blockNumber, int blockSize) +{ + MutexLocker locker(fd); + if (locker.InitCheck() != B_OK) + return NULL; + + cache_entry *entry = lookup_cache_entry(fd, blockNumber); + if (entry == NULL) { + // create new cache entry + entry = new_cache_entry(fd, blockNumber, blockSize); + if (entry == NULL) + return NULL; + } + memset(entry->data, 0, blockSize); + + entry->lock++; + + return entry->data; +} + + +extern "C" int +release_block(int fd, off_t blockNumber) +{ + MutexLocker locker(fd); + if (locker.InitCheck() != B_OK) + return B_ERROR; + + cache_entry *entry = lookup_cache_entry(fd, blockNumber); + if (entry == NULL) + panic("release_block() called on block %Ld that was not cached\n", blockNumber); + + entry->lock--; + put_cache_entry(fd, entry); + return 0; +} + + +extern "C" int +mark_blocks_dirty(int fd, off_t blockNumber, int numBlocks) +{ + return -1; +} + + +extern "C" int +cached_read(int fd, off_t blockNumber, void *data, off_t numBlocks, int blockSize) +{ + MutexLocker locker(fd); + if (locker.InitCheck() != B_OK) + return B_ERROR; + + ssize_t bytes = numBlocks * blockSize; + if (read_pos(fd, blockNumber * blockSize, data, bytes) < bytes) + return -1; + + // check if there are any cached blocks in that range shadowing the device contents + + for (off_t blockOffset = 0; blockOffset < numBlocks; blockOffset++) { + cache_entry *entry = lookup_cache_entry(fd, blockNumber + blockOffset); + if (entry != NULL) { + if (entry->is_dirty) + memcpy((void *)((addr_t)data + blockOffset * blockSize), entry->data, blockSize); + put_cache_entry(fd, entry); + } + } + return 0; +} + + +extern "C" int +cached_write(int fd, off_t blockNumber, const void *data, off_t numBlocks, int blockSize) +{ + return -1; +} + + +extern "C" int +cached_write_locked(int fd, off_t blockNumber, const void *data, off_t numBlocks, int blockSize) +{ + return -1; +} + + +extern "C" int +set_blocks_info(int fd, off_t *blocks, int numBlocks, + void (*func)(off_t bnum, size_t nblocks, void *arg), + void *arg) +{ + return -1; +} +