Moved freeing the vnode out of dec_vnode_ref_count() into a separate function

(free_vnode()). Upon freeing, the file cache is now only written back if the
file is not deleted.
dec_vnode_ref_count() no longer frees vnodes that are not going to be deleted.
Instead, it will add them to an unused vnode list, so that they can be freed
if needed (currently only 512 unused vnodes will be kept around). This should
speed up the whole thing, and makes the file cache much more useful, as it's
no longer dumped after the file is closed.
fs_unmount() is not yet respecting this (ie. it doesn't free those nodes as
it should).


git-svn-id: file:///srv/svn/repos/haiku/trunk/current@10287 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler
2004-11-29 21:27:17 +00:00
parent 86a48e24ad
commit 41c3a7a3d5
+105 -67
View File
@@ -52,6 +52,12 @@
#define MAX_SYM_LINKS SYMLINKS_MAX
const static uint32 kMaxUnusedVnodes = 512;
// This is the maximum number of unused vnodes that the system
// will keep around.
// It may be chosen with respect to the available memory or enhanced
// by some timestamp/frequency heurism.
static struct {
const char *path;
const char *target;
@@ -69,6 +75,7 @@ struct vnode {
struct vm_cache *cache;
mount_id device;
list_link mount_link;
list_link unused_link;
vnode_id id;
fs_vnode private_node;
struct fs_mount *mount;
@@ -160,6 +167,8 @@ static mutex sVnodeMutex;
#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
@@ -563,6 +572,37 @@ create_new_vnode(struct vnode **_vnode, mount_id mountID, vnode_id vnodeID)
}
/** Frees the vnode and all resources it has acquired.
* Will also make sure that any cache modifications are written back.
*/
static void
free_vnode(struct vnode *vnode, bool reenter)
{
ASSERT(vnode->ref_count == 0 && vnode->busy);
// write back any changes in this vnode's cache (if present)
if (vnode->cache && !vnode->delete_me)
vm_cache_write_modified((vm_cache_ref *)vnode->cache);
if (vnode->delete_me)
FS_CALL(vnode, remove_vnode)(vnode->mount->cookie, vnode->private_node, reenter);
else
FS_CALL(vnode, put_vnode)(vnode->mount->cookie, vnode->private_node, reenter);
// if we have a vm_cache attached, remove it
if (vnode->cache)
vm_cache_release_ref((vm_cache_ref *)vnode->cache);
vnode->cache = NULL;
remove_vnode_from_mount_list(vnode, vnode->mount);
free(vnode);
}
/** \brief Decrements the reference counter of the given vnode and deletes it,
* if the counter dropped to 0.
*
@@ -579,52 +619,47 @@ create_new_vnode(struct vnode **_vnode, mount_id mountID, vnode_id vnodeID)
static status_t
dec_vnode_ref_count(struct vnode *vnode, bool reenter)
{
int err;
int old_ref;
int32 oldRefCount;
mutex_lock(&sVnodeMutex);
if (vnode->busy == true)
if (vnode->busy)
panic("dec_vnode_ref_count called on vnode that was busy! vnode %p\n", vnode);
old_ref = atomic_add(&vnode->ref_count, -1);
oldRefCount = atomic_add(&vnode->ref_count, -1);
PRINT(("dec_vnode_ref_count: vnode %p, ref now %ld\n", vnode, vnode->ref_count));
if (old_ref == 1) {
vnode->busy = true;
if (oldRefCount == 1) {
bool freeNode = false;
// Just insert the vnode into an unused list if we don't need
// to delete it
if (vnode->delete_me) {
hash_remove(sVnodeTable, vnode);
vnode->busy = true;
freeNode = true;
} else {
list_add_item(&sUnusedVnodeList, vnode);
if (++sUnusedVnodes > kMaxUnusedVnodes) {
// there are too many unused vnodes so we free the oldest one
// ToDo: evaluate this mechanism
vnode = (struct vnode *)list_remove_head_item(&sUnusedVnodeList);
hash_remove(sVnodeTable, vnode);
vnode->busy = true;
freeNode = true;
}
}
mutex_unlock(&sVnodeMutex);
// write back any changes in this vnode's cache (if present)
if (vnode->cache)
vm_cache_write_modified((vm_cache_ref *)vnode->cache);
if (vnode->delete_me)
FS_CALL(vnode, remove_vnode)(vnode->mount->cookie, vnode->private_node, reenter);
else
FS_CALL(vnode, put_vnode)(vnode->mount->cookie, vnode->private_node, reenter);
// if we have a vm_cache attached, remove it
if (vnode->cache)
vm_cache_release_ref((vm_cache_ref *)vnode->cache);
vnode->cache = NULL;
remove_vnode_from_mount_list(vnode, vnode->mount);
mutex_lock(&sVnodeMutex);
hash_remove(sVnodeTable, vnode);
if (freeNode)
free_vnode(vnode, reenter);
} else
mutex_unlock(&sVnodeMutex);
free(vnode);
err = 1;
} else {
mutex_unlock(&sVnodeMutex);
err = 0;
}
return err;
return B_OK;
}
@@ -644,15 +679,16 @@ inc_vnode_ref_count(struct vnode *vnode)
/** \brief Looks up a vnode by mount and node ID in the sVnodeTable.
*
* The caller must hold the sVnodeMutex.
*
* \param mountID the mount ID.
* \param vnodeID the node ID.
*
* \return The vnode structure, if it was found in the hash table, \c NULL
* otherwise.
*/
The caller must hold the sVnodeMutex.
\param mountID the mount ID.
\param vnodeID the node ID.
\return The vnode structure, if it was found in the hash table, \c NULL
otherwise.
*/
static struct vnode *
lookup_vnode(mount_id mountID, vnode_id vnodeID)
{
@@ -666,19 +702,20 @@ lookup_vnode(mount_id mountID, vnode_id vnodeID)
/** \brief Retrieves a vnode for a given mount ID, node ID pair.
*
* If the node is not yet in memory, it will be loaded.
*
* The caller must not hold the sVnodeMutex or the sMountMutex.
*
* \param mountID the mount ID.
* \param vnodeID the node ID.
* \param _vnode Pointer to a vnode* variable into which the pointer to the
* retrieved vnode structure shall be written.
* \param reenter \c true, if this function is called (indirectly) from within
* a file system.
* \return \c B_OK, if everything when fine, an error code otherwise.
*/
If the node is not yet in memory, it will be loaded.
The caller must not hold the sVnodeMutex or the sMountMutex.
\param mountID the mount ID.
\param vnodeID the node ID.
\param _vnode Pointer to a vnode* variable into which the pointer to the
retrieved vnode structure shall be written.
\param reenter \c true, if this function is called (indirectly) from within
a file system.
\return \c B_OK, if everything when fine, an error code otherwise.
*/
static status_t
get_vnode(mount_id mountID, vnode_id vnodeID, struct vnode **_vnode, int reenter)
{
@@ -702,6 +739,10 @@ restart:
status_t status;
if (vnode) {
if (vnode->ref_count == 0) {
// this vnode has been unused before
list_remove_item(&sUnusedVnodeList, vnode);
}
inc_vnode_ref_count(vnode);
} else {
// we need to create a new vnode and read it in
@@ -2300,20 +2341,17 @@ vfs_mount_boot_file_system(kernel_args *args)
status_t
vfs_init(kernel_args *args)
{
{
struct vnode *v;
sVnodeTable = hash_init(VNODE_HASH_TABLE_SIZE, (addr_t)&v->next - (addr_t)v,
&vnode_compare, &vnode_hash);
if (sVnodeTable == NULL)
panic("vfs_init: error creating vnode hash table\n");
}
{
struct fs_mount *mount;
sMountsTable = hash_init(MOUNTS_HASH_TABLE_SIZE, (addr_t)&mount->next - (addr_t)mount,
&mount_compare, &mount_hash);
if (sMountsTable == NULL)
panic("vfs_init: error creating mounts hash table\n");
}
sVnodeTable = hash_init(VNODE_HASH_TABLE_SIZE, offsetof(struct vnode, next),
&vnode_compare, &vnode_hash);
if (sVnodeTable == NULL)
panic("vfs_init: error creating vnode hash table\n");
list_init_etc(&sUnusedVnodeList, offsetof(struct vnode, unused_link));
sMountsTable = hash_init(MOUNTS_HASH_TABLE_SIZE, offsetof(struct fs_mount, next),
&mount_compare, &mount_hash);
if (sMountsTable == NULL)
panic("vfs_init: error creating mounts hash table\n");
node_monitor_init();