* Resolved TODO in free_vnode(): There was a race condition between vnode
destruction and VMVnodeCache::AcquireUnreferencedStoreRef(). Solved by adding a flag to VMVnodeCache and letting AcquireUnreferencedStoreRef() fail, if set. * Added TODO regarding replacing the snooze() waiting for busy vnodes. * get_vnode(): Unlock sVnodeMutex while calling the put_vnode() hook on error. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@34841 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
+13
-2
@@ -25,6 +25,7 @@ VMVnodeCache::Init(struct vnode *vnode)
|
|||||||
|
|
||||||
fVnode = vnode;
|
fVnode = vnode;
|
||||||
fFileCacheRef = NULL;
|
fFileCacheRef = NULL;
|
||||||
|
fVnodeDeleted = false;
|
||||||
|
|
||||||
vfs_vnode_to_node_ref(fVnode, &fDevice, &fInode);
|
vfs_vnode_to_node_ref(fVnode, &fDevice, &fInode);
|
||||||
|
|
||||||
@@ -117,13 +118,23 @@ VMVnodeCache::CanWritePage(off_t offset)
|
|||||||
status_t
|
status_t
|
||||||
VMVnodeCache::AcquireUnreferencedStoreRef()
|
VMVnodeCache::AcquireUnreferencedStoreRef()
|
||||||
{
|
{
|
||||||
|
// Quick check whether getting a vnode reference is still allowed. Only
|
||||||
|
// after a successful vfs_get_vnode() the check is safe (since then we've
|
||||||
|
// either got the reference to our vnode, or have been notified that it is
|
||||||
|
// toast), but the check is cheap and saves quite a bit of work in case the
|
||||||
|
// condition holds.
|
||||||
|
if (fVnodeDeleted)
|
||||||
|
return B_BUSY;
|
||||||
|
|
||||||
struct vnode *vnode;
|
struct vnode *vnode;
|
||||||
status_t status = vfs_get_vnode(fDevice, fInode, false, &vnode);
|
status_t status = vfs_get_vnode(fDevice, fInode, false, &vnode);
|
||||||
|
|
||||||
// If successful, update the store's vnode pointer, so that release_ref()
|
// If successful, update the store's vnode pointer, so that release_ref()
|
||||||
// won't use a stale pointer.
|
// won't use a stale pointer.
|
||||||
if (status == B_OK)
|
if (status == B_OK && fVnodeDeleted) {
|
||||||
fVnode = vnode;
|
vfs_put_vnode(fVnode);
|
||||||
|
status = B_BUSY;
|
||||||
|
}
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-2
@@ -39,11 +39,14 @@ public:
|
|||||||
file_cache_ref* FileCacheRef() const
|
file_cache_ref* FileCacheRef() const
|
||||||
{ return fFileCacheRef; }
|
{ return fFileCacheRef; }
|
||||||
|
|
||||||
|
void VnodeDeleted() { fVnodeDeleted = true; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct vnode* fVnode;
|
struct vnode* fVnode;
|
||||||
dev_t fDevice;
|
|
||||||
ino_t fInode;
|
|
||||||
file_cache_ref* fFileCacheRef;
|
file_cache_ref* fFileCacheRef;
|
||||||
|
ino_t fInode;
|
||||||
|
dev_t fDevice;
|
||||||
|
volatile bool fVnodeDeleted;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -56,6 +56,7 @@
|
|||||||
|
|
||||||
#include "fifo.h"
|
#include "fifo.h"
|
||||||
#include "IORequest.h"
|
#include "IORequest.h"
|
||||||
|
#include "../cache/vnode_store.h"
|
||||||
|
|
||||||
|
|
||||||
//#define TRACE_VFS
|
//#define TRACE_VFS
|
||||||
@@ -1092,12 +1093,6 @@ free_vnode(struct vnode* vnode, bool reenter)
|
|||||||
// count, so that it will neither become negative nor 0.
|
// count, so that it will neither become negative nor 0.
|
||||||
vnode->ref_count = 2;
|
vnode->ref_count = 2;
|
||||||
|
|
||||||
// TODO: Usually, when the vnode is unreferenced, no one can get hold of the
|
|
||||||
// cache either (i.e. no one can get a cache reference while we're deleting
|
|
||||||
// the vnode).. This is, however, not the case for the page daemon. It gets
|
|
||||||
// its cache references via the pages it scans, so it can in fact get a
|
|
||||||
// vnode reference while we're deleting the vnode.
|
|
||||||
|
|
||||||
if (!vnode->unpublished) {
|
if (!vnode->unpublished) {
|
||||||
if (vnode->remove)
|
if (vnode->remove)
|
||||||
FS_CALL(vnode, remove_vnode, reenter);
|
FS_CALL(vnode, remove_vnode, reenter);
|
||||||
@@ -1105,8 +1100,16 @@ free_vnode(struct vnode* vnode, bool reenter)
|
|||||||
FS_CALL(vnode, put_vnode, reenter);
|
FS_CALL(vnode, put_vnode, reenter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the vnode has a VMCache attached, make sure that it won't try to get
|
||||||
|
// another reference via VMVnodeCache::AcquireUnreferencedStoreRef(). As
|
||||||
|
// long as the vnode is busy and in the hash, that won't happen, but as
|
||||||
|
// soon as we've removed it from the hash, it could reload the vnode -- with
|
||||||
|
// a new cache attached!
|
||||||
|
if (vnode->cache != NULL)
|
||||||
|
((VMVnodeCache*)vnode->cache)->VnodeDeleted();
|
||||||
|
|
||||||
// The file system has removed the resources of the vnode now, so we can
|
// The file system has removed the resources of the vnode now, so we can
|
||||||
// make it available again (and remove the busy vnode from the hash)
|
// make it available again (by removing the busy vnode from the hash).
|
||||||
mutex_lock(&sVnodeMutex);
|
mutex_lock(&sVnodeMutex);
|
||||||
hash_remove(sVnodeTable, vnode);
|
hash_remove(sVnodeTable, vnode);
|
||||||
mutex_unlock(&sVnodeMutex);
|
mutex_unlock(&sVnodeMutex);
|
||||||
@@ -1277,6 +1280,7 @@ restart:
|
|||||||
vnodeID);
|
vnodeID);
|
||||||
return B_BUSY;
|
return B_BUSY;
|
||||||
}
|
}
|
||||||
|
// TODO: Replace this mechanism!
|
||||||
snooze(10000); // 10 ms
|
snooze(10000); // 10 ms
|
||||||
mutex_lock(&sVnodeMutex);
|
mutex_lock(&sVnodeMutex);
|
||||||
goto restart;
|
goto restart;
|
||||||
@@ -1323,8 +1327,11 @@ restart:
|
|||||||
mutex_lock(&sVnodeMutex);
|
mutex_lock(&sVnodeMutex);
|
||||||
|
|
||||||
if (status != B_OK) {
|
if (status != B_OK) {
|
||||||
if (gotNode)
|
if (gotNode) {
|
||||||
|
mutex_unlock(&sVnodeMutex);
|
||||||
FS_CALL(vnode, put_vnode, reenter);
|
FS_CALL(vnode, put_vnode, reenter);
|
||||||
|
mutex_lock(&sVnodeMutex);
|
||||||
|
}
|
||||||
|
|
||||||
goto err1;
|
goto err1;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user