From a01dd963464e7a1dd3e4f7fa1db77307f701b123 Mon Sep 17 00:00:00 2001 From: Michael Lotz Date: Sun, 28 Apr 2013 13:36:20 +0200 Subject: [PATCH] Return more than a single dirent at a time in bfs_read_dir(). Return as many dirents as fit into the provided buffer. The readdir() implementation in libroot tries to get many dirents and caches them to reduce the amount of syscalls and the associated overhead. This reduces the syscall count dramatically for use cases where a lot of directories are enumerated. A "git status" on a Haiku repository for example goes from over 50k read dir syscalls down to just under 7k. The performance gain is only around a moderate 5% in that use case though. --- .../file_systems/bfs/kernel_interface.cpp | 49 +++++++++++++------ 1 file changed, 34 insertions(+), 15 deletions(-) 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 ad9b88774d..6504c9915c 100644 --- a/src/add-ons/kernel/file_systems/bfs/kernel_interface.cpp +++ b/src/add-ons/kernel/file_systems/bfs/kernel_interface.cpp @@ -1634,25 +1634,44 @@ bfs_read_dir(fs_volume* _volume, fs_vnode* _node, void* _cookie, FUNCTION(); TreeIterator* iterator = (TreeIterator*)_cookie; - - uint16 length; - ino_t id; - status_t status = iterator->GetNextEntry(dirent->d_name, &length, - bufferSize, &id); - if (status == B_ENTRY_NOT_FOUND) { - *_num = 0; - return B_OK; - } else if (status != B_OK) - RETURN_ERROR(status); - Volume* volume = (Volume*)_volume->private_volume; - dirent->d_dev = volume->ID(); - dirent->d_ino = id; + uint32 maxCount = *_num; + uint32 count = 0; - dirent->d_reclen = sizeof(struct dirent) + length; + while (count < maxCount && bufferSize > sizeof(struct dirent)) { + ino_t id; + uint16 length; + size_t nameBufferSize = bufferSize - sizeof(struct dirent) + 1; - *_num = 1; + status_t status = iterator->GetNextEntry(dirent->d_name, &length, + nameBufferSize, &id); + + if (status == B_ENTRY_NOT_FOUND) + break; + + if (status == B_BUFFER_OVERFLOW) { + // the remaining name buffer length was too small + if (count == 0) + RETURN_ERROR(B_BUFFER_OVERFLOW); + break; + } + + if (status != B_OK) + RETURN_ERROR(status); + + ASSERT(length < nameBufferSize); + + dirent->d_dev = volume->ID(); + dirent->d_ino = id; + dirent->d_reclen = sizeof(struct dirent) + length; + + bufferSize -= dirent->d_reclen; + dirent = (struct dirent*)((uint8*)dirent + dirent->d_reclen); + count++; + } + + *_num = count; return B_OK; }