From 3d268eda3d0ca504c865533347decf27b54025b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Sat, 10 Nov 2007 21:19:52 +0000 Subject: [PATCH] * Extracted file_map API out of the file cache - it's now an optional service that can be used by file systems. * Changed the way the file cache works: instead of reading/writing to the underlying device directly, it can now be used for any data source, ie. also network file systems. * As a result, the former pages_io() moved to the VFS layer, and can now be called by a file system via {read|write}_file_io_vec_pages() (naming suggestions are always welcomed :-)). It now gets an FD, and uses that to communicate with the device (via its fs_{read|write}_pages() hooks). * The file_cache_{read|write}() functions must now be called without holding an I/O relevant file system lock. That allows the file cache to prepare the pages without colliding with the page writer, IOW the "mayBlock" flag can go into the attic again (yay!). * This also results in a much better performance when the system does I/O and is low on memory, as the page writer can now finally write back some pages, and that even without maxing out the CPU :) * The API changes put slightly more burden on the fs_{read|write}_pages() hooks, but in combination with the file_map it's still pretty straight forward. It just will have to dispatch the call to the underlying device directly, usually it will just call its fs_{read|write}_pages() hooks via the above mentioned calls. * Ported BFS and FAT to the new API, the latter has not been tested, though. * Also ported the API changes to the fs_shell. I also completely removed its file cache level page handling - the downside is that device access is no longer cached (ie. depends on the host OS now), the upside is that the code is greatly simplified. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@22886 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- headers/os/drivers/fs_cache.h | 23 +- headers/os/drivers/fs_interface.h | 16 +- headers/private/fs_shell/fssh_api_wrapper.h | 13 +- headers/private/fs_shell/fssh_fs_cache.h | 28 +- headers/private/fs_shell/fssh_fs_interface.h | 22 +- headers/private/kernel/vfs.h | 6 +- headers/private/kernel/vm_types.h | 6 +- src/add-ons/kernel/file_systems/bfs/Debug.h | 2 +- src/add-ons/kernel/file_systems/bfs/Inode.cpp | 36 +- src/add-ons/kernel/file_systems/bfs/Inode.h | 3 + src/add-ons/kernel/file_systems/bfs/Lock.h | 24 +- .../file_systems/bfs/kernel_interface.cpp | 109 +- .../file_systems/cdda/kernel_interface.cpp | 6 +- src/add-ons/kernel/file_systems/fat/dir.c | 5 +- src/add-ons/kernel/file_systems/fat/dosfs.h | 6 +- src/add-ons/kernel/file_systems/fat/file.c | 96 +- src/add-ons/kernel/file_systems/fat/file.h | 4 +- .../file_systems/iso9660/kernel_interface.cpp | 9 +- src/system/kernel/cache/Jamfile | 1 + src/system/kernel/cache/file_cache.cpp | 628 +----- src/system/kernel/cache/file_map.cpp | 20 +- src/system/kernel/cache/vnode_store.cpp | 8 +- src/system/kernel/fs/devfs.cpp | 6 +- src/system/kernel/fs/pipefs.cpp | 6 +- src/system/kernel/fs/rootfs.cpp | 6 +- src/system/kernel/fs/vfs.cpp | 247 ++- src/system/kernel/vm/vm.cpp | 2 +- src/system/kernel/vm/vm_page.cpp | 8 +- .../kernel/vm/vm_store_anonymous_noswap.cpp | 4 +- src/system/kernel/vm/vm_store_device.c | 4 +- src/system/kernel/vm/vm_store_null.c | 4 +- src/tools/fs_shell/Jamfile | 1 + src/tools/fs_shell/file_cache.cpp | 1689 +---------------- src/tools/fs_shell/file_map.cpp | 325 ++++ src/tools/fs_shell/vfs.cpp | 293 ++- src/tools/fs_shell/vfs.h | 6 + 36 files changed, 1339 insertions(+), 2333 deletions(-) create mode 100644 src/tools/fs_shell/file_map.cpp diff --git a/headers/os/drivers/fs_cache.h b/headers/os/drivers/fs_cache.h index dd46794a35..2dd1b1bba3 100644 --- a/headers/os/drivers/fs_cache.h +++ b/headers/os/drivers/fs_cache.h @@ -56,23 +56,24 @@ extern status_t block_cache_set_dirty(void *_cache, off_t blockNumber, extern void block_cache_put(void *_cache, off_t blockNumber); /* file cache */ -extern void *file_cache_create(dev_t mountID, ino_t vnodeID, off_t size, - int fd); +extern void *file_cache_create(dev_t mountID, ino_t vnodeID, off_t size); extern void file_cache_delete(void *_cacheRef); extern status_t file_cache_set_size(void *_cacheRef, off_t size); extern status_t file_cache_sync(void *_cache); -extern status_t file_cache_invalidate_file_map(void *_cacheRef, off_t offset, - off_t size); -extern status_t file_cache_read_pages(void *_cacheRef, off_t offset, - const iovec *vecs, size_t count, size_t *_numBytes); -extern status_t file_cache_write_pages(void *_cacheRef, off_t offset, - const iovec *vecs, size_t count, size_t *_numBytes); -extern status_t file_cache_read(void *_cacheRef, off_t offset, void *bufferBase, - size_t *_size); -extern status_t file_cache_write(void *_cacheRef, off_t offset, +extern status_t file_cache_read(void *_cacheRef, void *cookie, off_t offset, + void *bufferBase, size_t *_size); +extern status_t file_cache_write(void *_cacheRef, void *cookie, off_t offset, const void *buffer, size_t *_size); +/* file map */ +extern void *file_map_create(dev_t mountID, ino_t vnodeID); +extern void file_map_delete(void *_map); +extern void file_map_set_size(void *_map, off_t size); +extern void file_map_invalidate(void *_map, off_t offset, off_t size); +extern status_t file_map_translate(void *_map, off_t offset, size_t size, + struct file_io_vec *vecs, size_t *_count); + #ifdef __cplusplus } #endif diff --git a/headers/os/drivers/fs_interface.h b/headers/os/drivers/fs_interface.h index e53330797f..d4330909b9 100644 --- a/headers/os/drivers/fs_interface.h +++ b/headers/os/drivers/fs_interface.h @@ -92,10 +92,10 @@ typedef struct file_system_module_info { bool (*can_page)(fs_volume fs, fs_vnode vnode, fs_cookie cookie); status_t (*read_pages)(fs_volume fs, fs_vnode vnode, fs_cookie cookie, off_t pos, const iovec *vecs, size_t count, size_t *_numBytes, - bool mayBlock, bool reenter); + bool reenter); status_t (*write_pages)(fs_volume fs, fs_vnode vnode, fs_cookie cookie, off_t pos, const iovec *vecs, size_t count, size_t *_numBytes, - bool mayBlock, bool reenter); + bool reenter); /* cache file access */ status_t (*get_file_map)(fs_volume fs, fs_vnode vnode, off_t offset, @@ -251,6 +251,18 @@ extern status_t remove_vnode(dev_t mountID, ino_t vnodeID); extern status_t unremove_vnode(dev_t mountID, ino_t vnodeID); extern status_t get_vnode_removed(dev_t mountID, ino_t vnodeID, bool* removed); +extern status_t read_pages(int fd, off_t pos, const struct iovec *vecs, + size_t count, size_t *_numBytes, bool fsReenter); +extern status_t write_pages(int fd, off_t pos, const struct iovec *vecs, + size_t count, size_t *_numBytes, bool fsReenter); +extern status_t read_file_io_vec_pages(int fd, + const struct file_io_vec *fileVecs, size_t fileVecCount, + const struct iovec *vecs, size_t vecCount, + uint32 *_vecIndex, size_t *_vecOffset, size_t *_bytes); +extern status_t write_file_io_vec_pages(int fd, + const struct file_io_vec *fileVecs, size_t fileVecCount, + const struct iovec *vecs, size_t vecCount, + uint32 *_vecIndex, size_t *_vecOffset, size_t *_bytes); // Deprecated! Will disappear soon! extern status_t notify_listener(int op, dev_t device, ino_t parentNode, diff --git a/headers/private/fs_shell/fssh_api_wrapper.h b/headers/private/fs_shell/fssh_api_wrapper.h index 867ffdae63..36e956e2e4 100644 --- a/headers/private/fs_shell/fssh_api_wrapper.h +++ b/headers/private/fs_shell/fssh_api_wrapper.h @@ -820,13 +820,16 @@ #define file_cache_delete fssh_file_cache_delete #define file_cache_set_size fssh_file_cache_set_size #define file_cache_sync fssh_file_cache_sync -#define file_cache_invalidate_file_map fssh_file_cache_invalidate_file_map -#define file_cache_read_pages fssh_file_cache_read_pages -#define file_cache_write_pages fssh_file_cache_write_pages #define file_cache_read fssh_file_cache_read #define file_cache_write fssh_file_cache_write +/* file map */ +#define file_map_create fssh_file_map_create +#define file_map_delete fssh_file_map_delete +#define file_map_set_size fssh_file_map_set_size +#define file_map_invalidate fssh_file_map_invalidate +#define file_map_translate fssh_file_map_translate //////////////////////////////////////////////////////////////////////////////// // #pragma mark - fssh_fs_index.h @@ -893,6 +896,10 @@ #define remove_vnode fssh_remove_vnode #define unremove_vnode fssh_unremove_vnode #define get_vnode_removed fssh_get_vnode_removed +#define read_pages fssh_read_pages +#define write_pages fssh_write_pages +#define read_file_io_vec_pages fssh_read_file_io_vec_pages +#define write_file_io_vec_pages fssh_write_file_io_vec_pages #define notify_entry_created fssh_notify_entry_created #define notify_entry_removed fssh_notify_entry_removed diff --git a/headers/private/fs_shell/fssh_fs_cache.h b/headers/private/fs_shell/fssh_fs_cache.h index 37cfb05796..e3841a5644 100644 --- a/headers/private/fs_shell/fssh_fs_cache.h +++ b/headers/private/fs_shell/fssh_fs_cache.h @@ -69,26 +69,30 @@ extern void fssh_block_cache_put(void *_cache, /* file cache */ extern void * fssh_file_cache_create(fssh_mount_id mountID, - fssh_vnode_id vnodeID, fssh_off_t size, int fd); + fssh_vnode_id vnodeID, fssh_off_t size); extern void fssh_file_cache_delete(void *_cacheRef); extern fssh_status_t fssh_file_cache_set_size(void *_cacheRef, fssh_off_t size); extern fssh_status_t fssh_file_cache_sync(void *_cache); -extern fssh_status_t fssh_file_cache_invalidate_file_map(void *_cacheRef, - fssh_off_t offset, fssh_off_t size); -extern fssh_status_t fssh_file_cache_read_pages(void *_cacheRef, - fssh_off_t offset, const fssh_iovec *vecs, - fssh_size_t count, fssh_size_t *_numBytes); -extern fssh_status_t fssh_file_cache_write_pages(void *_cacheRef, - fssh_off_t offset, const fssh_iovec *vecs, - fssh_size_t count, fssh_size_t *_numBytes); -extern fssh_status_t fssh_file_cache_read(void *_cacheRef, fssh_off_t offset, - void *bufferBase, fssh_size_t *_size); -extern fssh_status_t fssh_file_cache_write(void *_cacheRef, +extern fssh_status_t fssh_file_cache_read(void *_cacheRef, void *cookie, + fssh_off_t offset, void *bufferBase, + fssh_size_t *_size); +extern fssh_status_t fssh_file_cache_write(void *_cacheRef, void *cookie, fssh_off_t offset, const void *buffer, fssh_size_t *_size); +/* file map */ +extern void * fssh_file_map_create(fssh_mount_id mountID, + fssh_vnode_id vnodeID); +extern void fssh_file_map_delete(void *_map); +extern void fssh_file_map_set_size(void *_map, fssh_off_t size); +extern void fssh_file_map_invalidate(void *_map, fssh_off_t offset, + fssh_off_t size); +extern fssh_status_t fssh_file_map_translate(void *_map, fssh_off_t offset, + fssh_size_t size, struct fssh_file_io_vec *vecs, + fssh_size_t *_count); + #ifdef __cplusplus } #endif diff --git a/headers/private/fs_shell/fssh_fs_interface.h b/headers/private/fs_shell/fssh_fs_interface.h index e83ebb1393..4cbf9fdbb2 100644 --- a/headers/private/fs_shell/fssh_fs_interface.h +++ b/headers/private/fs_shell/fssh_fs_interface.h @@ -96,12 +96,10 @@ typedef struct fssh_file_system_module_info { fssh_fs_cookie cookie); fssh_status_t (*read_pages)(fssh_fs_volume fs, fssh_fs_vnode vnode, fssh_fs_cookie cookie, fssh_off_t pos, const fssh_iovec *vecs, - fssh_size_t count, fssh_size_t *_numBytes, bool mayBlock, - bool reenter); + fssh_size_t count, fssh_size_t *_numBytes, bool reenter); fssh_status_t (*write_pages)(fssh_fs_volume fs, fssh_fs_vnode vnode, fssh_fs_cookie cookie, fssh_off_t pos, const fssh_iovec *vecs, - fssh_size_t count, fssh_size_t *_numBytes, bool mayBlock, - bool reenter); + fssh_size_t count, fssh_size_t *_numBytes, bool reenter); /* cache file access */ fssh_status_t (*get_file_map)(fssh_fs_volume fs, fssh_fs_vnode vnode, @@ -293,6 +291,22 @@ extern fssh_status_t fssh_unremove_vnode(fssh_mount_id mountID, fssh_vnode_id vnodeID); extern fssh_status_t fssh_get_vnode_removed(fssh_mount_id mountID, fssh_vnode_id vnodeID, bool* removed); +extern fssh_status_t fssh_read_pages(int fd, fssh_off_t pos, + const struct fssh_iovec *vecs, fssh_size_t count, + fssh_size_t *_numBytes, bool fsReenter); +extern fssh_status_t fssh_write_pages(int fd, fssh_off_t pos, + const struct fssh_iovec *vecs, fssh_size_t count, + fssh_size_t *_numBytes, bool fsReenter); +extern fssh_status_t fssh_read_file_io_vec_pages(int fd, + const struct fssh_file_io_vec *fileVecs, + fssh_size_t fileVecCount, const struct fssh_iovec *vecs, + fssh_size_t vecCount, uint32_t *_vecIndex, + fssh_size_t *_vecOffset, fssh_size_t *_bytes); +extern fssh_status_t fssh_write_file_io_vec_pages(int fd, + const struct fssh_file_io_vec *fileVecs, + fssh_size_t fileVecCount, const struct fssh_iovec *vecs, + fssh_size_t vecCount, uint32_t *_vecIndex, + fssh_size_t *_vecOffset, fssh_size_t *_bytes); extern fssh_status_t fssh_notify_entry_created(fssh_mount_id device, fssh_vnode_id directory, const char *name, fssh_vnode_id node); diff --git a/headers/private/kernel/vfs.h b/headers/private/kernel/vfs.h index 039452018e..bb3665b9db 100644 --- a/headers/private/kernel/vfs.h +++ b/headers/private/kernel/vfs.h @@ -94,11 +94,9 @@ void vfs_acquire_vnode(struct vnode *vnode); status_t vfs_get_cookie_from_fd(int fd, void **_cookie); bool vfs_can_page(struct vnode *vnode, void *cookie); status_t vfs_read_pages(struct vnode *vnode, void *cookie, off_t pos, - const iovec *vecs, size_t count, size_t *_numBytes, bool mayBlock, - bool fsReenter); + const iovec *vecs, size_t count, size_t *_numBytes, bool fsReenter); status_t vfs_write_pages(struct vnode *vnode, void *cookie, off_t pos, - const iovec *vecs, size_t count, size_t *_numBytes, bool mayBlock, - bool fsReenter); + const iovec *vecs, size_t count, size_t *_numBytes, bool fsReenter); status_t vfs_get_vnode_cache(struct vnode *vnode, struct vm_cache **_cache, bool allocate); status_t vfs_get_file_map(struct vnode *vnode, off_t offset, size_t size, diff --git a/headers/private/kernel/vm_types.h b/headers/private/kernel/vm_types.h index cc733f1b8c..819b05138b 100644 --- a/headers/private/kernel/vm_types.h +++ b/headers/private/kernel/vm_types.h @@ -223,11 +223,9 @@ typedef struct vm_store_ops { status_t (*commit)(struct vm_store *backingStore, off_t size); bool (*has_page)(struct vm_store *backingStore, off_t offset); status_t (*read)(struct vm_store *backingStore, off_t offset, - const iovec *vecs, size_t count, size_t *_numBytes, bool mayBlock, - bool fsReenter); + const iovec *vecs, size_t count, size_t *_numBytes, bool fsReenter); status_t (*write)(struct vm_store *backingStore, off_t offset, - const iovec *vecs, size_t count, size_t *_numBytes, bool mayBlock, - bool fsReenter); + const iovec *vecs, size_t count, size_t *_numBytes, bool fsReenter); status_t (*fault)(struct vm_store *backingStore, struct vm_address_space *aspace, off_t offset); status_t (*acquire_unreferenced_ref)(struct vm_store *backingStore); diff --git a/src/add-ons/kernel/file_systems/bfs/Debug.h b/src/add-ons/kernel/file_systems/bfs/Debug.h index d25399882d..ce6a52636d 100644 --- a/src/add-ons/kernel/file_systems/bfs/Debug.h +++ b/src/add-ons/kernel/file_systems/bfs/Debug.h @@ -47,7 +47,7 @@ // D() // the statements in D() are only included if DEBUG is defined -#ifdef DEBUG +#if 0//DEBUG #define PRINT(x) { __out("bfs: "); __out x; } #define REPORT_ERROR(status) \ __out("bfs: %s:%d: %s\n", __FUNCTION__, __LINE__, strerror(status)); diff --git a/src/add-ons/kernel/file_systems/bfs/Inode.cpp b/src/add-ons/kernel/file_systems/bfs/Inode.cpp index 802a8d575d..4664fbfc45 100644 --- a/src/add-ons/kernel/file_systems/bfs/Inode.cpp +++ b/src/add-ons/kernel/file_systems/bfs/Inode.cpp @@ -171,7 +171,8 @@ Inode::Inode(Volume *volume, ino_t id) fID(id), fTree(NULL), fAttributes(NULL), - fCache(NULL) + fCache(NULL), + fMap(NULL) { PRINT(("Inode::Inode(volume = %p, id = %Ld) @ %p\n", volume, id, this)); @@ -189,8 +190,10 @@ Inode::Inode(Volume *volume, ino_t id) if (IsContainer()) fTree = new BPlusTree(this); - if (IsFile() || IsAttribute()) - SetFileCache(file_cache_create(fVolume->ID(), ID(), Size(), fVolume->Device())); + if (IsFile() || IsAttribute()) { + SetFileCache(file_cache_create(fVolume->ID(), ID(), Size())); + SetMap(file_map_create(volume->ID(), ID())); + } } @@ -201,7 +204,8 @@ Inode::Inode(Volume *volume, Transaction &transaction, ino_t id, mode_t mode, fID(id), fTree(NULL), fAttributes(NULL), - fCache(NULL) + fCache(NULL), + fMap(NULL) { PRINT(("Inode::Inode(volume = %p, transaction = %p, id = %Ld) @ %p\n", volume, &transaction, id, this)); @@ -242,6 +246,7 @@ Inode::~Inode() PRINT(("Inode::~Inode() @ %p\n", this)); file_cache_delete(FileCache()); + file_map_delete(Map()); delete fTree; } @@ -384,8 +389,8 @@ Inode::_MakeSpaceForSmallData(Transaction &transaction, bfs_inode *node, // Luckily, this doesn't cause any index updates Inode *attribute; - status_t status = CreateAttribute(transaction, item->Name(), item->Type(), - &attribute); + status_t status = CreateAttribute(transaction, item->Name(), + item->Type(), &attribute); if (status < B_OK) RETURN_ERROR(status); @@ -1233,12 +1238,16 @@ Inode::ReadAt(off_t pos, uint8 *buffer, size_t *_length) if (pos < 0) return B_BAD_VALUE; + ReadLocked locker(Lock()); + if (pos >= Size() || length == 0) { *_length = 0; return B_NO_ERROR; } - return file_cache_read(FileCache(), pos, buffer, _length); + locker.Unlock(); + + return file_cache_read(FileCache(), NULL, pos, buffer, _length); } @@ -1246,6 +1255,10 @@ status_t Inode::WriteAt(Transaction &transaction, off_t pos, const uint8 *buffer, size_t *_length) { + WriteLocked locker(Lock()); + if (locker.IsLocked() < B_OK) + RETURN_ERROR(B_ERROR); + // update the last modification time in memory, it will be written // back to the inode, and the index when the file is closed // ToDo: should update the internal last modified time only at this point! @@ -1298,7 +1311,9 @@ Inode::WriteAt(Transaction &transaction, off_t pos, const uint8 *buffer, if (length == 0) return B_OK; - return file_cache_write(FileCache(), pos, buffer, _length); + locker.Unlock(); + + return file_cache_write(FileCache(), NULL, pos, buffer, _length); } @@ -1945,6 +1960,8 @@ Inode::SetFileSize(Transaction &transaction, off_t size) return status; file_cache_set_size(FileCache(), size); + file_map_set_size(Map(), size); + return WriteBack(transaction); } @@ -2400,7 +2417,8 @@ Inode::Create(Transaction &transaction, Inode *parent, const char *name, if (inode->IsFile() || inode->IsAttribute()) { inode->SetFileCache(file_cache_create(volume->ID(), inode->ID(), - inode->Size(), volume->Device())); + inode->Size())); + inode->SetMap(file_map_create(volume->ID(), inode->ID())); } if (_created) diff --git a/src/add-ons/kernel/file_systems/bfs/Inode.h b/src/add-ons/kernel/file_systems/bfs/Inode.h index 0c351ec2bd..dd7054b0f5 100644 --- a/src/add-ons/kernel/file_systems/bfs/Inode.h +++ b/src/add-ons/kernel/file_systems/bfs/Inode.h @@ -156,6 +156,8 @@ class Inode { // file cache void *FileCache() const { return fCache; } void SetFileCache(void *cache) { fCache = cache; } + void *Map() const { return fMap; } + void SetMap(void *map) { fMap = map; } private: Inode(const Inode &); @@ -198,6 +200,7 @@ class Inode { BPlusTree *fTree; Inode *fAttributes; void *fCache; + void *fMap; bfs_inode fNode; off_t fOldSize; diff --git a/src/add-ons/kernel/file_systems/bfs/Lock.h b/src/add-ons/kernel/file_systems/bfs/Lock.h index 0b8730365f..bf65640b99 100644 --- a/src/add-ons/kernel/file_systems/bfs/Lock.h +++ b/src/add-ons/kernel/file_systems/bfs/Lock.h @@ -440,18 +440,26 @@ class ReadLocked { { fStatus = lock.Lock(); } - + ~ReadLocked() { if (fStatus == B_OK) fLock.Unlock(); } - - status_t IsLocked() + + status_t + IsLocked() { return fStatus; } + void + Unlock() + { + fLock.Unlock(); + fStatus = B_NO_INIT; + } + private: ReadWriteLock &fLock; status_t fStatus; @@ -480,11 +488,19 @@ class WriteLocked { fLock->UnlockWrite(); } - status_t IsLocked() + status_t + IsLocked() { return fStatus; } + void + Unlock() + { + fLock->UnlockWrite(); + fStatus = B_NO_INIT; + } + private: ReadWriteLock *fLock; status_t fStatus; 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 69c7872f41..125eb4ca83 100644 --- a/src/add-ons/kernel/file_systems/bfs/kernel_interface.cpp +++ b/src/add-ons/kernel/file_systems/bfs/kernel_interface.cpp @@ -331,23 +331,42 @@ bfs_can_page(fs_volume _fs, fs_vnode _v, fs_cookie _cookie) static status_t bfs_read_pages(fs_volume _fs, fs_vnode _node, fs_cookie _cookie, off_t pos, - const iovec *vecs, size_t count, size_t *_numBytes, bool mayBlock, - bool reenter) + const iovec *vecs, size_t count, size_t *_numBytes, bool reenter) { + Volume *volume = (Volume *)_fs; Inode *inode = (Inode *)_node; if (inode->FileCache() == NULL) RETURN_ERROR(B_BAD_VALUE); - if (!reenter) { - if (mayBlock) - inode->Lock().Lock(); - else if (inode->Lock().TryLock() < B_OK) - return B_WOULD_BLOCK; - } + if (!reenter) + inode->Lock().Lock(); - status_t status = file_cache_read_pages(inode->FileCache(), pos, vecs, - count, _numBytes); + uint32 vecIndex = 0; + size_t vecOffset = 0; + size_t bytesLeft = *_numBytes; + status_t status; + + while (true) { + file_io_vec fileVecs[8]; + uint32 fileVecCount = 8; + + status = file_map_translate(inode->Map(), pos, bytesLeft, fileVecs, + &fileVecCount); + if (status != B_OK && status != B_BUFFER_OVERFLOW) + break; + + bool bufferOverflow = status == B_BUFFER_OVERFLOW; + + size_t bytes; + status = read_file_io_vec_pages(volume->Device(), fileVecs, + fileVecCount, vecs, count, &vecIndex, &vecOffset, &bytes); + if (status != B_OK || !bufferOverflow) + break; + + pos += bytes; + bytesLeft -= bytes; + } if (!reenter) inode->Lock().Unlock(); @@ -358,23 +377,42 @@ bfs_read_pages(fs_volume _fs, fs_vnode _node, fs_cookie _cookie, off_t pos, static status_t bfs_write_pages(fs_volume _fs, fs_vnode _node, fs_cookie _cookie, off_t pos, - const iovec *vecs, size_t count, size_t *_numBytes, bool mayBlock, - bool reenter) + const iovec *vecs, size_t count, size_t *_numBytes, bool reenter) { + Volume *volume = (Volume *)_fs; Inode *inode = (Inode *)_node; if (inode->FileCache() == NULL) RETURN_ERROR(B_BAD_VALUE); - if (!reenter) { - if (mayBlock) - inode->Lock().Lock(); - else if (inode->Lock().TryLock() < B_OK) - return B_WOULD_BLOCK; - } + if (!reenter) + inode->Lock().Lock(); - status_t status = file_cache_write_pages(inode->FileCache(), pos, vecs, - count, _numBytes); + uint32 vecIndex = 0; + size_t vecOffset = 0; + size_t bytesLeft = *_numBytes; + status_t status; + + while (true) { + file_io_vec fileVecs[8]; + uint32 fileVecCount = 8; + + status = file_map_translate(inode->Map(), pos, bytesLeft, fileVecs, + &fileVecCount); + if (status != B_OK && status != B_BUFFER_OVERFLOW) + break; + + bool bufferOverflow = status == B_BUFFER_OVERFLOW; + + size_t bytes; + status = write_file_io_vec_pages(volume->Device(), fileVecs, + fileVecCount, vecs, count, &vecIndex, &vecOffset, &bytes); + if (status != B_OK || !bufferOverflow) + break; + + pos += bytes; + bytesLeft -= bytes; + } if (!reenter) inode->Lock().Unlock(); @@ -818,8 +856,8 @@ bfs_create_symlink(void *_ns, void *_directory, const char *name, | INODE_LOGGED); // links usually don't have a file cache attached - but we now need one - link->SetFileCache(file_cache_create(volume->ID(), link->ID(), 0, - volume->Device())); + link->SetFileCache(file_cache_create(volume->ID(), link->ID(), 0)); + link->SetMap(file_map_create(volume->ID(), link->ID())); // The following call will have to write the inode back, so // we don't have to do that here... @@ -1067,9 +1105,6 @@ bfs_rename(void *_ns, void *_oldDir, const char *oldName, void *_newDir, const c } -/** Opens the file with the specified mode. - */ - static status_t bfs_open(void *_fs, void *_node, int openMode, void **_cookie) { @@ -1133,12 +1168,9 @@ bfs_open(void *_fs, void *_node, int openMode, void **_cookie) } -/** Read a file specified by node, using information in cookie - * and at offset specified by pos. read len bytes into buffer buf. - */ - static status_t -bfs_read(void *_ns, void *_node, void *_cookie, off_t pos, void *buffer, size_t *_length) +bfs_read(void *_ns, void *_node, void *_cookie, off_t pos, void *buffer, + size_t *_length) { //FUNCTION(); Inode *inode = (Inode *)_node; @@ -1148,19 +1180,15 @@ bfs_read(void *_ns, void *_node, void *_cookie, off_t pos, void *buffer, size_t RETURN_ERROR(B_BAD_VALUE); } - ReadLocked locked(inode->Lock()); return inode->ReadAt(pos, (uint8 *)buffer, _length); } static status_t -bfs_write(void *_ns, void *_node, void *_cookie, off_t pos, const void *buffer, size_t *_length) +bfs_write(void *_ns, void *_node, void *_cookie, off_t pos, const void *buffer, + size_t *_length) { //FUNCTION(); - // uncomment to be more robust against a buggy vnode layer ;-) - //if (_ns == NULL || _node == NULL || _cookie == NULL) - // return B_BAD_VALUE; - Volume *volume = (Volume *)_ns; Inode *inode = (Inode *)_node; @@ -1174,21 +1202,20 @@ bfs_write(void *_ns, void *_node, void *_cookie, off_t pos, const void *buffer, if (cookie->open_mode & O_APPEND) pos = inode->Size(); - WriteLocked locked(inode->Lock()); - if (locked.IsLocked() < B_OK) - RETURN_ERROR(B_ERROR); - Transaction transaction; // We are not starting the transaction here, since // it might not be needed at all (the contents of // regular files aren't logged) - status_t status = inode->WriteAt(transaction, pos, (const uint8 *)buffer, _length); + status_t status = inode->WriteAt(transaction, pos, (const uint8 *)buffer, + _length); if (status == B_OK) transaction.Done(); if (status == B_OK) { + ReadLocked locker(inode->Lock()); + // periodically notify if the file size has changed // ToDo: should we better test for a change in the last_modified time only? if (!inode->IsDeleted() && cookie->last_size != inode->Size() diff --git a/src/add-ons/kernel/file_systems/cdda/kernel_interface.cpp b/src/add-ons/kernel/file_systems/cdda/kernel_interface.cpp index cbb9f8d780..b17f0b640b 100644 --- a/src/add-ons/kernel/file_systems/cdda/kernel_interface.cpp +++ b/src/add-ons/kernel/file_systems/cdda/kernel_interface.cpp @@ -1574,8 +1574,7 @@ cdda_can_page(fs_volume _volume, fs_vnode _v, fs_cookie cookie) static status_t cdda_read_pages(fs_volume _volume, fs_vnode _v, fs_cookie cookie, off_t pos, - const iovec *vecs, size_t count, size_t *_numBytes, bool mayBlock, - bool reenter) + const iovec *vecs, size_t count, size_t *_numBytes, bool reenter) { return B_NOT_ALLOWED; } @@ -1583,8 +1582,7 @@ cdda_read_pages(fs_volume _volume, fs_vnode _v, fs_cookie cookie, off_t pos, static status_t cdda_write_pages(fs_volume _volume, fs_vnode _v, fs_cookie cookie, off_t pos, - const iovec *vecs, size_t count, size_t *_numBytes, bool mayBlock, - bool reenter) + const iovec *vecs, size_t count, size_t *_numBytes, bool reenter) { return B_NOT_ALLOWED; } diff --git a/src/add-ons/kernel/file_systems/fat/dir.c b/src/add-ons/kernel/file_systems/fat/dir.c index 8931405785..6f91779515 100644 --- a/src/add-ons/kernel/file_systems/fat/dir.c +++ b/src/add-ons/kernel/file_systems/fat/dir.c @@ -1038,8 +1038,9 @@ dosfs_read_vnode(void *_vol, ino_t vnid, void **_node, bool reenter) entry->filename = malloc(sizeof(filename) + 1); if (entry->filename) strcpy(entry->filename, filename); #endif - entry->cache = file_cache_create(vol->id, vnid, entry->st_size, vol->fd); - if(!(entry->mode & FAT_SUBDIR)) + entry->cache = file_cache_create(vol->id, vnid, entry->st_size); + entry->file_map = file_map_create(vol->id, vnid); + if (!(entry->mode & FAT_SUBDIR)) set_mime_type(entry, filename); *_node = entry; diff --git a/src/add-ons/kernel/file_systems/fat/dosfs.h b/src/add-ons/kernel/file_systems/fat/dosfs.h index 9ed2b273e9..2af96326a8 100644 --- a/src/add-ons/kernel/file_systems/fat/dosfs.h +++ b/src/add-ons/kernel/file_systems/fat/dosfs.h @@ -65,12 +65,12 @@ typedef recursive_lock lock; #define VNODE_MAGIC 'treB' -typedef struct vnode -{ +typedef struct vnode { uint32 magic; ino_t vnid; // self id ino_t dir_vnid; // parent vnode id (directory containing entry) - void *cache; // for file cache + void *cache; + void *file_map; uint32 disk_image; // 0 = no, 1 = BEOS, 2 = IMAGE.BE diff --git a/src/add-ons/kernel/file_systems/fat/file.c b/src/add-ons/kernel/file_systems/fat/file.c index 79ed6b0861..f2791633c0 100644 --- a/src/add-ons/kernel/file_systems/fat/file.c +++ b/src/add-ons/kernel/file_systems/fat/file.c @@ -141,8 +141,8 @@ dosfs_release_vnode(void *_vol, void *_node, bool reenter) if (node->vnid != vol->root_vnode.vnid) { node->magic = ~VNODE_MAGIC; // munge magic number to be safe - if (node->cache != NULL) - file_cache_delete(node->cache); + file_cache_delete(node->cache); + file_map_delete(node->file_map); free(node); } } @@ -245,6 +245,7 @@ dosfs_wstat(void *_vol, void *_node, const struct stat *st, uint32 mask) node->iteration++; dirty = true; file_cache_set_size(node->cache, node->st_size); + file_map_set_size(node->file_map, node->st_size); } } } @@ -393,7 +394,7 @@ dosfs_read(void *_vol, void *_node, void *_cookie, off_t pos, if (pos + *len >= node->st_size) *len = node->st_size - pos; - result = file_cache_read(node->cache, pos, buf, len); + result = file_cache_read(node->cache, cookie, pos, buf, len); #if 0 @@ -595,9 +596,10 @@ dosfs_write(void *_vol, void *_node, void *_cookie, off_t pos, DPRINTF(0, ("setting file size to %Lx (%lx clusters)\n", node->st_size, clusters)); node->dirty = true; file_cache_set_size(node->cache, node->st_size); + file_map_set_size(node->file_map, node->st_size); } - result = file_cache_write(node->cache, pos, buf, len); + result = file_cache_write(node->cache, cookie, pos, buf, len); #if 0 if (cluster1 == 0xffffffff) { @@ -1479,11 +1481,13 @@ dosfs_can_page(fs_volume _fs, fs_vnode _v, fs_cookie _cookie) status_t dosfs_read_pages(fs_volume _fs, fs_vnode _node, fs_cookie _cookie, off_t pos, - const iovec *vecs, size_t count, size_t *_numBytes, bool mayBlock, - bool reenter) + const iovec *vecs, size_t count, size_t *_numBytes, bool reenter) { nspace *vol = (nspace *)_fs; vnode *node = (vnode *)_node; + uint32 vecIndex = 0; + size_t vecOffset = 0; + size_t bytesLeft = *_numBytes; status_t status; if (check_nspace_magic(vol, "dosfs_read_pages") @@ -1493,23 +1497,49 @@ dosfs_read_pages(fs_volume _fs, fs_vnode _node, fs_cookie _cookie, off_t pos, if (node->cache == NULL) return(B_BAD_VALUE); - // TODO: respect "mayBlock"! - LOCK_VOL(vol); - status = file_cache_read_pages(node->cache, pos, vecs, count, - _numBytes); - UNLOCK_VOL(vol); + if (!reenter) { + LOCK_VOL(vol); + } - return status; + while (true) { + struct file_io_vec fileVecs[8]; + uint32 fileVecCount = 8; + bool bufferOverflow; + size_t bytes; + + status = file_map_translate(node->file_map, pos, bytesLeft, fileVecs, + &fileVecCount); + if (status != B_OK && status != B_BUFFER_OVERFLOW) + break; + + bufferOverflow = status == B_BUFFER_OVERFLOW; + + status = read_file_io_vec_pages(vol->fd, fileVecs, + fileVecCount, vecs, count, &vecIndex, &vecOffset, &bytes); + if (status != B_OK || !bufferOverflow) + break; + + pos += bytes; + bytesLeft -= bytes; + } + + if (!reenter) { + UNLOCK_VOL(vol); + } + + return B_ERROR; } status_t dosfs_write_pages(fs_volume _fs, fs_vnode _node, fs_cookie _cookie, off_t pos, - const iovec *vecs, size_t count, size_t *_numBytes, bool mayBlock, - bool reenter) + const iovec *vecs, size_t count, size_t *_numBytes, bool reenter) { nspace *vol = (nspace *)_fs; vnode *node = (vnode *)_node; + uint32 vecIndex = 0; + size_t vecOffset = 0; + size_t bytesLeft = *_numBytes; status_t status; if (check_nspace_magic(vol, "dosfs_write_pages") @@ -1519,19 +1549,43 @@ dosfs_write_pages(fs_volume _fs, fs_vnode _node, fs_cookie _cookie, off_t pos, if (node->cache == NULL) return B_BAD_VALUE; - // TODO: respect "mayBlock"! - LOCK_VOL(vol); - status = file_cache_write_pages(node->cache, pos, vecs, count, - _numBytes); - UNLOCK_VOL(vol); + if (!reenter) { + LOCK_VOL(vol); + } - return status; + while (true) { + struct file_io_vec fileVecs[8]; + uint32 fileVecCount = 8; + bool bufferOverflow; + size_t bytes; + + status = file_map_translate(node->file_map, pos, bytesLeft, fileVecs, + &fileVecCount); + if (status != B_OK && status != B_BUFFER_OVERFLOW) + break; + + bufferOverflow = status == B_BUFFER_OVERFLOW; + + status = write_file_io_vec_pages(vol->fd, fileVecs, + fileVecCount, vecs, count, &vecIndex, &vecOffset, &bytes); + if (status != B_OK || !bufferOverflow) + break; + + pos += bytes; + bytesLeft -= bytes; + } + + if (!reenter) { + UNLOCK_VOL(vol); + } + + return B_ERROR; } status_t dosfs_get_file_map(void *_fs, void *_node, off_t pos, size_t len, - struct file_io_vec *vecs, size_t *_count) + struct file_io_vec *vecs, size_t *_count) { nspace *vol = (nspace *)_fs; vnode *node = (vnode *)_node; diff --git a/src/add-ons/kernel/file_systems/fat/file.h b/src/add-ons/kernel/file_systems/fat/file.h index 900b3117b1..e61c64494a 100644 --- a/src/add-ons/kernel/file_systems/fat/file.h +++ b/src/add-ons/kernel/file_systems/fat/file.h @@ -31,8 +31,8 @@ status_t dosfs_get_file_map(void *fs, void *node, off_t pos, size_t reqLen, struct file_io_vec *vecs, size_t *_count); bool dosfs_can_page(fs_volume _fs, fs_vnode _v, fs_cookie _cookie); status_t dosfs_read_pages(fs_volume _fs, fs_vnode _node, fs_cookie _cookie, off_t pos, - const iovec *vecs, size_t count, size_t *_numBytes, bool mayBlock, bool reenter); + const iovec *vecs, size_t count, size_t *_numBytes, bool reenter); status_t dosfs_write_pages(fs_volume _fs, fs_vnode _node, fs_cookie _cookie, off_t pos, - const iovec *vecs, size_t count, size_t *_numBytes, bool mayBlock, bool reenter); + const iovec *vecs, size_t count, size_t *_numBytes, bool reenter); #endif diff --git a/src/add-ons/kernel/file_systems/iso9660/kernel_interface.cpp b/src/add-ons/kernel/file_systems/iso9660/kernel_interface.cpp index 255a479818..2f2da505b8 100644 --- a/src/add-ons/kernel/file_systems/iso9660/kernel_interface.cpp +++ b/src/add-ons/kernel/file_systems/iso9660/kernel_interface.cpp @@ -462,7 +462,8 @@ fs_read_vnode(void *_ns, ino_t vnid, void **node, bool reenter) result = ENOMEM; if (result == B_OK && !(newNode->flags & ISO_ISDIR)) { - newNode->cache = file_cache_create(ns->id, vnid, newNode->dataLen[FS_DATA_FORMAT], ns->fdOfSession); + newNode->cache = file_cache_create(ns->id, vnid, + newNode->dataLen[FS_DATA_FORMAT]); } TRACE(("fs_read_vnode - EXIT, result is %s\n", strerror(result))); @@ -567,7 +568,7 @@ fs_get_file_map(fs_volume _fs, fs_vnode _node, off_t pos, size_t reqLen, // Read in the middle blocks. if (numBlocks > 0) { - for (int32 i=startBlock; i= file length, return length of 0. *len = 0; return B_OK; - } - return file_cache_read(node->cache, pos, buf, len); + } + return file_cache_read(node->cache, NULL, pos, buf, len); #endif } diff --git a/src/system/kernel/cache/Jamfile b/src/system/kernel/cache/Jamfile index 1b9613d403..1adf0cce9a 100644 --- a/src/system/kernel/cache/Jamfile +++ b/src/system/kernel/cache/Jamfile @@ -4,6 +4,7 @@ KernelMergeObject kernel_cache.o : block_allocator.cpp block_cache.cpp file_cache.cpp + file_map.cpp vnode_store.cpp : $(TARGET_KERNEL_PIC_CCFLAGS) -Wno-unused diff --git a/src/system/kernel/cache/file_cache.cpp b/src/system/kernel/cache/file_cache.cpp index e2248da534..cd6006d77e 100644 --- a/src/system/kernel/cache/file_cache.cpp +++ b/src/system/kernel/cache/file_cache.cpp @@ -35,42 +35,13 @@ // maximum number of iovecs per request #define MAX_IO_VECS 32 // 128 kB #define MAX_FILE_IO_VECS 32 -#define MAX_TEMP_IO_VECS 8 - -#define CACHED_FILE_EXTENTS 2 - // must be smaller than MAX_FILE_IO_VECS - // ToDo: find out how much of these are typically used #define BYPASS_IO_SIZE 65536 #define LAST_ACCESSES 3 -struct file_extent { - off_t offset; - file_io_vec disk; -}; - -struct file_map { - file_map(); - ~file_map(); - - file_extent *operator[](uint32 index); - file_extent *ExtentAt(uint32 index); - status_t Add(file_io_vec *vecs, size_t vecCount, off_t &lastOffset); - void Free(); - - union { - file_extent direct[CACHED_FILE_EXTENTS]; - file_extent *array; - }; - size_t count; -}; - struct file_cache_ref { vm_cache *cache; struct vnode *vnode; - struct vnode *device; - void *cookie; - file_map map; off_t last_access[LAST_ACCESSES]; // TODO: it would probably be enough to only store the least // significant 31 bits, and make this uint32 (one bit for @@ -79,7 +50,7 @@ struct file_cache_ref { bool last_access_was_write; }; -typedef status_t (*cache_func)(file_cache_ref *ref, off_t offset, +typedef status_t (*cache_func)(file_cache_ref *ref, void *cookie, off_t offset, int32 pageOffset, addr_t buffer, size_t bufferSize, size_t lastReservedPages, size_t reservePages); @@ -87,101 +58,6 @@ typedef status_t (*cache_func)(file_cache_ref *ref, off_t offset, static struct cache_module_info *sCacheModule; -file_map::file_map() -{ - array = NULL; - count = 0; -} - - -file_map::~file_map() -{ - Free(); -} - - -file_extent * -file_map::operator[](uint32 index) -{ - return ExtentAt(index); -} - - -file_extent * -file_map::ExtentAt(uint32 index) -{ - if (index >= count) - return NULL; - - if (count > CACHED_FILE_EXTENTS) - return &array[index]; - - return &direct[index]; -} - - -status_t -file_map::Add(file_io_vec *vecs, size_t vecCount, off_t &lastOffset) -{ - TRACE(("file_map::Add(vecCount = %ld)\n", vecCount)); - - off_t offset = 0; - - if (vecCount <= CACHED_FILE_EXTENTS && count == 0) { - // just use the reserved area in the file_cache_ref structure - } else { - // TODO: once we can invalidate only parts of the file map, - // we might need to copy the previously cached file extends - // from the direct range - file_extent *newMap = (file_extent *)realloc(array, - (count + vecCount) * sizeof(file_extent)); - if (newMap == NULL) - return B_NO_MEMORY; - - array = newMap; - - if (count != 0) { - file_extent *extent = ExtentAt(count - 1); - offset = extent->offset + extent->disk.length; - } - } - - int32 start = count; - count += vecCount; - - for (uint32 i = 0; i < vecCount; i++) { - file_extent *extent = ExtentAt(start + i); - - extent->offset = offset; - extent->disk = vecs[i]; - - offset += extent->disk.length; - } - -#ifdef TRACE_FILE_CACHE - for (uint32 i = 0; i < count; i++) { - file_extent *extent = ExtentAt(i); - dprintf("[%ld] extend offset %Ld, disk offset %Ld, length %Ld\n", - i, extent->offset, extent->disk.offset, extent->disk.length); - } -#endif - - lastOffset = offset; - return B_OK; -} - - -void -file_map::Free() -{ - if (count > CACHED_FILE_EXTENTS) - free(array); - - array = NULL; - count = 0; -} - - // #pragma mark - @@ -205,119 +81,6 @@ add_to_iovec(iovec *vecs, int32 &index, int32 max, addr_t address, size_t size) } -static file_extent * -find_file_extent(file_cache_ref *ref, off_t offset, uint32 *_index) -{ - // TODO: do binary search - - for (uint32 index = 0; index < ref->map.count; index++) { - file_extent *extent = ref->map[index]; - - if (extent->offset <= offset - && extent->offset + extent->disk.length > offset) { - if (_index) - *_index = index; - return extent; - } - } - - return NULL; -} - - -static status_t -get_file_map(file_cache_ref *ref, off_t offset, size_t size, - file_io_vec *vecs, size_t *_count) -{ - size_t maxVecs = *_count; - status_t status = B_OK; - - if (ref->map.count == 0) { - // we don't yet have the map of this file, so let's grab it - // (ordered by offset, so that we can do a binary search on them) - - MutexLocker _(ref->cache->lock); - - // the file map could have been requested in the mean time - if (ref->map.count == 0) { - size_t vecCount = maxVecs; - off_t mapOffset = 0; - - while (true) { - status = vfs_get_file_map(ref->vnode, mapOffset, ~0UL, vecs, - &vecCount); - if (status < B_OK && status != B_BUFFER_OVERFLOW) - return status; - - status_t addStatus = ref->map.Add(vecs, vecCount, mapOffset); - if (addStatus != B_OK) { - // only clobber the status in case of failure - status = addStatus; - } - - if (status != B_BUFFER_OVERFLOW) - break; - - // when we are here, the map has been stored in the array, and - // the array size was still too small to cover the whole file - vecCount = maxVecs; - } - } - } - - if (status != B_OK) { - // We must invalidate the (part of the) map we already - // have, as we cannot know if it's complete or not - ref->map.Free(); - return status; - } - - // We now have cached the map of this file, we now need to - // translate it for the requested access. - - uint32 index; - file_extent *fileExtent = find_file_extent(ref, offset, &index); - if (fileExtent == NULL) { - // access outside file bounds? But that's not our problem - *_count = 0; - return B_OK; - } - - offset -= fileExtent->offset; - vecs[0].offset = fileExtent->disk.offset + offset; - vecs[0].length = fileExtent->disk.length - offset; - - if (vecs[0].length >= size || index >= ref->map.count - 1) { - *_count = 1; - return B_OK; - } - - // copy the rest of the vecs - - size -= vecs[0].length; - - for (index = 1; index < ref->map.count;) { - fileExtent++; - - vecs[index] = fileExtent->disk; - index++; - - if (size <= fileExtent->disk.length) - break; - - if (index >= maxVecs) { - *_count = index; - return B_BUFFER_OVERFLOW; - } - - size -= fileExtent->disk.length; - } - - *_count = index; - return B_OK; -} - - static inline bool access_is_sequential(file_cache_ref *ref) { @@ -397,212 +160,6 @@ reserve_pages(file_cache_ref *ref, size_t reservePages, bool isWrite) } -/*! - Does the dirty work of translating the request into actual disk offsets - and reads to or writes from the supplied iovecs as specified by \a doWrite. -*/ -static status_t -pages_io(file_cache_ref *ref, off_t offset, const iovec *vecs, size_t count, - size_t *_numBytes, bool doWrite) -{ - TRACE(("pages_io: ref = %p, offset = %Ld, size = %lu, vecCount = %lu, %s\n", - ref, offset, *_numBytes, count, doWrite ? "write" : "read")); - - // translate the iovecs into direct device accesses - file_io_vec fileVecs[MAX_FILE_IO_VECS]; - size_t fileVecCount = MAX_FILE_IO_VECS; - size_t numBytes = *_numBytes; - - push_access(ref, offset, numBytes, doWrite); - - status_t status = get_file_map(ref, offset, numBytes, fileVecs, - &fileVecCount); - if (status < B_OK && status != B_BUFFER_OVERFLOW) { - TRACE(("get_file_map(offset = %Ld, numBytes = %lu) failed: %s\n", - offset, numBytes, strerror(status))); - return status; - } - - bool bufferOverflow = status == B_BUFFER_OVERFLOW; - -#ifdef TRACE_FILE_CACHE - dprintf("got %lu file vecs for %Ld:%lu%s:\n", fileVecCount, offset, - numBytes, bufferOverflow ? " (array too small)" : ""); - for (size_t i = 0; i < fileVecCount; i++) { - dprintf(" [%lu] offset = %Ld, size = %Ld\n", - i, fileVecs[i].offset, fileVecs[i].length); - } -#endif - - if (fileVecCount == 0) { - // There are no file vecs at this offset, so we're obviously trying - // to access the file outside of its bounds - TRACE(("pages_io: access outside of vnode %p at offset %Ld\n", - ref->vnode, offset)); - return B_BAD_VALUE; - } - - uint32 fileVecIndex; - size_t size; - - if (!doWrite) { - // now directly read the data from the device - // the first file_io_vec can be read directly - - size = fileVecs[0].length; - if (size > numBytes) - size = numBytes; - - status = vfs_read_pages(ref->device, ref->cookie, fileVecs[0].offset, - vecs, count, &size, true, false); - if (status < B_OK) - return status; - - // TODO: this is a work-around for buggy device drivers! - // When our own drivers honour the length, we can: - // a) also use this direct I/O for writes (otherwise, it would - // overwrite precious data) - // b) panic if the term below is true (at least for writes) - if (size > fileVecs[0].length) { - //dprintf("warning: device driver %p doesn't respect total length in read_pages() call!\n", ref->device); - size = fileVecs[0].length; - } - - ASSERT(size <= fileVecs[0].length); - - // If the file portion was contiguous, we're already done now - if (size == numBytes) - return B_OK; - - // if we reached the end of the file, we can return as well - if (size != fileVecs[0].length) { - *_numBytes = size; - return B_OK; - } - - fileVecIndex = 1; - } else { - fileVecIndex = 0; - size = 0; - } - - // Too bad, let's process the rest of the file_io_vecs - - size_t totalSize = size; - - // first, find out where we have to continue in our iovecs - uint32 i = 0; - for (; i < count; i++) { - if (size < vecs[i].iov_len) - break; - - size -= vecs[i].iov_len; - } - - size_t vecOffset = size; - size_t bytesLeft = numBytes - size; - - while (true) { - for (; fileVecIndex < fileVecCount; fileVecIndex++) { - file_io_vec &fileVec = fileVecs[fileVecIndex]; - off_t fileOffset = fileVec.offset; - off_t fileLeft = min_c(fileVec.length, bytesLeft); - - TRACE(("FILE VEC [%lu] length %Ld\n", fileVecIndex, fileLeft)); - - // process the complete fileVec - while (fileLeft > 0) { - iovec tempVecs[MAX_TEMP_IO_VECS]; - uint32 tempCount = 0; - - // size tracks how much of what is left of the current fileVec - // (fileLeft) has been assigned to tempVecs - size = 0; - - // assign what is left of the current fileVec to the tempVecs - for (size = 0; size < fileLeft && i < count - && tempCount < MAX_TEMP_IO_VECS;) { - // try to satisfy one iovec per iteration (or as much as - // possible) - - // bytes left of the current iovec - size_t vecLeft = vecs[i].iov_len - vecOffset; - if (vecLeft == 0) { - vecOffset = 0; - i++; - continue; - } - - TRACE(("fill vec %ld, offset = %lu, size = %lu\n", - i, vecOffset, size)); - - // actually available bytes - size_t tempVecSize = min_c(vecLeft, fileLeft - size); - - tempVecs[tempCount].iov_base - = (void *)((addr_t)vecs[i].iov_base + vecOffset); - tempVecs[tempCount].iov_len = tempVecSize; - tempCount++; - - size += tempVecSize; - vecOffset += tempVecSize; - } - - size_t bytes = size; - if (doWrite) { - status = vfs_write_pages(ref->device, ref->cookie, - fileOffset, tempVecs, tempCount, &bytes, true, false); - } else { - status = vfs_read_pages(ref->device, ref->cookie, - fileOffset, tempVecs, tempCount, &bytes, true, false); - } - if (status < B_OK) - return status; - - totalSize += bytes; - bytesLeft -= size; - fileOffset += size; - fileLeft -= size; - //dprintf("-> file left = %Lu\n", fileLeft); - - if (size != bytes || i >= count) { - // there are no more bytes or iovecs, let's bail out - *_numBytes = totalSize; - return B_OK; - } - } - } - - if (bufferOverflow) { - status = get_file_map(ref, offset + totalSize, bytesLeft, fileVecs, - &fileVecCount); - if (status < B_OK && status != B_BUFFER_OVERFLOW) { - TRACE(("get_file_map(offset = %Ld, numBytes = %lu) failed: %s\n", - offset, numBytes, strerror(status))); - return status; - } - - bufferOverflow = status == B_BUFFER_OVERFLOW; - fileVecIndex = 0; - -#ifdef TRACE_FILE_CACHE - dprintf("got %lu file vecs for %Ld:%lu%s:\n", fileVecCount, - offset + totalSize, numBytes, - bufferOverflow ? " (array too small)" : ""); - for (size_t i = 0; i < fileVecCount; i++) { - dprintf(" [%lu] offset = %Ld, size = %Ld\n", - i, fileVecs[i].offset, fileVecs[i].length); - } -#endif - } else - break; - } - - *_numBytes = totalSize; - return B_OK; -} - - /*! Reads the requested amount of data into the cache, and allocates pages needed to fulfill that request. This function is called by cache_io(). It can only handle a certain amount of bytes, and the caller must make @@ -611,9 +168,9 @@ pages_io(file_cache_ref *ref, off_t offset, const iovec *vecs, size_t count, operation it will unlock the cache, though. */ static status_t -read_into_cache(file_cache_ref *ref, off_t offset, int32 pageOffset, - addr_t buffer, size_t bufferSize, size_t lastReservedPages, - size_t reservePages) +read_into_cache(file_cache_ref *ref, void *cookie, off_t offset, + int32 pageOffset, addr_t buffer, size_t bufferSize, + size_t lastReservedPages, size_t reservePages) { TRACE(("read_into_cache(offset = %Ld, pageOffset = %ld, buffer = %#lx, " "bufferSize = %lu\n", offset, pageOffset, buffer, bufferSize)); @@ -650,11 +207,13 @@ read_into_cache(file_cache_ref *ref, off_t offset, int32 pageOffset, // TODO: check if the array is large enough (currently panics)! } + push_access(ref, offset, bufferSize, false); mutex_unlock(&cache->lock); vm_page_unreserve_pages(lastReservedPages); // read file into reserved pages - status_t status = pages_io(ref, offset, vecs, vecCount, &numBytes, false); + status_t status = vfs_read_pages(ref->vnode, cookie, offset, vecs, + vecCount, &numBytes, false); if (status < B_OK) { // reading failed, free allocated pages @@ -718,18 +277,20 @@ read_into_cache(file_cache_ref *ref, off_t offset, int32 pageOffset, static status_t -read_from_file(file_cache_ref *ref, off_t offset, int32 pageOffset, - addr_t buffer, size_t bufferSize, size_t lastReservedPages, - size_t reservePages) +read_from_file(file_cache_ref *ref, void *cookie, off_t offset, + int32 pageOffset, addr_t buffer, size_t bufferSize, + size_t lastReservedPages, size_t reservePages) { iovec vec; vec.iov_base = (void *)buffer; vec.iov_len = bufferSize; + push_access(ref, offset, bufferSize, false); mutex_unlock(&ref->cache->lock); vm_page_unreserve_pages(lastReservedPages); - status_t status = pages_io(ref, offset, &vec, 1, &bufferSize, false); + status_t status = vfs_read_pages(ref->vnode, cookie, offset, &vec, 1, + &bufferSize, false); if (status == B_OK) reserve_pages(ref, reservePages, false); @@ -745,9 +306,9 @@ read_from_file(file_cache_ref *ref, off_t offset, int32 pageOffset, The same restrictions apply. */ static status_t -write_to_cache(file_cache_ref *ref, off_t offset, int32 pageOffset, - addr_t buffer, size_t bufferSize, size_t lastReservedPages, - size_t reservePages) +write_to_cache(file_cache_ref *ref, void *cookie, off_t offset, + int32 pageOffset, addr_t buffer, size_t bufferSize, + size_t lastReservedPages, size_t reservePages) { // TODO: We're using way too much stack! Rather allocate a sufficiently // large chunk on the heap. @@ -783,6 +344,7 @@ write_to_cache(file_cache_ref *ref, off_t offset, int32 pageOffset, // ToDo: check if the array is large enough! } + push_access(ref, offset, bufferSize, true); mutex_unlock(&ref->cache->lock); vm_page_unreserve_pages(lastReservedPages); @@ -794,10 +356,11 @@ write_to_cache(file_cache_ref *ref, off_t offset, int32 pageOffset, iovec readVec = { vecs[0].iov_base, B_PAGE_SIZE }; size_t bytesRead = B_PAGE_SIZE; - status = pages_io(ref, offset, &readVec, 1, &bytesRead, false); + status = vfs_read_pages(ref->vnode, cookie, offset, &readVec, 1, + &bytesRead, false); // ToDo: handle errors for real! if (status < B_OK) - panic("1. pages_io() failed: %s!\n", strerror(status)); + panic("1. vfs_read_pages() failed: %s!\n", strerror(status)); } addr_t lastPageOffset = (pageOffset + bufferSize) & (B_PAGE_SIZE - 1); @@ -816,11 +379,12 @@ write_to_cache(file_cache_ref *ref, off_t offset, int32 pageOffset, iovec readVec = { (void *)last, B_PAGE_SIZE }; size_t bytesRead = B_PAGE_SIZE; - status = pages_io(ref, PAGE_ALIGN(offset + pageOffset + bufferSize) - - B_PAGE_SIZE, &readVec, 1, &bytesRead, false); + status = vfs_read_pages(ref->vnode, cookie, + PAGE_ALIGN(offset + pageOffset + bufferSize) - B_PAGE_SIZE, + &readVec, 1, &bytesRead, false); // ToDo: handle errors for real! if (status < B_OK) - panic("pages_io() failed: %s!\n", strerror(status)); + panic("vfs_read_pages() failed: %s!\n", strerror(status)); if (bytesRead < B_PAGE_SIZE) { // the space beyond the file size needs to be cleaned @@ -846,8 +410,8 @@ write_to_cache(file_cache_ref *ref, off_t offset, int32 pageOffset, if (writeThrough) { // write cached pages back to the file if we were asked to do that - status_t status = pages_io(ref, offset, vecs, vecCount, &numBytes, - true); + status_t status = vfs_write_pages(ref->vnode, cookie, offset, vecs, + vecCount, &numBytes, false); if (status < B_OK) { // ToDo: remove allocated pages, ...? panic("file_cache: remove allocated pages! write pages failed: %s\n", @@ -886,7 +450,7 @@ write_to_cache(file_cache_ref *ref, off_t offset, int32 pageOffset, static status_t -write_to_file(file_cache_ref *ref, off_t offset, int32 pageOffset, +write_to_file(file_cache_ref *ref, void *cookie, off_t offset, int32 pageOffset, addr_t buffer, size_t bufferSize, size_t lastReservedPages, size_t reservePages) { @@ -894,10 +458,12 @@ write_to_file(file_cache_ref *ref, off_t offset, int32 pageOffset, vec.iov_base = (void *)buffer; vec.iov_len = bufferSize; + push_access(ref, offset, bufferSize, true); mutex_unlock(&ref->cache->lock); vm_page_unreserve_pages(lastReservedPages); - status_t status = pages_io(ref, offset, &vec, 1, &bufferSize, true); + status_t status = vfs_write_pages(ref->vnode, cookie, offset, &vec, 1, + &bufferSize, false); if (status == B_OK) reserve_pages(ref, reservePages, true); @@ -908,10 +474,10 @@ write_to_file(file_cache_ref *ref, off_t offset, int32 pageOffset, static inline status_t -satisfy_cache_io(file_cache_ref *ref, cache_func function, off_t offset, - addr_t buffer, int32 &pageOffset, size_t bytesLeft, size_t &reservePages, - off_t &lastOffset, addr_t &lastBuffer, int32 &lastPageOffset, - size_t &lastLeft, size_t &lastReservedPages) +satisfy_cache_io(file_cache_ref *ref, void *cookie, cache_func function, + off_t offset, addr_t buffer, int32 &pageOffset, size_t bytesLeft, + size_t &reservePages, off_t &lastOffset, addr_t &lastBuffer, + int32 &lastPageOffset, size_t &lastLeft, size_t &lastReservedPages) { if (lastBuffer == buffer) return B_OK; @@ -920,8 +486,8 @@ satisfy_cache_io(file_cache_ref *ref, cache_func function, off_t offset, reservePages = min_c(MAX_IO_VECS, (lastLeft - requestSize + lastPageOffset + B_PAGE_SIZE - 1) >> PAGE_SHIFT); - status_t status = function(ref, lastOffset, lastPageOffset, lastBuffer, - requestSize, lastReservedPages, reservePages); + status_t status = function(ref, cookie, lastOffset, lastPageOffset, + lastBuffer, requestSize, lastReservedPages, reservePages); if (status == B_OK) { lastReservedPages = reservePages; lastBuffer = buffer; @@ -935,8 +501,8 @@ satisfy_cache_io(file_cache_ref *ref, cache_func function, off_t offset, static status_t -cache_io(void *_cacheRef, off_t offset, addr_t buffer, size_t *_size, - bool doWrite) +cache_io(void *_cacheRef, void *cookie, off_t offset, addr_t buffer, + size_t *_size, bool doWrite) { if (_cacheRef == NULL) panic("cache_io() called with NULL ref!\n"); @@ -1005,9 +571,9 @@ cache_io(void *_cacheRef, off_t offset, addr_t buffer, size_t *_size, // in the near future, we need to satisfy the request of the pages // we didn't get yet (to make sure no one else interferes in the // mean time). - status_t status = satisfy_cache_io(ref, function, offset, buffer, - pageOffset, bytesLeft, reservePages, lastOffset, lastBuffer, - lastPageOffset, lastLeft, lastReservedPages); + status_t status = satisfy_cache_io(ref, cookie, function, offset, + buffer, pageOffset, bytesLeft, reservePages, lastOffset, + lastBuffer, lastPageOffset, lastLeft, lastReservedPages); if (status != B_OK) return status; @@ -1073,9 +639,9 @@ cache_io(void *_cacheRef, off_t offset, addr_t buffer, size_t *_size, offset += B_PAGE_SIZE; if (buffer - lastBuffer + lastPageOffset >= kMaxChunkSize) { - status_t status = satisfy_cache_io(ref, function, offset, buffer, - pageOffset, bytesLeft, reservePages, lastOffset, lastBuffer, - lastPageOffset, lastLeft, lastReservedPages); + status_t status = satisfy_cache_io(ref, cookie, function, offset, + buffer, pageOffset, bytesLeft, reservePages, lastOffset, + lastBuffer, lastPageOffset, lastLeft, lastReservedPages); if (status != B_OK) return status; } @@ -1083,8 +649,8 @@ cache_io(void *_cacheRef, off_t offset, addr_t buffer, size_t *_size, // fill the last remaining bytes of the request (either write or read) - return function(ref, lastOffset, lastPageOffset, lastBuffer, lastLeft, - lastReservedPages, 0); + return function(ref, cookie, lastOffset, lastPageOffset, lastBuffer, + lastLeft, lastReservedPages, 0); } @@ -1303,10 +869,10 @@ file_cache_init(void) extern "C" void * -file_cache_create(dev_t mountID, ino_t vnodeID, off_t size, int fd) +file_cache_create(dev_t mountID, ino_t vnodeID, off_t size) { - TRACE(("file_cache_create(mountID = %ld, vnodeID = %Ld, size = %Ld, " - "fd = %d)\n", mountID, vnodeID, size, fd)); + TRACE(("file_cache_create(mountID = %ld, vnodeID = %Ld, size = %Ld)\n", + mountID, vnodeID, size)); file_cache_ref *ref = new file_cache_ref; if (ref == NULL) @@ -1325,29 +891,19 @@ file_cache_create(dev_t mountID, ino_t vnodeID, off_t size, int fd) // use atomic_test_and_set(), and free the resources again // when that fails... - // Get the vnode of the underlying device - if (vfs_get_vnode_from_fd(fd, true, &ref->device) != B_OK) - goto err1; - - // We also need the cookie of the underlying device to properly access it - if (vfs_get_cookie_from_fd(fd, &ref->cookie) != B_OK) - goto err2; - // Get the vnode for the object // (note, this does not grab a reference to the node) if (vfs_lookup_vnode(mountID, vnodeID, &ref->vnode) != B_OK) - goto err2; + goto err1; // Gets (usually creates) the cache for the node if (vfs_get_vnode_cache(ref->vnode, &ref->cache, true) != B_OK) - goto err2; + goto err1; ref->cache->virtual_size = size; ((vnode_store *)ref->cache->store)->file_cache_ref = ref; return ref; -err2: - vfs_put_vnode(ref->device); err1: delete ref; return NULL; @@ -1365,7 +921,6 @@ file_cache_delete(void *_cacheRef) TRACE(("file_cache_delete(ref = %p)\n", ref)); vm_cache_release_ref(ref->cache); - vfs_put_vnode(ref->device); delete ref; } @@ -1380,7 +935,7 @@ file_cache_set_size(void *_cacheRef, off_t newSize) if (ref == NULL) return B_OK; - mutex_lock(&ref->cache->lock); + MutexLocker _(ref->cache->lock); off_t offset = ref->cache->virtual_size; off_t size = newSize; @@ -1390,12 +945,7 @@ file_cache_set_size(void *_cacheRef, off_t newSize) } else size = newSize - offset; - status_t status = vm_cache_resize(ref->cache, newSize); - mutex_unlock(&ref->cache->lock); - - file_cache_invalidate_file_map(_cacheRef, offset, size); - - return status; + return vm_cache_resize(ref->cache, newSize); } @@ -1411,51 +961,26 @@ file_cache_sync(void *_cacheRef) extern "C" status_t -file_cache_read_pages(void *_cacheRef, off_t offset, const iovec *vecs, - size_t count, size_t *_numBytes) -{ - file_cache_ref *ref = (file_cache_ref *)_cacheRef; - - return pages_io(ref, offset, vecs, count, _numBytes, false); -} - - -extern "C" status_t -file_cache_write_pages(void *_cacheRef, off_t offset, const iovec *vecs, - size_t count, size_t *_numBytes) -{ - file_cache_ref *ref = (file_cache_ref *)_cacheRef; - - status_t status = pages_io(ref, offset, vecs, count, _numBytes, true); - - TRACE(("file_cache_write_pages(ref = %p, offset = %Ld, vecs = %p, " - "count = %lu, bytes = %lu) = %ld\n", ref, offset, vecs, count, - *_numBytes, status)); - - return status; -} - - -extern "C" status_t -file_cache_read(void *_cacheRef, off_t offset, void *bufferBase, size_t *_size) -{ - file_cache_ref *ref = (file_cache_ref *)_cacheRef; - - TRACE(("file_cache_read(ref = %p, offset = %Ld, buffer = %p, size = %lu)\n", - ref, offset, bufferBase, *_size)); - - return cache_io(ref, offset, (addr_t)bufferBase, _size, false); -} - - -extern "C" status_t -file_cache_write(void *_cacheRef, off_t offset, const void *buffer, +file_cache_read(void *_cacheRef, void *cookie, off_t offset, void *buffer, size_t *_size) { file_cache_ref *ref = (file_cache_ref *)_cacheRef; - status_t status = cache_io(ref, offset, (addr_t)const_cast(buffer), - _size, true); + TRACE(("file_cache_read(ref = %p, offset = %Ld, buffer = %p, size = %lu)\n", + ref, offset, buffer, *_size)); + + return cache_io(ref, cookie, offset, (addr_t)buffer, _size, false); +} + + +extern "C" status_t +file_cache_write(void *_cacheRef, void *cookie, off_t offset, + const void *buffer, size_t *_size) +{ + file_cache_ref *ref = (file_cache_ref *)_cacheRef; + + status_t status = cache_io(ref, cookie, offset, + (addr_t)const_cast(buffer), _size, true); TRACE(("file_cache_write(ref = %p, offset = %Ld, buffer = %p, size = %lu)" " = %ld\n", ref, offset, buffer, *_size, status)); @@ -1463,18 +988,3 @@ file_cache_write(void *_cacheRef, off_t offset, const void *buffer, return status; } - -extern "C" status_t -file_cache_invalidate_file_map(void *_cacheRef, off_t offset, off_t size) -{ - file_cache_ref *ref = (file_cache_ref *)_cacheRef; - - // ToDo: honour offset/size parameters - - TRACE(("file_cache_invalidate_file_map(offset = %Ld, size = %Ld)\n", offset, - size)); - - MutexLocker _(ref->cache->lock); - ref->map.Free(); - return B_OK; -} diff --git a/src/system/kernel/cache/file_map.cpp b/src/system/kernel/cache/file_map.cpp index 9726f097ab..af951cb9f0 100644 --- a/src/system/kernel/cache/file_map.cpp +++ b/src/system/kernel/cache/file_map.cpp @@ -128,7 +128,7 @@ file_map::Add(file_io_vec *vecs, size_t vecCount, off_t &lastOffset) offset += extent->disk.length; } -#ifdef TRACE_FILE_CACHE +#ifdef TRACE_FILE_MAP for (uint32 i = 0; i < count; i++) { file_extent *extent = ExtentAt(i); dprintf("[%ld] extend offset %Ld, disk offset %Ld, length %Ld\n", @@ -210,9 +210,24 @@ file_map_delete(void *_map) } +extern "C" void +file_map_set_size(void *_map, off_t size) +{ + if (_map == NULL) + return; + + // TODO: honour offset/size parameters + file_map *map = (file_map *)_map; + map->Free(); +} + + extern "C" void file_map_invalidate(void *_map, off_t offset, off_t size) { + if (_map == NULL) + return; + // TODO: honour offset/size parameters file_map *map = (file_map *)_map; map->Free(); @@ -223,6 +238,9 @@ extern "C" status_t file_map_translate(void *_map, off_t offset, size_t size, file_io_vec *vecs, size_t *_count) { + if (_map == NULL) + return B_BAD_VALUE; + file_map &map = *(file_map *)_map; size_t maxVecs = *_count; status_t status = B_OK; diff --git a/src/system/kernel/cache/vnode_store.cpp b/src/system/kernel/cache/vnode_store.cpp index 0380ff50e4..11652a380a 100644 --- a/src/system/kernel/cache/vnode_store.cpp +++ b/src/system/kernel/cache/vnode_store.cpp @@ -42,13 +42,13 @@ store_has_page(struct vm_store *_store, off_t offset) static status_t store_read(struct vm_store *_store, off_t offset, const iovec *vecs, - size_t count, size_t *_numBytes, bool mayBlock, bool fsReenter) + size_t count, size_t *_numBytes, bool fsReenter) { vnode_store *store = (vnode_store *)_store; size_t bytesUntouched = *_numBytes; status_t status = vfs_read_pages(store->vnode, NULL, offset, vecs, count, - _numBytes, mayBlock, fsReenter); + _numBytes, fsReenter); bytesUntouched -= *_numBytes; @@ -75,11 +75,11 @@ store_read(struct vm_store *_store, off_t offset, const iovec *vecs, static status_t store_write(struct vm_store *_store, off_t offset, const iovec *vecs, - size_t count, size_t *_numBytes, bool mayBlock, bool fsReenter) + size_t count, size_t *_numBytes, bool fsReenter) { vnode_store *store = (vnode_store *)_store; return vfs_write_pages(store->vnode, NULL, offset, vecs, count, _numBytes, - mayBlock, fsReenter); + fsReenter); } diff --git a/src/system/kernel/fs/devfs.cpp b/src/system/kernel/fs/devfs.cpp index 33c97a0571..649fe920c0 100644 --- a/src/system/kernel/fs/devfs.cpp +++ b/src/system/kernel/fs/devfs.cpp @@ -1884,8 +1884,7 @@ devfs_can_page(fs_volume _fs, fs_vnode _vnode, fs_cookie cookie) static status_t devfs_read_pages(fs_volume _fs, fs_vnode _vnode, fs_cookie _cookie, off_t pos, - const iovec *vecs, size_t count, size_t *_numBytes, bool mayBlock, - bool reenter) + const iovec *vecs, size_t count, size_t *_numBytes, bool reenter) { struct devfs_vnode *vnode = (devfs_vnode *)_vnode; struct devfs_cookie *cookie = (struct devfs_cookie *)_cookie; @@ -1944,8 +1943,7 @@ devfs_read_pages(fs_volume _fs, fs_vnode _vnode, fs_cookie _cookie, off_t pos, static status_t devfs_write_pages(fs_volume _fs, fs_vnode _vnode, fs_cookie _cookie, off_t pos, - const iovec *vecs, size_t count, size_t *_numBytes, bool mayBlock, - bool reenter) + const iovec *vecs, size_t count, size_t *_numBytes, bool reenter) { struct devfs_vnode *vnode = (devfs_vnode *)_vnode; struct devfs_cookie *cookie = (struct devfs_cookie *)_cookie; diff --git a/src/system/kernel/fs/pipefs.cpp b/src/system/kernel/fs/pipefs.cpp index b94abc1d39..8ae0b9c583 100644 --- a/src/system/kernel/fs/pipefs.cpp +++ b/src/system/kernel/fs/pipefs.cpp @@ -1596,8 +1596,7 @@ pipefs_can_page(fs_volume _volume, fs_vnode _v, fs_cookie cookie) static status_t pipefs_read_pages(fs_volume _volume, fs_vnode _v, fs_cookie cookie, off_t pos, - const iovec *vecs, size_t count, size_t *_numBytes, bool mayBlock, - bool reenter) + const iovec *vecs, size_t count, size_t *_numBytes, bool reenter) { return B_NOT_ALLOWED; } @@ -1605,8 +1604,7 @@ pipefs_read_pages(fs_volume _volume, fs_vnode _v, fs_cookie cookie, off_t pos, static status_t pipefs_write_pages(fs_volume _volume, fs_vnode _v, fs_cookie cookie, off_t pos, - const iovec *vecs, size_t count, size_t *_numBytes, bool mayBlock, - bool reenter) + const iovec *vecs, size_t count, size_t *_numBytes, bool reenter) { return B_NOT_ALLOWED; } diff --git a/src/system/kernel/fs/rootfs.cpp b/src/system/kernel/fs/rootfs.cpp index 1cfe8ef11c..e001dd71d7 100644 --- a/src/system/kernel/fs/rootfs.cpp +++ b/src/system/kernel/fs/rootfs.cpp @@ -787,8 +787,7 @@ rootfs_can_page(fs_volume _fs, fs_vnode _v, fs_cookie cookie) static status_t rootfs_read_pages(fs_volume _fs, fs_vnode _v, fs_cookie cookie, off_t pos, - const iovec *vecs, size_t count, size_t *_numBytes, bool mayBlock, - bool reenter) + const iovec *vecs, size_t count, size_t *_numBytes, bool reenter) { return B_NOT_ALLOWED; } @@ -796,8 +795,7 @@ rootfs_read_pages(fs_volume _fs, fs_vnode _v, fs_cookie cookie, off_t pos, static status_t rootfs_write_pages(fs_volume _fs, fs_vnode _v, fs_cookie cookie, off_t pos, - const iovec *vecs, size_t count, size_t *_numBytes, bool mayBlock, - bool reenter) + const iovec *vecs, size_t count, size_t *_numBytes, bool reenter) { return B_NOT_ALLOWED; } diff --git a/src/system/kernel/fs/vfs.cpp b/src/system/kernel/fs/vfs.cpp index d762699914..78d6135ee4 100644 --- a/src/system/kernel/fs/vfs.cpp +++ b/src/system/kernel/fs/vfs.cpp @@ -191,6 +191,8 @@ static struct vnode *sRoot; static hash_table *sMountsTable; static dev_t sNextMountID = 1; +#define MAX_TEMP_IO_VECS 8 + mode_t __gUmask = 022; /* function declarations */ @@ -2567,6 +2569,161 @@ dump_vnode_usage(int argc, char **argv) #endif // ADD_DEBUGGER_COMMANDS +/*! Does the dirty work of combining the file_io_vecs with the iovecs + and calls the file system hooks to read/write the request to disk. +*/ +static status_t +common_file_io_vec_pages(struct vnode *vnode, void *cookie, + const file_io_vec *fileVecs, size_t fileVecCount, const iovec *vecs, + size_t vecCount, uint32 *_vecIndex, size_t *_vecOffset, size_t *_numBytes, + bool doWrite) +{ + if (fileVecCount == 0) { + // There are no file vecs at this offset, so we're obviously trying + // to access the file outside of its bounds + return B_BAD_VALUE; + } + + size_t numBytes = *_numBytes; + uint32 fileVecIndex; + size_t vecOffset = *_vecOffset; + uint32 vecIndex = *_vecIndex; + status_t status; + size_t size; + + if (!doWrite) { + // now directly read the data from the device + // the first file_io_vec can be read directly + + size = fileVecs[0].length; + if (size > numBytes) + size = numBytes; + + status = FS_CALL(vnode, read_pages)(vnode->mount->cookie, + vnode->private_node, cookie, fileVecs[0].offset, vecs, vecCount, + &size, false); + if (status < B_OK) + return status; + + // TODO: this is a work-around for buggy device drivers! + // When our own drivers honour the length, we can: + // a) also use this direct I/O for writes (otherwise, it would + // overwrite precious data) + // b) panic if the term below is true (at least for writes) + if (size > fileVecs[0].length) { + //dprintf("warning: device driver %p doesn't respect total length in read_pages() call!\n", ref->device); + size = fileVecs[0].length; + } + + ASSERT(size <= fileVecs[0].length); + + // If the file portion was contiguous, we're already done now + if (size == numBytes) + return B_OK; + + // if we reached the end of the file, we can return as well + if (size != fileVecs[0].length) { + *_numBytes = size; + return B_OK; + } + + fileVecIndex = 1; + + // first, find out where we have to continue in our iovecs + for (; vecIndex < vecCount; vecIndex++) { + if (size < vecs[vecIndex].iov_len) + break; + + size -= vecs[vecIndex].iov_len; + } + } else { + fileVecIndex = 0; + size = 0; + } + + // Too bad, let's process the rest of the file_io_vecs + + size_t totalSize = size; + size_t bytesLeft = numBytes - size; + + for (; fileVecIndex < fileVecCount; fileVecIndex++) { + const file_io_vec &fileVec = fileVecs[fileVecIndex]; + off_t fileOffset = fileVec.offset; + off_t fileLeft = min_c(fileVec.length, bytesLeft); + + TRACE(("FILE VEC [%lu] length %Ld\n", fileVecIndex, fileLeft)); + + // process the complete fileVec + while (fileLeft > 0) { + iovec tempVecs[MAX_TEMP_IO_VECS]; + uint32 tempCount = 0; + + // size tracks how much of what is left of the current fileVec + // (fileLeft) has been assigned to tempVecs + size = 0; + + // assign what is left of the current fileVec to the tempVecs + for (size = 0; size < fileLeft && vecIndex < vecCount + && tempCount < MAX_TEMP_IO_VECS;) { + // try to satisfy one iovec per iteration (or as much as + // possible) + + // bytes left of the current iovec + size_t vecLeft = vecs[vecIndex].iov_len - vecOffset; + if (vecLeft == 0) { + vecOffset = 0; + vecIndex++; + continue; + } + + TRACE(("fill vec %ld, offset = %lu, size = %lu\n", + vecIndex, vecOffset, size)); + + // actually available bytes + size_t tempVecSize = min_c(vecLeft, fileLeft - size); + + tempVecs[tempCount].iov_base + = (void *)((addr_t)vecs[vecIndex].iov_base + vecOffset); + tempVecs[tempCount].iov_len = tempVecSize; + tempCount++; + + size += tempVecSize; + vecOffset += tempVecSize; + } + + size_t bytes = size; + if (doWrite) { + status = FS_CALL(vnode, write_pages)(vnode->mount->cookie, + vnode->private_node, cookie, fileOffset, tempVecs, + tempCount, &bytes, false); + } else { + status = FS_CALL(vnode, read_pages)(vnode->mount->cookie, + vnode->private_node, cookie, fileOffset, tempVecs, + tempCount, &bytes, false); + } + if (status < B_OK) + return status; + + totalSize += bytes; + bytesLeft -= size; + fileOffset += size; + fileLeft -= size; + //dprintf("-> file left = %Lu\n", fileLeft); + + if (size != bytes || vecIndex >= vecCount) { + // there are no more bytes or iovecs, let's bail out + *_numBytes = totalSize; + return B_OK; + } + } + } + + *_vecIndex = vecIndex; + *_vecOffset = vecOffset; + *_numBytes = totalSize; + return B_OK; +} + // #pragma mark - public API for file systems @@ -2736,6 +2893,88 @@ get_vnode_removed(dev_t mountID, ino_t vnodeID, bool* removed) } +extern "C" status_t +read_pages(int fd, off_t pos, const iovec *vecs, size_t count, + size_t *_numBytes, bool fsReenter) +{ + struct file_descriptor *descriptor; + struct vnode *vnode; + + descriptor = get_fd_and_vnode(fd, &vnode, true); + if (descriptor == NULL) + return B_FILE_ERROR; + + status_t status = FS_CALL(vnode, read_pages)(vnode->mount->cookie, + vnode->private_node, descriptor->cookie, pos, vecs, count, _numBytes, + fsReenter); + + put_fd(descriptor); + return status; +} + + +extern "C" status_t +write_pages(int fd, off_t pos, const iovec *vecs, size_t count, + size_t *_numBytes, bool fsReenter) +{ + struct file_descriptor *descriptor; + struct vnode *vnode; + + descriptor = get_fd_and_vnode(fd, &vnode, true); + if (descriptor == NULL) + return B_FILE_ERROR; + + status_t status = FS_CALL(vnode, write_pages)(vnode->mount->cookie, + vnode->private_node, descriptor->cookie, pos, vecs, count, _numBytes, + fsReenter); + + put_fd(descriptor); + return status; +} + + +extern "C" status_t +read_file_io_vec_pages(int fd, const file_io_vec *fileVecs, size_t fileVecCount, + const iovec *vecs, size_t vecCount, uint32 *_vecIndex, size_t *_vecOffset, + size_t *_bytes) +{ + struct file_descriptor *descriptor; + struct vnode *vnode; + + descriptor = get_fd_and_vnode(fd, &vnode, true); + if (descriptor == NULL) + return B_FILE_ERROR; + + status_t status = common_file_io_vec_pages(vnode, descriptor->cookie, + fileVecs, fileVecCount, vecs, vecCount, _vecIndex, _vecOffset, _bytes, + false); + + put_fd(descriptor); + return status; +} + + +extern "C" status_t +write_file_io_vec_pages(int fd, const file_io_vec *fileVecs, size_t fileVecCount, + const iovec *vecs, size_t vecCount, uint32 *_vecIndex, size_t *_vecOffset, + size_t *_bytes) +{ + struct file_descriptor *descriptor; + struct vnode *vnode; + + descriptor = get_fd_and_vnode(fd, &vnode, true); + if (descriptor == NULL) + return B_FILE_ERROR; + + status_t status = common_file_io_vec_pages(vnode, descriptor->cookie, + fileVecs, fileVecCount, vecs, vecCount, _vecIndex, _vecOffset, _bytes, + true); + + put_fd(descriptor); + return status; +} + + // #pragma mark - private VFS API // Functions the VFS exports for other parts of the kernel @@ -3135,23 +3374,23 @@ vfs_can_page(struct vnode *vnode, void *cookie) extern "C" status_t vfs_read_pages(struct vnode *vnode, void *cookie, off_t pos, const iovec *vecs, - size_t count, size_t *_numBytes, bool mayBlock, bool fsReenter) + size_t count, size_t *_numBytes, bool fsReenter) { FUNCTION(("vfs_read_pages: vnode %p, vecs %p, pos %Ld\n", vnode, vecs, pos)); return FS_CALL(vnode, read_pages)(vnode->mount->cookie, vnode->private_node, - cookie, pos, vecs, count, _numBytes, mayBlock, fsReenter); + cookie, pos, vecs, count, _numBytes, fsReenter); } extern "C" status_t vfs_write_pages(struct vnode *vnode, void *cookie, off_t pos, const iovec *vecs, - size_t count, size_t *_numBytes, bool mayBlock, bool fsReenter) + size_t count, size_t *_numBytes, bool fsReenter) { FUNCTION(("vfs_write_pages: vnode %p, vecs %p, pos %Ld\n", vnode, vecs, pos)); return FS_CALL(vnode, write_pages)(vnode->mount->cookie, vnode->private_node, - cookie, pos, vecs, count, _numBytes, mayBlock, fsReenter); + cookie, pos, vecs, count, _numBytes, fsReenter); } diff --git a/src/system/kernel/vm/vm.cpp b/src/system/kernel/vm/vm.cpp index 5e23c07a3e..8c8af7cea7 100644 --- a/src/system/kernel/vm/vm.cpp +++ b/src/system/kernel/vm/vm.cpp @@ -3880,7 +3880,7 @@ fault_find_page(vm_translation_map *map, vm_cache *topCache, // read it in status_t status = store->ops->read(store, cacheOffset, &vec, 1, - &bytesRead, true, false); + &bytesRead, false); map->ops->put_physical_page((addr_t)vec.iov_base); diff --git a/src/system/kernel/vm/vm_page.cpp b/src/system/kernel/vm/vm_page.cpp index 8cd7166476..366d9153f5 100644 --- a/src/system/kernel/vm/vm_page.cpp +++ b/src/system/kernel/vm/vm_page.cpp @@ -803,7 +803,7 @@ page_scrubber(void *unused) static status_t -write_page(vm_page *page, bool mayBlock, bool fsReenter) +write_page(vm_page *page, bool fsReenter) { vm_store *store = page->cache->store; size_t length = B_PAGE_SIZE; @@ -819,7 +819,7 @@ write_page(vm_page *page, bool mayBlock, bool fsReenter) vecs->iov_len = B_PAGE_SIZE; status = store->ops->write(store, (off_t)page->cache_offset << PAGE_SHIFT, - vecs, 1, &length, mayBlock, fsReenter); + vecs, 1, &length, fsReenter); vm_put_physical_page((addr_t)vecs[0].iov_base); #if 0 @@ -973,7 +973,7 @@ page_writer(void* /*unused*/) // TODO: put this as requests into the I/O scheduler status_t writeStatus[kNumPages]; for (uint32 i = 0; i < numPages; i++) { - writeStatus[i] = write_page(pages[i], false, false); + writeStatus[i] = write_page(pages[i], false); } // mark pages depending on whether they could be written or not @@ -1258,7 +1258,7 @@ vm_page_write_modified_pages(vm_cache *cache, bool fsReenter) vm_clear_map_flags(page, PAGE_MODIFIED); mutex_unlock(&cache->lock); - status_t status = write_page(page, true, fsReenter); + status_t status = write_page(page, fsReenter); mutex_lock(&cache->lock); InterruptsSpinLocker locker(&sPageLock); diff --git a/src/system/kernel/vm/vm_store_anonymous_noswap.cpp b/src/system/kernel/vm/vm_store_anonymous_noswap.cpp index c6e679d2fa..5aa52ba997 100644 --- a/src/system/kernel/vm/vm_store_anonymous_noswap.cpp +++ b/src/system/kernel/vm/vm_store_anonymous_noswap.cpp @@ -90,7 +90,7 @@ anonymous_has_page(struct vm_store *store, off_t offset) static status_t anonymous_read(struct vm_store *store, off_t offset, const iovec *vecs, - size_t count, size_t *_numBytes, bool mayBlock, bool fsReenter) + size_t count, size_t *_numBytes, bool fsReenter) { panic("anonymous_store: read called. Invalid!\n"); return B_ERROR; @@ -99,7 +99,7 @@ anonymous_read(struct vm_store *store, off_t offset, const iovec *vecs, static status_t anonymous_write(struct vm_store *store, off_t offset, const iovec *vecs, - size_t count, size_t *_numBytes, bool mayBlock, bool fsReenter) + size_t count, size_t *_numBytes, bool fsReenter) { // no place to write, this will cause the page daemon to skip this store return B_ERROR; diff --git a/src/system/kernel/vm/vm_store_device.c b/src/system/kernel/vm/vm_store_device.c index cca74f7ffb..92b7433993 100644 --- a/src/system/kernel/vm/vm_store_device.c +++ b/src/system/kernel/vm/vm_store_device.c @@ -46,7 +46,7 @@ device_has_page(struct vm_store *store, off_t offset) static status_t device_read(struct vm_store *store, off_t offset, const iovec *vecs, - size_t count, size_t *_numBytes, bool mayBlock, bool fsReenter) + size_t count, size_t *_numBytes, bool fsReenter) { panic("device_store: read called. Invalid!\n"); return B_ERROR; @@ -55,7 +55,7 @@ device_read(struct vm_store *store, off_t offset, const iovec *vecs, static status_t device_write(struct vm_store *store, off_t offset, const iovec *vecs, - size_t count, size_t *_numBytes, bool mayBlock, bool fsReenter) + size_t count, size_t *_numBytes, bool fsReenter) { // no place to write, this will cause the page daemon to skip this store return B_OK; diff --git a/src/system/kernel/vm/vm_store_null.c b/src/system/kernel/vm/vm_store_null.c index 883eafa908..90ef4d040c 100644 --- a/src/system/kernel/vm/vm_store_null.c +++ b/src/system/kernel/vm/vm_store_null.c @@ -36,7 +36,7 @@ null_has_page(struct vm_store *store, off_t offset) static status_t null_read(struct vm_store *store, off_t offset, const iovec *vecs, - size_t count, size_t *_numBytes, bool mayBlock, bool fsReenter) + size_t count, size_t *_numBytes, bool fsReenter) { return B_ERROR; } @@ -44,7 +44,7 @@ null_read(struct vm_store *store, off_t offset, const iovec *vecs, static status_t null_write(struct vm_store *store, off_t offset, const iovec *vecs, - size_t count, size_t *_numBytes, bool mayBlock, bool fsReenter) + size_t count, size_t *_numBytes, bool fsReenter) { return B_ERROR; } diff --git a/src/tools/fs_shell/Jamfile b/src/tools/fs_shell/Jamfile index fee8e3810e..ada90660de 100644 --- a/src/tools/fs_shell/Jamfile +++ b/src/tools/fs_shell/Jamfile @@ -39,6 +39,7 @@ BuildPlatformStaticLibrary fs_shell.a : fcntl.cpp fd.cpp file_cache.cpp + file_map.cpp kernel_export.cpp KPath.cpp hash.cpp diff --git a/src/tools/fs_shell/file_cache.cpp b/src/tools/fs_shell/file_cache.cpp index 7f1083401c..8fc5d5893e 100644 --- a/src/tools/fs_shell/file_cache.cpp +++ b/src/tools/fs_shell/file_cache.cpp @@ -37,56 +37,16 @@ using std::nothrow; // This is a hacked version of the kernel file cache implementation. The main // part of the implementation didn't change that much -- some code not needed -// in userland has been removed, file_cache_ref, vm_cache_ref and vm_cache -// have been unified, and the complete interface to the VM (vm_*()) and the -// VFS (vfs_*()) has been re-implemented to suit our needs. -// -// The PagePool class is a data structure used for managing the pages (vm_page) -// allocated and assigned to caches (file_cache_ref). It has a list for free -// pages, i.e. those that are not assigned to any cache and can be reused at -// once. A second list contains those pages that belong to a cache, but are -// not in use by any of the functions. These pages have a reference count of -// 0. They will be stolen from the owning cache when a usable page is needed, -// there are no free pages anymore, and the limit of pages that shall be used -// at maximum has already been reached. -// -// The only purpose of the page reference count (vm_page::ref_count) is to -// indicate whether the page is in use (i.e. known to one or more of the cache -// functions currently being executed). vm_page_get_page(), -// vm_page_allocate_page(), and vm_cache_lookup_page acquire a reference to -// the page, vm_page_put_page() releases a reference. - -// vm_page::state indicates in what state -// a page currently is. PAGE_STATE_FREE is only encountered for pages not -// belonging to a cache (being in page pool's free pages list, or just having -// been removed or not yet added). PAGE_STATE_ACTIVE/MODIFIED indicate that -// the page contains valid file data, in the latter case not yet written to -// disk. PAGE_STATE_BUSY means that the page is currently being manipulated by -// an operation, usually meaning that page data are being read from/written to -// disk. The required behavior when encountering a busy page, is to put the -// page, release the page pool and cache locks, wait a short time, and -// retry again later. -// -// Changing the page state requires having a reference to the page, -// holding the lock of the owning cache (if any) and the lock of the page pool -// (in case the page is not newly allocated). -// -// A page is in up to three lists. The free or unused list in the page pool -// (guarded by the page pool lock), the pages list of the cache the page -// belongs to, and the modified pages list of the cache (both cache lists -// are guarded by both the page pool and the cache lock). The modified pages -// list only contains PAGE_STATE_MODIFIED or PAGE_STATE_BUSY pages. - +// in userland has been removed, most notably everything on the page level. +// On the downside, the cache now never caches anything, but will always +// directly work on the underlying device. Since that is usually cached by the +// host operating system, it shouldn't hurt much, though. // maximum number of iovecs per request #define MAX_IO_VECS 64 // 256 kB #define MAX_FILE_IO_VECS 32 #define MAX_TEMP_IO_VECS 8 -#define CACHED_FILE_EXTENTS 2 - // must be smaller than MAX_FILE_IO_VECS - // ToDo: find out how much of these are typically used - #define user_memcpy(a, b, c) fssh_memcpy(a, b, c) #define PAGE_ALIGN(x) (((x) + (FSSH_B_PAGE_SIZE - 1)) & ~(FSSH_B_PAGE_SIZE - 1)) @@ -94,1489 +54,96 @@ using std::nothrow; namespace FSShell { -enum { - PAGE_STATE_ACTIVE = 0, - PAGE_STATE_BUSY, - PAGE_STATE_MODIFIED, - PAGE_STATE_FREE -}; - -enum { - PHYSICAL_PAGE_NO_WAIT = 0, - PHYSICAL_PAGE_CAN_WAIT, -}; - -struct vm_page; struct file_cache_ref; -typedef DoublyLinkedListLink page_link; - -// vm page -struct vm_page { - vm_page* next; - page_link link_cache; - page_link link_cache_modified; - page_link link_pool; - file_cache_ref* cache; - fssh_off_t offset; - void* data; - uint8_t state; - uint32_t ref_count; - - vm_page() - : next(NULL), - cache(NULL), - offset(0), - data(malloc(FSSH_B_PAGE_SIZE)), - state(PAGE_STATE_FREE), - ref_count(1) - { - } - - ~vm_page() - { - free(data); - } - - fssh_addr_t Address() const { return (fssh_addr_t)data; } - - static int Compare(void *_cacheEntry, const void *_key); - static uint32_t Hash(void *_cacheEntry, const void *_key, uint32_t range); -}; - -struct file_extent { - fssh_off_t offset; - fssh_file_io_vec disk; -}; - -struct file_map { - file_map(); - ~file_map(); - - file_extent *operator[](uint32_t index); - file_extent *ExtentAt(uint32_t index); - fssh_status_t Add(fssh_file_io_vec *vecs, fssh_size_t vecCount, fssh_off_t &lastOffset); - void Free(); - - union { - file_extent direct[CACHED_FILE_EXTENTS]; - file_extent *array; - }; - fssh_size_t count; -}; - - -static vm_page *vm_page_allocate_page(int state); -static void vm_page_get_page(vm_page* page); -static fssh_status_t vm_page_put_page(vm_page* page); -static fssh_status_t vm_page_set_state(vm_page *page, int state); - -static void vm_cache_insert_page(file_cache_ref *cacheRef, vm_page *page, - fssh_off_t offset); -static void vm_cache_remove_page(file_cache_ref *cacheRef, vm_page *page); -static vm_page *vm_cache_lookup_page(file_cache_ref *cacheRef, fssh_off_t page); -static fssh_status_t vm_cache_resize(file_cache_ref *cacheRef, fssh_off_t newSize); -static fssh_status_t vm_cache_write_modified(file_cache_ref *ref, bool fsReenter); - -static fssh_status_t vfs_read_pages(int fd, fssh_off_t pos, const fssh_iovec *vecs, - fssh_size_t count, fssh_size_t *_numBytes, bool mayBlock, bool fsReenter); -static fssh_status_t vfs_write_pages(int fd, fssh_off_t pos, const fssh_iovec *vecs, - fssh_size_t count, fssh_size_t *_numBytes, bool mayBlock, bool fsReenter); - -static fssh_status_t pages_io(file_cache_ref *ref, fssh_off_t offset, const fssh_iovec *vecs, - fssh_size_t count, fssh_size_t *_numBytes, bool doWrite); - - -typedef DoublyLinkedList > cache_modified_page_list; - -typedef DoublyLinkedList > cache_page_list; +typedef fssh_status_t (*cache_func)(file_cache_ref *ref, void *cookie, + fssh_off_t offset, int32_t pageOffset, fssh_addr_t buffer, + fssh_size_t bufferSize); struct file_cache_ref { mutex lock; fssh_mount_id mountID; fssh_vnode_id nodeID; void* node; - int deviceFD; fssh_off_t virtual_size; - - cache_page_list pages; - cache_modified_page_list modifiedPages; - - file_map map; }; -struct page_hash_key { - page_hash_key() {} - page_hash_key(int fd, fssh_vnode_id id, fssh_off_t offset) - : deviceFD(fd), - nodeID(id), - offset(offset) - { - } - - int deviceFD; - fssh_vnode_id nodeID; - fssh_off_t offset; -}; - - -typedef DoublyLinkedList > pool_page_list; - -struct PagePool { - - PagePool() - : pageHash(NULL), - unusedPages(), - freePages(), - allocatedPages(0) - { - } - - ~PagePool() - { - } - - fssh_status_t Init() - { - fssh_status_t error = recursive_lock_init(&lock, "page pool"); - if (error != FSSH_B_OK) { - fssh_panic("PagePool: Failed to init lock\n"); - return error; - } - - pageHash = hash_init(256, 0, &vm_page::Compare, &vm_page::Hash); - if (pageHash == NULL) { - fssh_panic("PagePool: Failed to create page hash\n"); - return FSSH_B_NO_MEMORY; - } - - return FSSH_B_OK; - } - - bool Lock() { return (recursive_lock_lock(&lock) == FSSH_B_OK); } - void Unlock() { recursive_lock_unlock(&lock); } - - recursive_lock lock; - hash_table* pageHash; - pool_page_list unusedPages; - pool_page_list freePages; - uint32_t allocatedPages; -}; - -struct PagePutter { - PagePutter(vm_page* page) - : fPage(page) - { - } - - ~PagePutter() - { - if (fPage) - vm_page_put_page(fPage); - } - -private: - vm_page* fPage; -}; - -static PagePool sPagePool; -static const uint32_t kMaxAllocatedPages = 1024; - - -int -vm_page::Compare(void *_cacheEntry, const void *_key) -{ - vm_page *page = (vm_page*)_cacheEntry; - const page_hash_key *key = (const page_hash_key *)_key; - - // device FD - if (page->cache->deviceFD != key->deviceFD) - return page->cache->deviceFD - key->deviceFD; - - // node ID - if (page->cache->nodeID < key->nodeID) - return -1; - if (page->cache->nodeID > key->nodeID) - return 1; - - // offset - if (page->offset < key->offset) - return -1; - if (page->offset > key->offset) - return 1; - - return 0; -} - -uint32_t -vm_page::Hash(void *_cacheEntry, const void *_key, uint32_t range) -{ - vm_page *page = (vm_page*)_cacheEntry; - const page_hash_key *key = (const page_hash_key *)_key; - - int fd = (page ? page->cache->deviceFD : key->deviceFD); - fssh_vnode_id id = (page ? page->cache->nodeID : key->nodeID); - fssh_off_t offset = (page ? page->offset : key->offset); - - uint32_t value = fd; - value = value * 17 + id; - value = value * 17 + offset; - - return value % range; -} - - -vm_page * -vm_page_allocate_page(int state) -{ - AutoLocker poolLocker(sPagePool); - - // is a queued free page available? - vm_page* page = sPagePool.freePages.RemoveHead(); - if (page) { - page->ref_count++; - return page; - } - - // no free page - -#if 0 -// TODO: Nice idea in principle, but causes a serious locking problem. -// vm_page_allocate_page() is always invoked with some cached locked at the -// same time. Thus locking a cache here to steal a page can cause a deadlock. - // If the limit for allocated pages has been reached, we try to steal an - // unused page. - if (sPagePool.allocatedPages >= kMaxAllocatedPages - && !sPagePool.unusedPages.IsEmpty()) { - // we grab the first non-busy page - for (pool_page_list::Iterator it(&sPagePool.unusedPages); - vm_page* currentPage = it.Next();) { - if (currentPage->state != PAGE_STATE_BUSY) { - it.Remove(); - page = currentPage; - page->ref_count++; - break; - } - } - - // If we've found a suitable page, we need to mark it busy, write it - // if it was modified, and finally remove it from its cache. - if (page != NULL) { - bool modified = (page->state == PAGE_STATE_MODIFIED); - - // mark the page busy - page->state = PAGE_STATE_BUSY; - - // We don't need the pool lock anymore. - poolLocker.Unlock(); - - file_cache_ref* cache = page->cache; - - // If the page is modified, we write it to the disk. - if (modified) { - // find out, how much to write, and remove the page from the - // cache's modified pages list - MutexLocker cacheLocker(cache->lock); - fssh_size_t bytes = fssh_min_c(FSSH_B_PAGE_SIZE, - cache->virtual_size - page->offset); - cache->modifiedPages.Remove(page); - cacheLocker.Unlock(); - - // if we need to write anything, do it now - if (bytes > 0) { - fssh_iovec vecs[1]; - vecs[0].iov_base = page->data; - vecs[0].iov_len = bytes; - fssh_status_t error = pages_io(cache, page->offset, vecs, 1, - &bytes, true); - if (error != FSSH_B_OK) { - // failed to write the page: bad, but we can't do - // much about it - fssh_dprintf("vm_page_allocate_page(): Failed to write " - "page %p (cache %p, offset: %lld).\n", page, - cache, page->offset); - } - } - } - - // remove the page from the cache - MutexLocker cacheLocker(cache->lock); - vm_cache_remove_page(cache, page); - - // now it's ours - page->state = PAGE_STATE_FREE; - - return page; - } - } -#endif // 0 - - // no page yet -- allocate a new one - - page = new(nothrow) vm_page; - if (!page || !page->data) { - delete page; - return NULL; - } - - sPagePool.allocatedPages++; - - return page; -} - - -static void -vm_page_get_page(vm_page* page) -{ - if (page) { - AutoLocker _(sPagePool); - - // increase ref count - page->ref_count++; - - // if the page was unused before, remove it from the unused pages list - if (page->ref_count == 1) - sPagePool.unusedPages.Remove(page); - } -} - - -static fssh_status_t -vm_page_put_page(vm_page* page) -{ - if (!page) - return FSSH_B_BAD_VALUE; - - AutoLocker _(sPagePool); - - if (page->ref_count <= 0) { - fssh_panic("vm_page_put_page(): page %p already unreferenced!\n", page); - return FSSH_B_BAD_VALUE; - } - - // decrease ref count - page->ref_count--; - - if (page->ref_count > 0) - return FSSH_B_OK; - - // the page is unreference now: add it to the unused or free pages list - - if (page->state == PAGE_STATE_FREE) { - // page is free - // if we've maxed out the allowed allocated page, free this one, - // otherwise add it to the free list - if (sPagePool.allocatedPages > kMaxAllocatedPages) { - delete page; - sPagePool.allocatedPages--; - } else - sPagePool.freePages.Add(page); - } else { - // page is is not free; add to unused pages list - sPagePool.unusedPages.Add(page); - } - - return FSSH_B_OK; -} - - -fssh_status_t -vm_page_set_state(vm_page *page, int state) -{ - AutoLocker _(sPagePool); - - if (page->ref_count <= 0) { - fssh_panic("vm_page_set_state(): page %p is already unreferenced!\n", - page); - return FSSH_B_BAD_VALUE; - } - - if (state == page->state) - return FSSH_B_OK; - - // If it was modified before, remove the page from the cache's modified - // pages list. - if (page->state == PAGE_STATE_MODIFIED && page->cache) - page->cache->modifiedPages.Remove(page); - - switch (state) { - case PAGE_STATE_ACTIVE: - case PAGE_STATE_BUSY: - case PAGE_STATE_FREE: - page->state = state; - break; - - case PAGE_STATE_MODIFIED: - { - page->state = state; - - // add page to the modified list of the cache - if (!page->cache) { - fssh_panic("vm_page_set_state(): setting page state of page %p " - "to PAGE_STATE_MODIFIED, but page is not in a cache\n", - page); - return FSSH_B_BAD_VALUE; - } - - page->cache->modifiedPages.Add(page); - - break; - } - - default: - fssh_panic("vm_page_set_state(): invalid page state: %d\n", state); - return FSSH_B_BAD_VALUE; - } - - return FSSH_B_OK; -} - - -// cache must be locked -// -void -vm_cache_insert_page(file_cache_ref *cache, vm_page *page, fssh_off_t offset) -{ - AutoLocker _(sPagePool); - - if (page->cache != NULL) { - fssh_panic("vm_cache_insert_page(%p, %p): page already in cache %p\n", - cache, page, page->cache); - return; - } - - page->cache = cache; - page->offset = offset; - - // insert page into hash - fssh_status_t error = hash_insert(sPagePool.pageHash, page); - if (error != FSSH_B_OK) { - fssh_panic("vm_cache_insert_page(): Failed to insert page %p into hash!\n", - page); - page->cache = NULL; - page->offset = 0; - return; - } - - // add page to cache page list - cache->pages.Add(page); -} - - -// cache must be locked -// -void -vm_cache_remove_page(file_cache_ref *cache, vm_page *page) -{ - if (cache != page->cache) { - fssh_panic("vm_cache_remove_page(%p, %p): page is in cache %p\n", - cache, page, page->cache); - return; - } - - AutoLocker _(sPagePool); - - if (page->state == PAGE_STATE_MODIFIED) - cache->modifiedPages.Remove(page); - - cache->pages.Remove(page); - - hash_remove(sPagePool.pageHash, page); - - page->cache = NULL; - page->offset = 0; -} - - -vm_page * -vm_cache_lookup_page(file_cache_ref *cache, fssh_off_t offset) -{ - if (!cache) - return NULL; - - AutoLocker _(sPagePool); - - page_hash_key key(cache->deviceFD, cache->nodeID, offset); - - vm_page* page = (vm_page*)hash_lookup(sPagePool.pageHash, &key); - - if (page) - vm_page_get_page(page); - - return page; -} - - -// cache must be locked -// -fssh_status_t -vm_cache_resize(file_cache_ref *cache, fssh_off_t newSize) -{ - fssh_off_t oldAlignedSize = PAGE_ALIGN(cache->virtual_size); - fssh_off_t newAlignedSize = PAGE_ALIGN(newSize); - - cache->virtual_size = newSize; - - // if the aligned cache size increased or remained the same, we're done - if (newAlignedSize >= oldAlignedSize) - return FSSH_B_OK; - - // the file shrinks, so we need to get rid of excess pages - - // Hold the page pool lock virtually all the time from now on. - AutoLocker poolLocker(sPagePool); - - // For sake of efficiency we sort the cache's list of pages so that all - // pages that need to be removed are at the beginning of the list. - vm_page* page = cache->pages.Head(); - if (newAlignedSize > 0) { - while (page) { - vm_page* nextPage = cache->pages.GetNext(page); - - if (page->offset >= newAlignedSize) { - // move to the beginning of the list - cache->pages.Remove(page); - cache->pages.Add(page, false); - } - - page = nextPage; - } - } - - // now we remove and free the excess pages one by one - while (true) { - // get the first page in the list to remove - // (since we sorted the list, this is usually very cheap) - for (cache_page_list::Iterator it(&cache->pages); (page = it.Next());) { - if (page->offset >= newAlignedSize) - break; - } - - // no more pages? -- then we're done - if (!page) - return FSSH_B_OK; - - if (page->state == PAGE_STATE_BUSY) { - // the page is busy -- wait a while and try again - poolLocker.Unlock(); - mutex_unlock(&cache->lock); - fssh_snooze(20000); - mutex_lock(&cache->lock); - poolLocker.Lock(); - } else { - // the page isn't busy -- get rid of it - vm_page_get_page(page); - vm_cache_remove_page(cache, page); - vm_page_set_state(page, PAGE_STATE_FREE); - vm_page_put_page(page); - } - } - - return FSSH_B_OK; -} - - -fssh_status_t -vm_cache_write_modified(file_cache_ref *cache, bool fsReenter) -{ - // TODO: Write more than one page at a time. To avoid deadlocks, when a - // busy page is encountered the previously collected pages need to be - // written. Otherwise as many pages as our on-stack array can contain - // can be processed at once. - MutexLocker locker(cache->lock); - while (true) { - // get the next modified page and mark it busy - vm_page* page = NULL; - - while (true) { - // get the first modified page - AutoLocker poolLocker(sPagePool); - page = cache->modifiedPages.Head(); - - if (!page) - return FSSH_B_OK; - - // if not marked busy, remove it and mark it busy - if (page->state != PAGE_STATE_BUSY) { - cache->modifiedPages.Remove(page); - vm_page_get_page(page); - page->state = PAGE_STATE_BUSY; - break; - } - - // page is busy -- wait a while - vm_page_put_page(page); - poolLocker.Unlock(); - locker.Unlock(); - fssh_snooze(20000); - locker.Lock(); - } - - locker.Unlock(); - - // write the page - fssh_size_t bytes = fssh_min_c(FSSH_B_PAGE_SIZE, cache->virtual_size - page->offset); - fssh_iovec vecs[1]; - vecs[0].iov_base = page->data; - vecs[0].iov_len = bytes; - fssh_status_t error = pages_io(cache, page->offset, vecs, 1, &bytes, true); - if (error != FSSH_B_OK) - return error; - - locker.Lock(); - - vm_page_set_state(page, PAGE_STATE_ACTIVE); - vm_page_put_page(page); - } - - return FSSH_B_OK; -} - - -fssh_status_t -vfs_read_pages(int fd, fssh_off_t pos, const fssh_iovec *vecs, fssh_size_t count, - fssh_size_t *_numBytes, bool mayBlock, bool fsReenter) -{ - // check how much the iovecs allow us to read - fssh_size_t toRead = 0; - for (fssh_size_t i = 0; i < count; i++) - toRead += vecs[i].iov_len; - - fssh_iovec* newVecs = NULL; - if (*_numBytes < toRead) { - // We're supposed to read less than specified by the vecs. Since - // readv_pos() doesn't support this, we need to clone the vecs. - newVecs = new(nothrow) fssh_iovec[count]; - if (!newVecs) - return FSSH_B_NO_MEMORY; - - fssh_size_t newCount = 0; - for (fssh_size_t i = 0; i < count && toRead > 0; i++) { - fssh_size_t vecLen = fssh_min_c(vecs[i].iov_len, toRead); - newVecs[i].iov_base = vecs[i].iov_base; - newVecs[i].iov_len = vecLen; - toRead -= vecLen; - newCount++; - } - - vecs = newVecs; - count = newCount; - } - - fssh_ssize_t bytesRead = fssh_readv_pos(fd, pos, vecs, count); - delete[] newVecs; - if (bytesRead < 0) - return bytesRead; - - *_numBytes = bytesRead; - return FSSH_B_OK; -} - - -fssh_status_t -vfs_write_pages(int fd, fssh_off_t pos, const fssh_iovec *vecs, fssh_size_t count, - fssh_size_t *_numBytes, bool mayBlock, bool fsReenter) -{ - // check how much the iovecs allow us to write - fssh_size_t toWrite = 0; - for (fssh_size_t i = 0; i < count; i++) - toWrite += vecs[i].iov_len; - - fssh_iovec* newVecs = NULL; - if (*_numBytes < toWrite) { - // We're supposed to write less than specified by the vecs. Since - // writev_pos() doesn't support this, we need to clone the vecs. - newVecs = new(nothrow) fssh_iovec[count]; - if (!newVecs) - return FSSH_B_NO_MEMORY; - - fssh_size_t newCount = 0; - for (fssh_size_t i = 0; i < count && toWrite > 0; i++) { - fssh_size_t vecLen = fssh_min_c(vecs[i].iov_len, toWrite); - newVecs[i].iov_base = vecs[i].iov_base; - newVecs[i].iov_len = vecLen; - toWrite -= vecLen; - newCount++; - } - - vecs = newVecs; - count = newCount; - } - - fssh_ssize_t bytesWritten = fssh_writev_pos(fd, pos, vecs, count); - delete[] newVecs; - if (bytesWritten < 0) - return bytesWritten; - - *_numBytes = bytesWritten; - return FSSH_B_OK; -} - fssh_status_t file_cache_init() { - fssh_status_t error = sPagePool.Init(); - if (error != FSSH_B_OK) - return error; - return FSSH_B_OK; } -// #pragma mark - - - -file_map::file_map() -{ - array = NULL; - count = 0; -} - - -file_map::~file_map() -{ - Free(); -} - - -file_extent * -file_map::operator[](uint32_t index) -{ - return ExtentAt(index); -} - - -file_extent * -file_map::ExtentAt(uint32_t index) -{ - if (index >= count) - return NULL; - - if (count > CACHED_FILE_EXTENTS) - return &array[index]; - - return &direct[index]; -} - - -fssh_status_t -file_map::Add(fssh_file_io_vec *vecs, fssh_size_t vecCount, fssh_off_t &lastOffset) -{ - TRACE(("file_map::Add(vecCount = %ld)\n", vecCount)); - - fssh_off_t offset = 0; - - if (vecCount <= CACHED_FILE_EXTENTS && count == 0) { - // just use the reserved area in the file_cache_ref structure - } else { - // TODO: once we can invalidate only parts of the file map, - // we might need to copy the previously cached file extends - // from the direct range - file_extent *newMap = (file_extent *)realloc(array, - (count + vecCount) * sizeof(file_extent)); - if (newMap == NULL) - return FSSH_B_NO_MEMORY; - - array = newMap; - - if (count != 0) { - file_extent *extent = ExtentAt(count - 1); - offset = extent->offset + extent->disk.length; - } - } - - int32_t start = count; - count += vecCount; - - for (uint32_t i = 0; i < vecCount; i++) { - file_extent *extent = ExtentAt(start + i); - - extent->offset = offset; - extent->disk = vecs[i]; - - offset += extent->disk.length; - } - -#ifdef TRACE_FILE_CACHE - for (uint32_t i = 0; i < count; i++) { - file_extent *extent = ExtentAt(i); - fssh_dprintf("[%ld] extend offset %Ld, disk offset %Ld, length %Ld\n", - i, extent->offset, extent->disk.offset, extent->disk.length); - } -#endif - - lastOffset = offset; - return FSSH_B_OK; -} - - -void -file_map::Free() -{ - if (count > CACHED_FILE_EXTENTS) - free(array); - - array = NULL; - count = 0; -} - - // #pragma mark - -static void -add_to_iovec(fssh_iovec *vecs, int32_t &index, int32_t max, fssh_addr_t address, fssh_size_t size) -{ - if (index > 0 && (fssh_addr_t)vecs[index - 1].iov_base + vecs[index - 1].iov_len == address) { - // the iovec can be combined with the previous one - vecs[index - 1].iov_len += size; - return; - } - - if (index == max) - fssh_panic("no more space for iovecs!"); - - // we need to start a new iovec - vecs[index].iov_base = (void *)address; - vecs[index].iov_len = size; - index++; -} - - -static file_extent * -find_file_extent(file_cache_ref *ref, fssh_off_t offset, uint32_t *_index) -{ - // TODO: do binary search - - for (uint32_t index = 0; index < ref->map.count; index++) { - file_extent *extent = ref->map[index]; - - if (extent->offset <= offset - && extent->offset + extent->disk.length > offset) { - if (_index) - *_index = index; - return extent; - } - } - - return NULL; -} - - static fssh_status_t -get_file_map(file_cache_ref *ref, fssh_off_t offset, fssh_size_t size, - fssh_file_io_vec *vecs, fssh_size_t *_count) -{ - fssh_size_t maxVecs = *_count; - fssh_status_t status = FSSH_B_OK; - - if (ref->map.count == 0) { - // we don't yet have the map of this file, so let's grab it - // (ordered by offset, so that we can do a binary search on them) - - mutex_lock(&ref->lock); - - // the file map could have been requested in the mean time - if (ref->map.count == 0) { - fssh_size_t vecCount = maxVecs; - fssh_off_t mapOffset = 0; - - while (true) { - status = vfs_get_file_map(ref->node, mapOffset, ~0UL, vecs, - &vecCount); - if (status < FSSH_B_OK && status != FSSH_B_BUFFER_OVERFLOW) { - mutex_unlock(&ref->lock); - return status; - } - - fssh_status_t addStatus = ref->map.Add(vecs, vecCount, mapOffset); - if (addStatus != FSSH_B_OK) { - // only clobber the status in case of failure - status = addStatus; - } - - if (status != FSSH_B_BUFFER_OVERFLOW) - break; - - // when we are here, the map has been stored in the array, and - // the array size was still too small to cover the whole file - vecCount = maxVecs; - } - } - - mutex_unlock(&ref->lock); - } - - if (status != FSSH_B_OK) { - // We must invalidate the (part of the) map we already - // have, as we cannot know if it's complete or not - ref->map.Free(); - return status; - } - - // We now have cached the map of this file, we now need to - // translate it for the requested access. - - uint32_t index; - file_extent *fileExtent = find_file_extent(ref, offset, &index); - if (fileExtent == NULL) { - // access outside file bounds? But that's not our problem - *_count = 0; - return FSSH_B_OK; - } - - offset -= fileExtent->offset; - vecs[0].offset = fileExtent->disk.offset + offset; - vecs[0].length = fileExtent->disk.length - offset; - - if (vecs[0].length >= size || index >= ref->map.count - 1) { - *_count = 1; - return FSSH_B_OK; - } - - // copy the rest of the vecs - - size -= vecs[0].length; - - for (index = 1; index < ref->map.count;) { - fileExtent++; - - vecs[index] = fileExtent->disk; - index++; - - if (size <= fileExtent->disk.length) - break; - - if (index >= maxVecs) { - *_count = index; - return FSSH_B_BUFFER_OVERFLOW; - } - - size -= fileExtent->disk.length; - } - - *_count = index; - return FSSH_B_OK; -} - - -/*! - Does the dirty work of translating the request into actual disk offsets - and reads to or writes from the supplied iovecs as specified by \a doWrite. -*/ -static fssh_status_t -pages_io(file_cache_ref *ref, fssh_off_t offset, const fssh_iovec *vecs, fssh_size_t count, - fssh_size_t *_numBytes, bool doWrite) -{ - TRACE(("pages_io: ref = %p, offset = %Ld, size = %lu, vecCount = %lu, %s\n", - ref, offset, *_numBytes, count, doWrite ? "write" : "read")); - - // translate the iovecs into direct device accesses - fssh_file_io_vec fileVecs[MAX_FILE_IO_VECS]; - fssh_size_t fileVecCount = MAX_FILE_IO_VECS; - fssh_size_t numBytes = *_numBytes; - - fssh_status_t status = get_file_map(ref, offset, numBytes, fileVecs, - &fileVecCount); - if (status < FSSH_B_OK && status != FSSH_B_BUFFER_OVERFLOW) { - TRACE(("get_file_map(offset = %Ld, numBytes = %lu) failed: %s\n", - offset, numBytes, fssh_strerror(status))); - return status; - } - - bool bufferOverflow = status == FSSH_B_BUFFER_OVERFLOW; - -#ifdef TRACE_FILE_CACHE - fssh_dprintf("got %lu file vecs for %Ld:%lu%s:\n", fileVecCount, offset, - numBytes, bufferOverflow ? " (array too small)" : ""); - for (fssh_size_t i = 0; i < fileVecCount; i++) { - fssh_dprintf(" [%lu] offset = %Ld, size = %Ld\n", - i, fileVecs[i].offset, fileVecs[i].length); - } -#endif - - if (fileVecCount == 0) { - // There are no file vecs at this offset, so we're obviously trying - // to access the file outside of its bounds - TRACE(("pages_io: access outside of vnode %p at offset %Ld\n", - ref->vnode, offset)); - return FSSH_B_BAD_VALUE; - } - - uint32_t fileVecIndex; - fssh_size_t size; - - if (!doWrite) { - // now directly read the data from the device - // the first file_io_vec can be read directly - - size = fileVecs[0].length; - if (size > numBytes) - size = numBytes; - - status = vfs_read_pages(ref->deviceFD, fileVecs[0].offset, vecs, - count, &size, true, false); - if (status < FSSH_B_OK) - return status; - - // TODO: this is a work-around for buggy device drivers! - // When our own drivers honour the length, we can: - // a) also use this direct I/O for writes (otherwise, it would - // overwrite precious data) - // b) panic if the term below is true (at least for writes) - if (size > fileVecs[0].length) { - //fssh_dprintf("warning: device driver %p doesn't respect total length in read_pages() call!\n", ref->device); - size = fileVecs[0].length; - } - - ASSERT(size <= fileVecs[0].length); - - // If the file portion was contiguous, we're already done now - if (size == numBytes) - return FSSH_B_OK; - - // if we reached the end of the file, we can return as well - if (size != fileVecs[0].length) { - *_numBytes = size; - return FSSH_B_OK; - } - - fileVecIndex = 1; - } else { - fileVecIndex = 0; - size = 0; - } - - // Too bad, let's process the rest of the file_io_vecs - - fssh_size_t totalSize = size; - - // first, find out where we have to continue in our iovecs - uint32_t i = 0; - for (; i < count; i++) { - if (size < vecs[i].iov_len) - break; - - size -= vecs[i].iov_len; - } - - fssh_size_t vecOffset = size; - fssh_size_t bytesLeft = numBytes - size; - - while (true) { - for (; fileVecIndex < fileVecCount; fileVecIndex++) { - fssh_file_io_vec &fileVec = fileVecs[fileVecIndex]; - fssh_off_t fileOffset = fileVec.offset; - fssh_off_t fileLeft = fssh_min_c(fileVec.length, bytesLeft); - - TRACE(("FILE VEC [%lu] length %Ld\n", fileVecIndex, fileLeft)); - - // process the complete fileVec - while (fileLeft > 0) { - fssh_iovec tempVecs[MAX_TEMP_IO_VECS]; - uint32_t tempCount = 0; - - // size tracks how much of what is left of the current fileVec - // (fileLeft) has been assigned to tempVecs - size = 0; - - // assign what is left of the current fileVec to the tempVecs - for (size = 0; size < fileLeft && i < count - && tempCount < MAX_TEMP_IO_VECS;) { - // try to satisfy one iovec per iteration (or as much as - // possible) - - // bytes left of the current iovec - fssh_size_t vecLeft = vecs[i].iov_len - vecOffset; - if (vecLeft == 0) { - vecOffset = 0; - i++; - continue; - } - - TRACE(("fill vec %ld, offset = %lu, size = %lu\n", - i, vecOffset, size)); - - // actually available bytes - fssh_size_t tempVecSize = fssh_min_c(vecLeft, fileLeft - size); - - tempVecs[tempCount].iov_base - = (void *)((fssh_addr_t)vecs[i].iov_base + vecOffset); - tempVecs[tempCount].iov_len = tempVecSize; - tempCount++; - - size += tempVecSize; - vecOffset += tempVecSize; - } - - fssh_size_t bytes = size; - if (doWrite) { - status = vfs_write_pages(ref->deviceFD, fileOffset, - tempVecs, tempCount, &bytes, true, false); - } else { - status = vfs_read_pages(ref->deviceFD, fileOffset, - tempVecs, tempCount, &bytes, true, false); - } - if (status < FSSH_B_OK) - return status; - - totalSize += bytes; - bytesLeft -= size; - fileOffset += size; - fileLeft -= size; - //fssh_dprintf("-> file left = %Lu\n", fileLeft); - - if (size != bytes || i >= count) { - // there are no more bytes or iovecs, let's bail out - *_numBytes = totalSize; - return FSSH_B_OK; - } - } - } - - if (bufferOverflow) { - status = get_file_map(ref, offset + totalSize, bytesLeft, fileVecs, - &fileVecCount); - if (status < FSSH_B_OK && status != FSSH_B_BUFFER_OVERFLOW) { - TRACE(("get_file_map(offset = %Ld, numBytes = %lu) failed: %s\n", - offset, numBytes, fssh_strerror(status))); - return status; - } - - bufferOverflow = status == FSSH_B_BUFFER_OVERFLOW; - fileVecIndex = 0; - -#ifdef TRACE_FILE_CACHE - fssh_dprintf("got %lu file vecs for %Ld:%lu%s:\n", fileVecCount, - offset + totalSize, numBytes, - bufferOverflow ? " (array too small)" : ""); - for (fssh_size_t i = 0; i < fileVecCount; i++) { - fssh_dprintf(" [%lu] offset = %Ld, size = %Ld\n", - i, fileVecs[i].offset, fileVecs[i].length); - } -#endif - } else - break; - } - - *_numBytes = totalSize; - return FSSH_B_OK; -} - - -/*! - This function is called by read_into_cache() (and from there only) - it - can only handle a certain amount of bytes, and read_into_cache() makes - sure that it matches that criterion. -*/ -static inline fssh_status_t -read_chunk_into_cache(file_cache_ref *ref, fssh_off_t offset, fssh_size_t numBytes, +read_from_file(file_cache_ref *ref, void *cookie, fssh_off_t offset, int32_t pageOffset, fssh_addr_t buffer, fssh_size_t bufferSize) { - TRACE(("read_chunk(offset = %Ld, size = %lu, pageOffset = %ld, buffer = %#lx, bufferSize = %lu\n", - offset, size, pageOffset, buffer, bufferSize)); - - fssh_iovec vecs[MAX_IO_VECS]; - int32_t vecCount = 0; - - vm_page *pages[MAX_IO_VECS]; - int32_t pageIndex = 0; - - // allocate pages for the cache and mark them busy - for (fssh_size_t pos = 0; pos < numBytes; pos += FSSH_B_PAGE_SIZE) { - vm_page *page = pages[pageIndex++] = vm_page_allocate_page(PAGE_STATE_FREE); - if (page == NULL) - fssh_panic("no more pages!"); - - page->state = PAGE_STATE_BUSY; - - vm_cache_insert_page(ref, page, offset + pos); - - fssh_addr_t virtualAddress = page->Address(); - - add_to_iovec(vecs, vecCount, MAX_IO_VECS, virtualAddress, FSSH_B_PAGE_SIZE); - // ToDo: check if the array is large enough! - } + fssh_iovec vec; + vec.iov_base = (void *)buffer; + vec.iov_len = bufferSize; mutex_unlock(&ref->lock); - // read file into reserved pages - fssh_status_t status = pages_io(ref, offset, vecs, vecCount, &numBytes, false); - if (status < FSSH_B_OK) { - // reading failed, free allocated pages - - fssh_dprintf("file_cache: read pages failed: %s\n", fssh_strerror(status)); - - mutex_lock(&ref->lock); - - for (int32_t i = 0; i < pageIndex; i++) { - vm_cache_remove_page(ref, pages[i]); - vm_page_set_state(pages[i], PAGE_STATE_FREE); - vm_page_put_page(pages[i]); - } - - return status; - } - - // copy the pages and unmap them again - - for (int32_t i = 0; i < vecCount; i++) { - fssh_addr_t base = (fssh_addr_t)vecs[i].iov_base; - fssh_size_t size = vecs[i].iov_len; - - // copy to user buffer if necessary - if (bufferSize != 0) { - fssh_size_t bytes = fssh_min_c(bufferSize, size - pageOffset); - - user_memcpy((void *)buffer, (void *)(base + pageOffset), bytes); - buffer += bytes; - bufferSize -= bytes; - pageOffset = 0; - } - } + fssh_status_t status = vfs_read_pages(ref->node, cookie, offset, &vec, 1, + &bufferSize, false); mutex_lock(&ref->lock); - // make the pages accessible in the cache - for (int32_t i = pageIndex; i-- > 0;) { - vm_page_set_state(pages[i], PAGE_STATE_ACTIVE); - vm_page_put_page(pages[i]); - } - - return FSSH_B_OK; -} - - -/*! - This function reads \a size bytes directly from the file into the cache. - If \a bufferSize does not equal zero, \a bufferSize bytes from the data - read in are also copied to the provided \a buffer. - This function always allocates all pages; it is the responsibility of the - calling function to only ask for yet uncached ranges. - The cache_ref lock must be hold when calling this function. -*/ -static fssh_status_t -read_into_cache(file_cache_ref *ref, fssh_off_t offset, fssh_size_t size, fssh_addr_t buffer, fssh_size_t bufferSize) -{ - TRACE(("read_from_cache: ref = %p, offset = %Ld, size = %lu, buffer = %p, bufferSize = %lu\n", - ref, offset, size, (void *)buffer, bufferSize)); - - // do we have to read in anything at all? - if (size == 0) - return FSSH_B_OK; - - // make sure "offset" is page aligned - but also remember the page offset - int32_t pageOffset = offset & (FSSH_B_PAGE_SIZE - 1); - size = PAGE_ALIGN(size + pageOffset); - offset -= pageOffset; - - while (true) { - fssh_size_t chunkSize = size; - if (chunkSize > (MAX_IO_VECS * FSSH_B_PAGE_SIZE)) - chunkSize = MAX_IO_VECS * FSSH_B_PAGE_SIZE; - - fssh_status_t status = read_chunk_into_cache(ref, offset, chunkSize, pageOffset, - buffer, bufferSize); - if (status != FSSH_B_OK) - return status; - - if ((size -= chunkSize) == 0) - return FSSH_B_OK; - - if (chunkSize >= bufferSize) { - bufferSize = 0; - buffer = 0; - } else { - bufferSize -= chunkSize - pageOffset; - buffer += chunkSize - pageOffset; - } - - offset += chunkSize; - pageOffset = 0; - } - - return FSSH_B_OK; -} - - -/** Like read_chunk_into_cache() but writes data into the cache */ - -static inline fssh_status_t -write_chunk_to_cache(file_cache_ref *ref, fssh_off_t offset, fssh_size_t numBytes, - int32_t pageOffset, fssh_addr_t buffer, fssh_size_t bufferSize) -{ - fssh_iovec vecs[MAX_IO_VECS]; - int32_t vecCount = 0; - vm_page *pages[MAX_IO_VECS]; - int32_t pageIndex = 0; - fssh_status_t status = FSSH_B_OK; - - // ToDo: this should be settable somewhere - bool writeThrough = false; - - // allocate pages for the cache and mark them busy - for (fssh_size_t pos = 0; pos < numBytes; pos += FSSH_B_PAGE_SIZE) { - // ToDo: if space is becoming tight, and this cache is already grown - // big - shouldn't we better steal the pages directly in that case? - // (a working set like approach for the file cache) - vm_page *page = pages[pageIndex++] = vm_page_allocate_page(PAGE_STATE_FREE); - page->state = PAGE_STATE_BUSY; - - vm_cache_insert_page(ref, page, offset + pos); - - fssh_addr_t virtualAddress = page->Address(); - - add_to_iovec(vecs, vecCount, MAX_IO_VECS, virtualAddress, FSSH_B_PAGE_SIZE); - // ToDo: check if the array is large enough! - } - - mutex_unlock(&ref->lock); - - // copy contents (and read in partially written pages first) - - if (pageOffset != 0) { - // This is only a partial write, so we have to read the rest of the page - // from the file to have consistent data in the cache - fssh_iovec readVec = { vecs[0].iov_base, FSSH_B_PAGE_SIZE }; - fssh_size_t bytesRead = FSSH_B_PAGE_SIZE; - - status = pages_io(ref, offset, &readVec, 1, &bytesRead, false); - // ToDo: handle errors for real! - if (status < FSSH_B_OK) - fssh_panic("1. pages_io() failed: %s!\n", fssh_strerror(status)); - } - - fssh_addr_t lastPageOffset = (pageOffset + bufferSize) & (FSSH_B_PAGE_SIZE - 1); - if (lastPageOffset != 0) { - // get the last page in the I/O vectors - fssh_addr_t last = (fssh_addr_t)vecs[vecCount - 1].iov_base - + vecs[vecCount - 1].iov_len - FSSH_B_PAGE_SIZE; - - if (offset + pageOffset + bufferSize == ref->virtual_size) { - // the space in the page after this write action needs to be cleaned - fssh_memset((void *)(last + lastPageOffset), 0, FSSH_B_PAGE_SIZE - lastPageOffset); - } else if (vecCount > 1) { - // the end of this write does not happen on a page boundary, so we - // need to fetch the last page before we can update it - fssh_iovec readVec = { (void *)last, FSSH_B_PAGE_SIZE }; - fssh_size_t bytesRead = FSSH_B_PAGE_SIZE; - - status = pages_io(ref, offset + numBytes - FSSH_B_PAGE_SIZE, &readVec, 1, - &bytesRead, false); - // ToDo: handle errors for real! - if (status < FSSH_B_OK) - fssh_panic("pages_io() failed: %s!\n", fssh_strerror(status)); - } - } - - for (int32_t i = 0; i < vecCount; i++) { - fssh_addr_t base = (fssh_addr_t)vecs[i].iov_base; - fssh_size_t bytes = fssh_min_c(bufferSize, fssh_size_t(vecs[i].iov_len - pageOffset)); - - // copy data from user buffer - user_memcpy((void *)(base + pageOffset), (void *)buffer, bytes); - - bufferSize -= bytes; - if (bufferSize == 0) - break; - - buffer += bytes; - pageOffset = 0; - } - - if (writeThrough) { - // write cached pages back to the file if we were asked to do that - fssh_status_t status = pages_io(ref, offset, vecs, vecCount, &numBytes, true); - if (status < FSSH_B_OK) { - // ToDo: remove allocated pages, ...? - fssh_panic("file_cache: remove allocated pages! write pages failed: %s\n", - fssh_strerror(status)); - } - } - - mutex_lock(&ref->lock); - - // unmap the pages again - - // make the pages accessible in the cache - for (int32_t i = pageIndex; i-- > 0;) { - if (writeThrough) - vm_page_set_state(pages[i], PAGE_STATE_ACTIVE); - else - vm_page_set_state(pages[i], PAGE_STATE_MODIFIED); - vm_page_put_page(pages[i]); - } - return status; } -/** Like read_into_cache() but writes data into the cache. To preserve data consistency, - * it might also read pages into the cache, though, if only a partial page gets written. - * The cache_ref lock must be hold when calling this function. - */ - static fssh_status_t -write_to_cache(file_cache_ref *ref, fssh_off_t offset, fssh_size_t size, fssh_addr_t buffer, fssh_size_t bufferSize) +write_to_file(file_cache_ref *ref, void *cookie, fssh_off_t offset, + int32_t pageOffset, fssh_addr_t buffer, fssh_size_t bufferSize) { - TRACE(("write_to_cache: ref = %p, offset = %Ld, size = %lu, buffer = %p, bufferSize = %lu\n", - ref, offset, size, (void *)buffer, bufferSize)); + fssh_iovec vec; + vec.iov_base = (void *)buffer; + vec.iov_len = bufferSize; - // make sure "offset" is page aligned - but also remember the page offset - int32_t pageOffset = offset & (FSSH_B_PAGE_SIZE - 1); - size = PAGE_ALIGN(size + pageOffset); - offset -= pageOffset; + mutex_unlock(&ref->lock); - while (true) { - fssh_size_t chunkSize = size; - if (chunkSize > (MAX_IO_VECS * FSSH_B_PAGE_SIZE)) - chunkSize = MAX_IO_VECS * FSSH_B_PAGE_SIZE; + fssh_status_t status = vfs_write_pages(ref->node, cookie, offset, &vec, 1, + &bufferSize, false); - fssh_status_t status = write_chunk_to_cache(ref, offset, chunkSize, pageOffset, buffer, bufferSize); - if (status != FSSH_B_OK) - return status; + mutex_lock(&ref->lock); - if ((size -= chunkSize) == 0) - return FSSH_B_OK; - - if (chunkSize >= bufferSize) { - bufferSize = 0; - buffer = 0; - } else { - bufferSize -= chunkSize - pageOffset; - buffer += chunkSize - pageOffset; - } - - offset += chunkSize; - pageOffset = 0; - } - - return FSSH_B_OK; + return status; } -static fssh_status_t -satisfy_cache_io(file_cache_ref *ref, fssh_off_t offset, fssh_addr_t buffer, fssh_addr_t lastBuffer, - bool doWrite) +static inline fssh_status_t +satisfy_cache_io(file_cache_ref *ref, void *cookie, cache_func function, + fssh_off_t offset, fssh_addr_t buffer, int32_t &pageOffset, + fssh_size_t bytesLeft, fssh_off_t &lastOffset, + fssh_addr_t &lastBuffer, int32_t &lastPageOffset, fssh_size_t &lastLeft) { + if (lastBuffer == buffer) + return FSSH_B_OK; + fssh_size_t requestSize = buffer - lastBuffer; - if (doWrite) - return write_to_cache(ref, offset, requestSize, lastBuffer, requestSize); - - return read_into_cache(ref, offset, requestSize, lastBuffer, requestSize); + fssh_status_t status = function(ref, cookie, lastOffset, lastPageOffset, + lastBuffer, requestSize); + if (status == FSSH_B_OK) { + lastBuffer = buffer; + lastLeft = bytesLeft; + lastOffset = offset; + lastPageOffset = 0; + pageOffset = 0; + } + return status; } static fssh_status_t -cache_io(void *_cacheRef, fssh_off_t offset, fssh_addr_t buffer, fssh_size_t *_size, bool doWrite) +cache_io(void *_cacheRef, void *cookie, fssh_off_t offset, fssh_addr_t buffer, + fssh_size_t *_size, bool doWrite) { if (_cacheRef == NULL) fssh_panic("cache_io() called with NULL ref!\n"); @@ -1602,86 +169,34 @@ cache_io(void *_cacheRef, fssh_off_t offset, fssh_addr_t buffer, fssh_size_t *_s size = fileSize - pageOffset - offset; *_size = size; } + if (size == 0) + return FSSH_B_OK; - // "offset" and "lastOffset" are always aligned to FSSH_B_PAGE_SIZE, + cache_func function; + if (doWrite) { + // in low memory situations, we bypass the cache beyond a + // certain I/O size + function = write_to_file; + } else { + function = read_from_file; + } + + // "offset" and "lastOffset" are always aligned to B_PAGE_SIZE, // the "last*" variables always point to the end of the last // satisfied request part + const uint32_t kMaxChunkSize = MAX_IO_VECS * FSSH_B_PAGE_SIZE; fssh_size_t bytesLeft = size, lastLeft = size; int32_t lastPageOffset = pageOffset; fssh_addr_t lastBuffer = buffer; fssh_off_t lastOffset = offset; - mutex_lock(&ref->lock); + MutexLocker locker(ref->lock); - for (; bytesLeft > 0; offset += FSSH_B_PAGE_SIZE) { + while (bytesLeft > 0) { // check if this page is already in memory - restart: - vm_page *page = vm_cache_lookup_page(ref, offset); - PagePutter pagePutter(page); - - if (page != NULL) { - // The page is busy - since we need to unlock the cache sometime - // in the near future, we need to satisfy the request of the pages - // we didn't get yet (to make sure no one else interferes in the - // mean time). - fssh_status_t status = FSSH_B_OK; - - if (lastBuffer != buffer) { - status = satisfy_cache_io(ref, lastOffset + lastPageOffset, - buffer, lastBuffer, doWrite); - if (status == FSSH_B_OK) { - lastBuffer = buffer; - lastLeft = bytesLeft; - lastOffset = offset; - lastPageOffset = 0; - pageOffset = 0; - } - } - - if (status != FSSH_B_OK) { - mutex_unlock(&ref->lock); - return status; - } - - if (page->state == PAGE_STATE_BUSY) { - mutex_unlock(&ref->lock); - // ToDo: don't wait forever! - fssh_snooze(20000); - mutex_lock(&ref->lock); - goto restart; - } - } - - fssh_size_t bytesInPage = fssh_min_c(fssh_size_t(FSSH_B_PAGE_SIZE - pageOffset), bytesLeft); - fssh_addr_t virtualAddress; - - TRACE(("lookup page from offset %Ld: %p, size = %lu, pageOffset = %lu\n", offset, page, bytesLeft, pageOffset)); - if (page != NULL) { - virtualAddress = page->Address(); - - // and copy the contents of the page already in memory - if (doWrite) { - user_memcpy((void *)(virtualAddress + pageOffset), (void *)buffer, bytesInPage); - - // make sure the page is in the modified list - if (page->state != PAGE_STATE_MODIFIED) - vm_page_set_state(page, PAGE_STATE_MODIFIED); - } else - user_memcpy((void *)buffer, (void *)(virtualAddress + pageOffset), bytesInPage); - - if (bytesLeft <= bytesInPage) { - // we've read the last page, so we're done! - mutex_unlock(&ref->lock); - return FSSH_B_OK; - } - - // prepare a potential gap request - lastBuffer = buffer + bytesInPage; - lastLeft = bytesLeft - bytesInPage; - lastOffset = offset + FSSH_B_PAGE_SIZE; - lastPageOffset = 0; - } + fssh_size_t bytesInPage = fssh_min_c( + fssh_size_t(FSSH_B_PAGE_SIZE - pageOffset), bytesLeft); if (bytesLeft <= bytesInPage) break; @@ -1689,18 +204,21 @@ cache_io(void *_cacheRef, fssh_off_t offset, fssh_addr_t buffer, fssh_size_t *_s buffer += bytesInPage; bytesLeft -= bytesInPage; pageOffset = 0; + offset += FSSH_B_PAGE_SIZE; + + if (buffer - lastBuffer + lastPageOffset >= kMaxChunkSize) { + fssh_status_t status = satisfy_cache_io(ref, cookie, function, + offset, buffer, pageOffset, bytesLeft, lastOffset, + lastBuffer, lastPageOffset, lastLeft); + if (status != FSSH_B_OK) + return status; + } } // fill the last remaining bytes of the request (either write or read) - fssh_status_t status; - if (doWrite) - status = write_to_cache(ref, lastOffset + lastPageOffset, lastLeft, lastBuffer, lastLeft); - else - status = read_into_cache(ref, lastOffset + lastPageOffset, lastLeft, lastBuffer, lastLeft); - - mutex_unlock(&ref->lock); - return status; + return function(ref, cookie, lastOffset, lastPageOffset, lastBuffer, + lastLeft); } @@ -1715,7 +233,7 @@ using namespace FSShell; void * fssh_file_cache_create(fssh_mount_id mountID, fssh_vnode_id vnodeID, - fssh_off_t size, int fd) + fssh_off_t size) { TRACE(("file_cache_create(mountID = %ld, vnodeID = %Ld, size = %Ld, fd = %d)\n", mountID, vnodeID, size, fd)); @@ -1725,7 +243,6 @@ fssh_file_cache_create(fssh_mount_id mountID, fssh_vnode_id vnodeID, ref->mountID = mountID; ref->nodeID = vnodeID; - ref->deviceFD = fd; ref->virtual_size = size; // get vnode @@ -1762,13 +279,7 @@ fssh_file_cache_delete(void *_cacheRef) TRACE(("file_cache_delete(ref = %p)\n", ref)); - // write all modified pages and resize the cache to 0 to free all pages - // it contains - vm_cache_write_modified(ref, false); - mutex_lock(&ref->lock); - vm_cache_resize(ref, 0); - mutex_destroy(&ref->lock); delete ref; @@ -1785,14 +296,11 @@ fssh_file_cache_set_size(void *_cacheRef, fssh_off_t size) if (ref == NULL) return FSSH_B_OK; - fssh_file_cache_invalidate_file_map(_cacheRef, 0, size); - // ToDo: make this better (we would only need to extend or shrink the map) - mutex_lock(&ref->lock); - fssh_status_t status = vm_cache_resize(ref, size); + ref->virtual_size = size; mutex_unlock(&ref->lock); - return status; + return FSSH_B_OK; } @@ -1803,67 +311,34 @@ fssh_file_cache_sync(void *_cacheRef) if (ref == NULL) return FSSH_B_BAD_VALUE; - return vm_cache_write_modified(ref, true); + return FSSH_B_OK; } fssh_status_t -fssh_file_cache_read_pages(void *_cacheRef, fssh_off_t offset, const fssh_iovec *vecs, fssh_size_t count, fssh_size_t *_numBytes) -{ - file_cache_ref *ref = (file_cache_ref *)_cacheRef; - - return pages_io(ref, offset, vecs, count, _numBytes, false); -} - - -fssh_status_t -fssh_file_cache_write_pages(void *_cacheRef, fssh_off_t offset, const fssh_iovec *vecs, fssh_size_t count, fssh_size_t *_numBytes) -{ - file_cache_ref *ref = (file_cache_ref *)_cacheRef; - - fssh_status_t status = pages_io(ref, offset, vecs, count, _numBytes, true); - TRACE(("file_cache_write_pages(ref = %p, offset = %Ld, vecs = %p, count = %lu, bytes = %lu) = %ld\n", - ref, offset, vecs, count, *_numBytes, status)); - - return status; -} - - -fssh_status_t -fssh_file_cache_read(void *_cacheRef, fssh_off_t offset, void *bufferBase, fssh_size_t *_size) +fssh_file_cache_read(void *_cacheRef, void *cookie, fssh_off_t offset, + void *bufferBase, fssh_size_t *_size) { file_cache_ref *ref = (file_cache_ref *)_cacheRef; TRACE(("file_cache_read(ref = %p, offset = %Ld, buffer = %p, size = %lu)\n", ref, offset, bufferBase, *_size)); - return cache_io(ref, offset, (fssh_addr_t)bufferBase, _size, false); + return cache_io(ref, cookie, offset, (fssh_addr_t)bufferBase, _size, false); } fssh_status_t -fssh_file_cache_write(void *_cacheRef, fssh_off_t offset, const void *buffer, fssh_size_t *_size) +fssh_file_cache_write(void *_cacheRef, void *cookie, fssh_off_t offset, + const void *buffer, fssh_size_t *_size) { file_cache_ref *ref = (file_cache_ref *)_cacheRef; - fssh_status_t status = cache_io(ref, offset, (fssh_addr_t)const_cast(buffer), _size, true); + fssh_status_t status = cache_io(ref, cookie, offset, + (fssh_addr_t)const_cast(buffer), _size, true); TRACE(("file_cache_write(ref = %p, offset = %Ld, buffer = %p, size = %lu) = %ld\n", ref, offset, buffer, *_size, status)); return status; } - -fssh_status_t -fssh_file_cache_invalidate_file_map(void *_cacheRef, fssh_off_t offset, fssh_off_t size) -{ - file_cache_ref *ref = (file_cache_ref *)_cacheRef; - - // ToDo: honour offset/size parameters - - TRACE(("file_cache_invalidate_file_map(offset = %Ld, size = %Ld)\n", offset, size)); - mutex_lock(&ref->lock); - ref->map.Free(); - mutex_unlock(&ref->lock); - return FSSH_B_OK; -} diff --git a/src/tools/fs_shell/file_map.cpp b/src/tools/fs_shell/file_map.cpp new file mode 100644 index 0000000000..3d75ce3045 --- /dev/null +++ b/src/tools/fs_shell/file_map.cpp @@ -0,0 +1,325 @@ +/* + * Copyright 2004-2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Distributed under the terms of the MIT License. + */ + + +#include "fssh_fs_cache.h" + +#include +#include + +#include "fssh_kernel_export.h" +#include "vfs.h" + + +//#define TRACE_FILE_MAP +#ifdef TRACE_FILE_MAP +# define TRACE(x) fssh_dprintf x +#else +# define TRACE(x) ; +#endif + +#define CACHED_FILE_EXTENTS 2 + // must be smaller than MAX_FILE_IO_VECS + // ToDo: find out how much of these are typically used + +using namespace FSShell; + +namespace FSShell { + +struct file_extent { + fssh_off_t offset; + fssh_file_io_vec disk; +}; + +struct file_map { + file_map(); + ~file_map(); + + file_extent *operator[](uint32_t index); + file_extent *ExtentAt(uint32_t index); + fssh_status_t Add(fssh_file_io_vec *vecs, fssh_size_t vecCount, + fssh_off_t &lastOffset); + void Free(); + + union { + file_extent direct[CACHED_FILE_EXTENTS]; + file_extent *array; + }; + fssh_size_t count; + void *vnode; +}; + + +file_map::file_map() +{ + array = NULL; + count = 0; +} + + +file_map::~file_map() +{ + Free(); +} + + +file_extent * +file_map::operator[](uint32_t index) +{ + return ExtentAt(index); +} + + +file_extent * +file_map::ExtentAt(uint32_t index) +{ + if (index >= count) + return NULL; + + if (count > CACHED_FILE_EXTENTS) + return &array[index]; + + return &direct[index]; +} + + +fssh_status_t +file_map::Add(fssh_file_io_vec *vecs, fssh_size_t vecCount, + fssh_off_t &lastOffset) +{ + TRACE(("file_map::Add(vecCount = %ld)\n", vecCount)); + + fssh_off_t offset = 0; + + if (vecCount <= CACHED_FILE_EXTENTS && count == 0) { + // just use the reserved area in the file_cache_ref structure + } else { + // TODO: once we can invalidate only parts of the file map, + // we might need to copy the previously cached file extends + // from the direct range + file_extent *newMap = (file_extent *)realloc(array, + (count + vecCount) * sizeof(file_extent)); + if (newMap == NULL) + return FSSH_B_NO_MEMORY; + + array = newMap; + + if (count != 0) { + file_extent *extent = ExtentAt(count - 1); + offset = extent->offset + extent->disk.length; + } + } + + int32_t start = count; + count += vecCount; + + for (uint32_t i = 0; i < vecCount; i++) { + file_extent *extent = ExtentAt(start + i); + + extent->offset = offset; + extent->disk = vecs[i]; + + offset += extent->disk.length; + } + +#ifdef TRACE_FILE_MAP + for (uint32_t i = 0; i < count; i++) { + file_extent *extent = ExtentAt(i); + fssh_dprintf("[%ld] extend offset %Ld, disk offset %Ld, length %Ld\n", + i, extent->offset, extent->disk.offset, extent->disk.length); + } +#endif + + lastOffset = offset; + return FSSH_B_OK; +} + + +void +file_map::Free() +{ + if (count > CACHED_FILE_EXTENTS) + free(array); + + array = NULL; + count = 0; +} + + +// #pragma mark - + + +static file_extent * +find_file_extent(file_map &map, fssh_off_t offset, uint32_t *_index) +{ + // TODO: do binary search + + for (uint32_t index = 0; index < map.count; index++) { + file_extent *extent = map[index]; + + if (extent->offset <= offset + && extent->offset + extent->disk.length > offset) { + if (_index) + *_index = index; + return extent; + } + } + + return NULL; +} + + +} // namespace FSShell + + +// #pragma mark - public FS API + + +extern "C" void * +fssh_file_map_create(fssh_mount_id mountID, fssh_vnode_id vnodeID) +{ + TRACE(("file_map_create(mountID = %ld, vnodeID = %Ld)\n", mountID, vnodeID)); + + file_map *map = new file_map; + if (map == NULL) + return NULL; + + // Get the vnode for the object + // (note, this does not grab a reference to the node) + if (vfs_lookup_vnode(mountID, vnodeID, &map->vnode) != FSSH_B_OK) { + delete map; + return NULL; + } + + return map; +} + + +extern "C" void +fssh_file_map_delete(void *_map) +{ + file_map *map = (file_map *)_map; + if (map == NULL) + return; + + TRACE(("file_map_delete(map = %p)\n", map)); + delete map; +} + + +extern "C" void +fssh_file_map_set_size(void *_map, fssh_off_t size) +{ + if (_map == NULL) + return; + + // TODO: honour offset/size parameters + file_map *map = (file_map *)_map; + map->Free(); +} + + +extern "C" void +fssh_file_map_invalidate(void *_map, fssh_off_t offset, fssh_off_t size) +{ + if (_map == NULL) + return; + + // TODO: honour offset/size parameters + file_map *map = (file_map *)_map; + map->Free(); +} + + +extern "C" fssh_status_t +fssh_file_map_translate(void *_map, fssh_off_t offset, fssh_size_t size, + fssh_file_io_vec *vecs, fssh_size_t *_count) +{ + if (_map == NULL) + return FSSH_B_BAD_VALUE; + + file_map &map = *(file_map *)_map; + fssh_size_t maxVecs = *_count; + fssh_status_t status = FSSH_B_OK; + + if (map.count == 0) { + // we don't yet have the map of this file, so let's grab it + // (ordered by offset, so that we can do a binary search on them) + fssh_size_t vecCount = maxVecs; + fssh_off_t mapOffset = 0; + + while (true) { + status = vfs_get_file_map(map.vnode, mapOffset, ~0UL, vecs, + &vecCount); + if (status < FSSH_B_OK && status != FSSH_B_BUFFER_OVERFLOW) + return status; + + fssh_status_t addStatus = map.Add(vecs, vecCount, mapOffset); + if (addStatus != FSSH_B_OK) { + // only clobber the status in case of failure + status = addStatus; + } + + if (status != FSSH_B_BUFFER_OVERFLOW) + break; + + // when we are here, the map has been stored in the array, and + // the array size was still too small to cover the whole file + vecCount = maxVecs; + } + } + + if (status != FSSH_B_OK) { + // We must invalidate the (part of the) map we already + // have, as we cannot know if it's complete or not + map.Free(); + return status; + } + + // We now have cached the map of this file, we now need to + // translate it for the requested access. + + uint32_t index; + file_extent *fileExtent = find_file_extent(map, offset, &index); + if (fileExtent == NULL) { + // access outside file bounds? But that's not our problem + *_count = 0; + return FSSH_B_OK; + } + + offset -= fileExtent->offset; + vecs[0].offset = fileExtent->disk.offset + offset; + vecs[0].length = fileExtent->disk.length - offset; + + if (vecs[0].length >= size || index >= map.count - 1) { + *_count = 1; + return FSSH_B_OK; + } + + // copy the rest of the vecs + + size -= vecs[0].length; + + for (index = 1; index < map.count;) { + fileExtent++; + + vecs[index] = fileExtent->disk; + index++; + + if (size <= fileExtent->disk.length) + break; + + if (index >= maxVecs) { + *_count = index; + return FSSH_B_BUFFER_OVERFLOW; + } + + size -= fileExtent->disk.length; + } + + *_count = index; + return FSSH_B_OK; +} + diff --git a/src/tools/fs_shell/vfs.cpp b/src/tools/fs_shell/vfs.cpp index fad1d7f647..b536fbc235 100644 --- a/src/tools/fs_shell/vfs.cpp +++ b/src/tools/fs_shell/vfs.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2002-2006, Axel Dörfler, axeld@pinc-software.de. + * Copyright 2002-2007, Axel Dörfler, axeld@pinc-software.de. * Distributed under the terms of the MIT License. * * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved. @@ -10,6 +10,7 @@ #include "vfs.h" +#include #include #include "fd.h" @@ -25,13 +26,13 @@ #include "fssh_stat.h" #include "fssh_stdio.h" #include "fssh_string.h" +#include "fssh_uio.h" #include "fssh_unistd.h" #include "hash.h" #include "KPath.h" #include "posix_compatibility.h" #include "syscalls.h" - //#define TRACE_VFS #ifdef TRACE_VFS # define TRACE(x) fssh_dprintf x @@ -166,6 +167,8 @@ static struct vnode *sRoot; static hash_table *sMountsTable; static fssh_mount_id sNextMountID = 1; +#define MAX_TEMP_IO_VECS 8 + fssh_mode_t __fssh_gUmask = 022; /* function declarations */ @@ -1724,6 +1727,158 @@ get_new_fd(int type, struct fs_mount *mount, struct vnode *vnode, } +/*! Does the dirty work of combining the file_io_vecs with the iovecs + and calls the file system hooks to read/write the request to disk. +*/ +static fssh_status_t +common_file_io_vec_pages(int fd, const fssh_file_io_vec *fileVecs, + fssh_size_t fileVecCount, const fssh_iovec *vecs, fssh_size_t vecCount, + uint32_t *_vecIndex, fssh_size_t *_vecOffset, fssh_size_t *_numBytes, + bool doWrite) +{ + if (fileVecCount == 0) { + // There are no file vecs at this offset, so we're obviously trying + // to access the file outside of its bounds + return FSSH_B_BAD_VALUE; + } + + fssh_size_t numBytes = *_numBytes; + uint32_t fileVecIndex; + fssh_size_t vecOffset = *_vecOffset; + uint32_t vecIndex = *_vecIndex; + fssh_status_t status; + fssh_size_t size; + + if (!doWrite) { + // now directly read the data from the device + // the first file_io_vec can be read directly + + size = fileVecs[0].length; + if (size > numBytes) + size = numBytes; + + status = fssh_read_pages(fd, fileVecs[0].offset, vecs, vecCount, + &size, false); + if (status < FSSH_B_OK) + return status; + + // TODO: this is a work-around for buggy device drivers! + // When our own drivers honour the length, we can: + // a) also use this direct I/O for writes (otherwise, it would + // overwrite precious data) + // b) panic if the term below is true (at least for writes) + if (size > fileVecs[0].length) { + //dprintf("warning: device driver %p doesn't respect total length in read_pages() call!\n", ref->device); + size = fileVecs[0].length; + } + + ASSERT(size <= fileVecs[0].length); + + // If the file portion was contiguous, we're already done now + if (size == numBytes) + return FSSH_B_OK; + + // if we reached the end of the file, we can return as well + if (size != fileVecs[0].length) { + *_numBytes = size; + return FSSH_B_OK; + } + + fileVecIndex = 1; + + // first, find out where we have to continue in our iovecs + for (; vecIndex < vecCount; vecIndex++) { + if (size < vecs[vecIndex].iov_len) + break; + + size -= vecs[vecIndex].iov_len; + } + } else { + fileVecIndex = 0; + size = 0; + } + + // Too bad, let's process the rest of the file_io_vecs + + fssh_size_t totalSize = size; + fssh_size_t bytesLeft = numBytes - size; + + for (; fileVecIndex < fileVecCount; fileVecIndex++) { + const fssh_file_io_vec &fileVec = fileVecs[fileVecIndex]; + fssh_off_t fileOffset = fileVec.offset; + fssh_off_t fileLeft = fssh_min_c(fileVec.length, bytesLeft); + + TRACE(("FILE VEC [%lu] length %Ld\n", fileVecIndex, fileLeft)); + + // process the complete fileVec + while (fileLeft > 0) { + fssh_iovec tempVecs[MAX_TEMP_IO_VECS]; + uint32_t tempCount = 0; + + // size tracks how much of what is left of the current fileVec + // (fileLeft) has been assigned to tempVecs + size = 0; + + // assign what is left of the current fileVec to the tempVecs + for (size = 0; size < fileLeft && vecIndex < vecCount + && tempCount < MAX_TEMP_IO_VECS;) { + // try to satisfy one iovec per iteration (or as much as + // possible) + + // bytes left of the current iovec + fssh_size_t vecLeft = vecs[vecIndex].iov_len - vecOffset; + if (vecLeft == 0) { + vecOffset = 0; + vecIndex++; + continue; + } + + TRACE(("fill vec %ld, offset = %lu, size = %lu\n", + vecIndex, vecOffset, size)); + + // actually available bytes + fssh_size_t tempVecSize = fssh_min_c(vecLeft, fileLeft - size); + + tempVecs[tempCount].iov_base + = (void *)((fssh_addr_t)vecs[vecIndex].iov_base + vecOffset); + tempVecs[tempCount].iov_len = tempVecSize; + tempCount++; + + size += tempVecSize; + vecOffset += tempVecSize; + } + + fssh_size_t bytes = size; + if (doWrite) { + status = fssh_write_pages(fd, fileOffset, tempVecs, + tempCount, &bytes, false); + } else { + status = fssh_read_pages(fd, fileOffset, tempVecs, + tempCount, &bytes, false); + } + if (status < FSSH_B_OK) + return status; + + totalSize += bytes; + bytesLeft -= size; + fileOffset += size; + fileLeft -= size; + + if (size != bytes || vecIndex >= vecCount) { + // there are no more bytes or iovecs, let's bail out + *_numBytes = totalSize; + return FSSH_B_OK; + } + } + } + + *_vecIndex = vecIndex; + *_vecOffset = vecOffset; + *_numBytes = totalSize; + return FSSH_B_OK; +} + + // #pragma mark - public VFS API @@ -1895,6 +2050,110 @@ fssh_get_vnode_removed(fssh_mount_id mountID, fssh_vnode_id vnodeID, } +//! Works directly on the host's file system +extern "C" fssh_status_t +fssh_read_pages(int fd, fssh_off_t pos, const fssh_iovec *vecs, + fssh_size_t count, fssh_size_t *_numBytes, bool fsReenter) +{ + // check how much the iovecs allow us to read + fssh_size_t toRead = 0; + for (fssh_size_t i = 0; i < count; i++) + toRead += vecs[i].iov_len; + + fssh_iovec* newVecs = NULL; + if (*_numBytes < toRead) { + // We're supposed to read less than specified by the vecs. Since + // readv_pos() doesn't support this, we need to clone the vecs. + newVecs = new(nothrow) fssh_iovec[count]; + if (!newVecs) + return FSSH_B_NO_MEMORY; + + fssh_size_t newCount = 0; + for (fssh_size_t i = 0; i < count && toRead > 0; i++) { + fssh_size_t vecLen = fssh_min_c(vecs[i].iov_len, toRead); + newVecs[i].iov_base = vecs[i].iov_base; + newVecs[i].iov_len = vecLen; + toRead -= vecLen; + newCount++; + } + + vecs = newVecs; + count = newCount; + } + + fssh_ssize_t bytesRead = fssh_readv_pos(fd, pos, vecs, count); + delete[] newVecs; + if (bytesRead < 0) + return fssh_get_errno(); + + *_numBytes = bytesRead; + return FSSH_B_OK; +} + + +//! Works directly on the host's file system +extern "C" fssh_status_t +fssh_write_pages(int fd, fssh_off_t pos, const fssh_iovec *vecs, + fssh_size_t count, fssh_size_t *_numBytes, bool fsReenter) +{ + // check how much the iovecs allow us to write + fssh_size_t toWrite = 0; + for (fssh_size_t i = 0; i < count; i++) + toWrite += vecs[i].iov_len; + + fssh_iovec* newVecs = NULL; + if (*_numBytes < toWrite) { + // We're supposed to write less than specified by the vecs. Since + // writev_pos() doesn't support this, we need to clone the vecs. + newVecs = new(nothrow) fssh_iovec[count]; + if (!newVecs) + return FSSH_B_NO_MEMORY; + + fssh_size_t newCount = 0; + for (fssh_size_t i = 0; i < count && toWrite > 0; i++) { + fssh_size_t vecLen = fssh_min_c(vecs[i].iov_len, toWrite); + newVecs[i].iov_base = vecs[i].iov_base; + newVecs[i].iov_len = vecLen; + toWrite -= vecLen; + newCount++; + } + + vecs = newVecs; + count = newCount; + } + + fssh_ssize_t bytesWritten = fssh_writev_pos(fd, pos, vecs, count); + delete[] newVecs; + if (bytesWritten < 0) + return fssh_get_errno(); + + *_numBytes = bytesWritten; + return FSSH_B_OK; +} + + +//! Works directly on the host's file system +extern "C" fssh_status_t +fssh_read_file_io_vec_pages(int fd, const fssh_file_io_vec *fileVecs, + fssh_size_t fileVecCount, const fssh_iovec *vecs, fssh_size_t vecCount, + uint32_t *_vecIndex, fssh_size_t *_vecOffset, fssh_size_t *_bytes) +{ + return common_file_io_vec_pages(fd, fileVecs, fileVecCount, + vecs, vecCount, _vecIndex, _vecOffset, _bytes, false); +} + + +//! Works directly on the host's file system +extern "C" fssh_status_t +fssh_write_file_io_vec_pages(int fd, const fssh_file_io_vec *fileVecs, + fssh_size_t fileVecCount, const fssh_iovec *vecs, fssh_size_t vecCount, + uint32_t *_vecIndex, fssh_size_t *_vecOffset, fssh_size_t *_bytes) +{ + return common_file_io_vec_pages(fd, fileVecs, fileVecCount, + vecs, vecCount, _vecIndex, _vecOffset, _bytes, true); +} + + // #pragma mark - private VFS API // Functions the VFS exports for other parts of the kernel @@ -1980,16 +2239,42 @@ vfs_get_vnode(fssh_mount_id mountID, fssh_vnode_id vnodeID, void **_vnode) } +fssh_status_t +vfs_read_pages(void *_vnode, void *cookie, fssh_off_t pos, + const fssh_iovec *vecs, fssh_size_t count, fssh_size_t *_numBytes, + bool fsReenter) +{ + struct vnode *vnode = (struct vnode *)_vnode; + + return FS_CALL(vnode, read_pages)(vnode->mount->cookie, vnode->private_node, + cookie, pos, vecs, count, _numBytes, fsReenter); +} + + +fssh_status_t +vfs_write_pages(void *_vnode, void *cookie, fssh_off_t pos, + const fssh_iovec *vecs, fssh_size_t count, fssh_size_t *_numBytes, + bool fsReenter) +{ + struct vnode *vnode = (struct vnode *)_vnode; + + return FS_CALL(vnode, write_pages)(vnode->mount->cookie, vnode->private_node, + cookie, pos, vecs, count, _numBytes, fsReenter); +} + + fssh_status_t vfs_entry_ref_to_vnode(fssh_mount_id mountID, fssh_vnode_id directoryID, const char *name, void **_vnode) { - return entry_ref_to_vnode(mountID, directoryID, name, (struct vnode **)_vnode); + return entry_ref_to_vnode(mountID, directoryID, name, + (struct vnode **)_vnode); } void -vfssh_fs_vnode_to_node_ref(void *_vnode, fssh_mount_id *_mountID, fssh_vnode_id *_vnodeID) +vfs_fs_vnode_to_node_ref(void *_vnode, fssh_mount_id *_mountID, + fssh_vnode_id *_vnodeID) { struct vnode *vnode = (struct vnode *)_vnode; diff --git a/src/tools/fs_shell/vfs.h b/src/tools/fs_shell/vfs.h index ff6229d8c8..c9c634f6e4 100644 --- a/src/tools/fs_shell/vfs.h +++ b/src/tools/fs_shell/vfs.h @@ -73,6 +73,12 @@ fssh_status_t vfs_lookup_vnode(fssh_mount_id mountID, fssh_vnode_id vnodeID, void vfs_put_vnode(void *vnode); void vfs_acquire_vnode(void *vnode); fssh_status_t vfs_get_cookie_from_fd(int fd, void **_cookie); +fssh_status_t vfs_read_pages(void *vnode, void *cookie, fssh_off_t pos, + const fssh_iovec *vecs, fssh_size_t count, + fssh_size_t *_numBytes, bool fsReenter); +fssh_status_t vfs_write_pages(void *vnode, void *cookie, + fssh_off_t pos, const fssh_iovec *vecs, fssh_size_t count, + fssh_size_t *_numBytes, bool fsReenter); fssh_status_t vfs_get_file_map(void *_vnode, fssh_off_t offset, fssh_size_t size, fssh_file_io_vec *vecs, fssh_size_t *_count);