Fixed the get_vnode()/new_vnode() race condition by introducing a new

function publish_vnode(). When calling new_vnode(), the node is only
reserved, but not yet accessible for others. Only when you call
publish_vnode(), it will become available. That simplifies new node
handling/locking considerably.
Note, you don't have to call new_vnode() before publish_vnode(); ie.
publish_vnode() acts exactly like new_vnode() did before if used that
way.


git-svn-id: file:///srv/svn/repos/haiku/trunk/current@11719 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler 2005-03-14 01:15:31 +00:00
parent bade75a525
commit 4e08f1474d
2 changed files with 53 additions and 13 deletions

View File

@ -299,7 +299,7 @@ Volume::CreateNode(Inode *parent, const char *name, int32 type)
InsertNode(inode);
hash_insert(fNodeHash, inode);
new_vnode(ID(), inode->ID(), inode);
publish_vnode(ID(), inode->ID(), inode);
if (fRootNode != NULL)
fRootNode->SetModificationTime(time(NULL));

View File

@ -82,8 +82,9 @@ struct vnode {
struct fs_mount *mount;
struct vnode *covered_by;
int32 ref_count;
bool delete_me;
bool busy;
uint8 remove : 1;
uint8 busy : 1;
uint8 unpublished : 1;
};
struct vnode_hash_key {
@ -589,13 +590,15 @@ free_vnode(struct vnode *vnode, bool reenter)
// if the vnode won't be deleted, in which case the changes
// will be discarded
if (vnode->cache && !vnode->delete_me)
if (vnode->cache && !vnode->remove)
vm_cache_write_modified(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 (!vnode->unpublished) {
if (vnode->remove)
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)
@ -641,7 +644,7 @@ dec_vnode_ref_count(struct vnode *vnode, bool reenter)
// Just insert the vnode into an unused list if we don't need
// to delete it
if (vnode->delete_me) {
if (vnode->remove) {
hash_remove(sVnodeTable, vnode);
vnode->busy = true;
freeNode = true;
@ -1648,8 +1651,39 @@ new_vnode(mount_id mountID, vnode_id vnodeID, fs_vnode privateNode)
panic("vnode %ld:%Ld already exists (node = %p, vnode->node = %p)!", mountID, vnodeID, privateNode, vnode->private_node);
status_t status = create_new_vnode(&vnode, mountID, vnodeID);
if (status == B_OK)
if (status == B_OK) {
vnode->private_node = privateNode;
vnode->busy = true;
vnode->unpublished = true;
}
PRINT(("returns: %s\n", strerror(status)));
mutex_unlock(&sVnodeMutex);
return status;
}
extern "C" status_t
publish_vnode(mount_id mountID, vnode_id vnodeID, fs_vnode privateNode)
{
FUNCTION(("publish_vnode()\n"));
mutex_lock(&sVnodeMutex);
struct vnode *vnode = lookup_vnode(mountID, vnodeID);
status_t status = B_OK;
if (vnode != NULL && vnode->busy && vnode->unpublished
&& vnode->private_node == privateNode) {
vnode->busy = false;
vnode->unpublished = false;
} else if (vnode == NULL && privateNode != NULL) {
status = create_new_vnode(&vnode, mountID, vnodeID);
if (status == B_OK)
vnode->private_node = privateNode;
} else
status = B_BAD_VALUE;
PRINT(("returns: %s\n", strerror(status)));
@ -1696,8 +1730,14 @@ remove_vnode(mount_id mountID, vnode_id vnodeID)
mutex_lock(&sVnodeMutex);
vnode = lookup_vnode(mountID, vnodeID);
if (vnode)
vnode->delete_me = true;
if (vnode != NULL) {
vnode->remove = true;
if (vnode->unpublished) {
// if the vnode hasn't been published yet, we delete it here
atomic_add(&vnode->ref_count, -1);
free_vnode(vnode, true);
}
}
mutex_unlock(&sVnodeMutex);
return B_OK;
@ -1713,7 +1753,7 @@ unremove_vnode(mount_id mountID, vnode_id vnodeID)
vnode = lookup_vnode(mountID, vnodeID);
if (vnode)
vnode->delete_me = false;
vnode->remove = false;
mutex_unlock(&sVnodeMutex);
return B_OK;