* Moved the unused vnode management to a new file. Well the few variables

used for it that is.
* The main cause for the heavy contention of the unused vnodes mutex was that
  relatively few vnodes are actually used for a longer time. Mainly those are
  the volume roots, mmap()ed files, and the files opened by programs. A good
  deal of nodes -- particularly directories -- are just referenced for a very
  short time, e.g. to resolve a path to a contained entry. This caused those
  nodes to be added to and removed from the unused vnodes list very
  frequently, thus resulting in a high contention of the mutex guarding it.
  To address the problem I've introduced an approximation of a set of "hot"
  vnodes, i.e. vnodes that have recently been marked unused. They are stored
  in an array that by means of an r/w locker and atomic operations can most
  of the time be accessed concurrently. Whenever it gets full, it is flushed
  to the actual unused vnodes list.
* dec_vnode_ref_count(): No longer check the unused vnode count every time.
  The called new vnode_unused() does only from time to time and returns when
  the caller is expected to free some of the unused vnodes. As a side effect
  this also fixes a bug I previously introduced: The unused vnode to be freed
  was marked busy without being locked first.

The -j8 Haiku image test build shows that the changes reduce the contention
of the unused vnode list mutex to virtually zero without introducing any
significant contention of the new r/w lock. The VMCache lock contention also
seems to be decreased somewhat, which is probably not that surprising
considering that the page writer acquires/releases vnode references with the
cache lock held. The "pages" lock takes over even more contention, now
causing more than 100000 waits per second.
The total build time reduction is about 4.5%. Kernel time drops more than
10%.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@34866 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold
2010-01-03 02:43:32 +00:00
parent 8ccbb7815c
commit 5484890096
3 changed files with 289 additions and 43 deletions
+40 -43
View File
@@ -56,6 +56,7 @@
#include "EntryCache.h"
#include "fifo.h"
#include "IORequest.h"
#include "unused_vnodes.h"
#include "Vnode.h"
#include "../cache/vnode_store.h"
@@ -104,13 +105,6 @@
#endif
const static uint32 kMaxUnusedVnodes = 8192;
// This is the maximum number of unused vnodes that the system
// will keep around (weak limit, if there is enough memory left,
// they won't get flushed even when hitting that limit).
// It may be chosen with respect to the available memory or enhanced
// by some timestamp/frequency heurism.
const static size_t kMaxPathLength = 65536;
// The absolute maximum path length (for getcwd() - this is not depending
// on PATH_MAX
@@ -263,17 +257,9 @@ static rw_lock sVnodeLock = RW_LOCK_INITIALIZER("vfs_vnode_lock");
*/
static mutex sIOContextRootLock = MUTEX_INITIALIZER("io_context::root lock");
/*! \brief Guards sUnusedVnodeList and sUnusedVnodes.
Innermost lock. Must not be held when acquiring any other lock.
*/
static mutex sUnusedVnodesLock = MUTEX_INITIALIZER("unused vnodes");
#define VNODE_HASH_TABLE_SIZE 1024
static hash_table* sVnodeTable;
static list sUnusedVnodeList;
static uint32 sUnusedVnodes = 0;
static struct vnode* sRoot;
#define MOUNTS_HASH_TABLE_SIZE 16
@@ -286,6 +272,8 @@ mode_t __gUmask = 022;
/* function declarations */
static void free_unused_vnodes();
// file descriptor operation prototypes
static status_t file_read(struct file_descriptor* descriptor, off_t pos,
void* buffer, size_t* _bytes);
@@ -1053,33 +1041,24 @@ dec_vnode_ref_count(struct vnode* vnode, bool alwaysFree, bool reenter)
panic("dec_vnode_ref_count: called on busy vnode %p\n", vnode);
bool freeNode = false;
bool freeUnusedNodes = false;
// Just insert the vnode into an unused list if we don't need
// to delete it
if (vnode->IsRemoved() || alwaysFree) {
vnode_to_be_freed(vnode);
vnode->SetBusy(true);
freeNode = true;
} else {
MutexLocker unusedVnodesLocker(sUnusedVnodesLock);
list_add_item(&sUnusedVnodeList, vnode);
if (++sUnusedVnodes > kMaxUnusedVnodes
&& low_resource_state(
B_KERNEL_RESOURCE_PAGES | B_KERNEL_RESOURCE_MEMORY)
!= B_NO_LOW_RESOURCE) {
// there are too many unused vnodes so we free the oldest one
// TODO: evaluate this mechanism
vnode = (struct vnode*)list_remove_head_item(&sUnusedVnodeList);
vnode->SetBusy(true);
freeNode = true;
sUnusedVnodes--;
}
}
} else
freeUnusedNodes = vnode_unused(vnode);
nodeLocker.Unlock();
locker.Unlock();
if (freeNode)
free_vnode(vnode, reenter);
else if (freeUnusedNodes)
free_unused_vnodes();
return B_OK;
}
@@ -1178,9 +1157,7 @@ restart:
if (vnode) {
if (vnode->ref_count == 0) {
// this vnode has been unused before
MutexLocker unusedVnodesLocker(sUnusedVnodesLock);
list_remove_item(&sUnusedVnodeList, vnode);
sUnusedVnodes--;
vnode_used(vnode);
}
inc_vnode_ref_count(vnode);
@@ -1269,9 +1246,16 @@ put_vnode(struct vnode* vnode)
static void
vnode_low_resource_handler(void* /*data*/, uint32 resources, int32 level)
free_unused_vnodes(int32 level)
{
TRACE(("vnode_low_resource_handler(level = %ld)\n", level));
unused_vnodes_check_started();
if (level == B_NO_LOW_RESOURCE) {
unused_vnodes_check_done();
return;
}
flush_hot_vnodes();
// determine how many nodes to free
uint32 count = 1;
@@ -1279,8 +1263,6 @@ vnode_low_resource_handler(void* /*data*/, uint32 resources, int32 level)
MutexLocker unusedVnodesLocker(sUnusedVnodesLock);
switch (level) {
case B_NO_LOW_RESOURCE:
return;
case B_LOW_RESOURCE_NOTE:
count = sUnusedVnodes / 100;
break;
@@ -1336,6 +1318,25 @@ vnode_low_resource_handler(void* /*data*/, uint32 resources, int32 level)
dec_vnode_ref_count(vnode, true, false);
// this should free the vnode when it's still unused
}
unused_vnodes_check_done();
}
static void
free_unused_vnodes()
{
free_unused_vnodes(
low_resource_state(B_KERNEL_RESOURCE_PAGES | B_KERNEL_RESOURCE_MEMORY));
}
static void
vnode_low_resource_handler(void* /*data*/, uint32 resources, int32 level)
{
TRACE(("vnode_low_resource_handler(level = %ld)\n", level));
free_unused_vnodes(level);
}
@@ -7292,9 +7293,7 @@ fs_unmount(char* path, dev_t mountID, uint32 flags, bool kernel)
if (vnode->ref_count == 0) {
// this vnode has been unused before
MutexLocker unusedVnodesLocker(sUnusedVnodesLock);
list_remove_item(&sUnusedVnodeList, vnode);
sUnusedVnodes--;
vnode_used(vnode);
}
}
@@ -7402,9 +7401,7 @@ fs_sync(dev_t device)
if (vnode->ref_count == 0) {
// this vnode has been unused before
MutexLocker unusedVnodesLocker(sUnusedVnodesLock);
list_remove_item(&sUnusedVnodeList, vnode);
sUnusedVnodes--;
vnode_used(vnode);
}
inc_vnode_ref_count(vnode);