Volume::_RemoveNodeAndVNode(): Squash TODO
Only get/remove/put the vnode when the node is actually known to the VFS. This does not only save unnecessary work, it also solves a (temporary) deadlock -- at least partially. If another thread caused a call to our get_vnode() hook just before, it would block on the volume lock we're holding when adding/removing packages. The vnode would be marked busy until the other thread's request was fulfilled and our call to get_vnode() would block until timing out. Now we're calling get_vnode() only, if we already know that the VFS already has a valid vnode. There still remains a race condition. If the VFS discards the vnode right before we call get_vnode(), we essentially have the same situation as before (i.e. us calling get_vnode() although the vnode is no longer known to the VFS) with the same potential problem. For a real solution we need a get_vnode() variant which can be told not to block.
This commit is contained in:
@@ -1474,15 +1474,36 @@ Volume::_RemoveNode(Node* node)
|
||||
void
|
||||
Volume::_RemoveNodeAndVNode(Node* node)
|
||||
{
|
||||
// we get and put the vnode to notify the VFS
|
||||
// TODO: We should probably only do that, if the node is known to the
|
||||
// VFS in the first place.
|
||||
Node* dummyNode;
|
||||
bool gotVNode = GetVNode(node->ID(), dummyNode) == B_OK;
|
||||
// If the node is known to the VFS, we get the vnode, remove it, and put it,
|
||||
// so that the VFS will discard it as soon as possible (i.e. now, if no one
|
||||
// else is using it).
|
||||
NodeWriteLocker nodeWriteLocker(node);
|
||||
|
||||
// Remove the node from its parent and the volume. This makes the node
|
||||
// inaccessible via the get_vnode() and lookup() hooks.
|
||||
_RemoveNode(node);
|
||||
|
||||
if (gotVNode) {
|
||||
bool getVNode = node->IsKnownToVFS();
|
||||
|
||||
nodeWriteLocker.Unlock();
|
||||
|
||||
// Get a vnode reference, if the node is already known to the VFS.
|
||||
Node* dummyNode;
|
||||
if (getVNode && GetVNode(node->ID(), dummyNode) == B_OK) {
|
||||
// TODO: There still is a race condition here which we can't avoid
|
||||
// without more help from the VFS. Right after we drop the write
|
||||
// lock a vnode for the node could be discarded by the VFS. At that
|
||||
// point another thread trying to get the vnode by ID would create
|
||||
// a vnode, mark it busy and call our get_vnode() hook. It would
|
||||
// block since we (i.e. the package loader thread executing this
|
||||
// method) still have the volume write lock. Our get_vnode() call
|
||||
// would block, since it finds the vnode marked busy. It times out
|
||||
// eventually, but until then a good deal of FS operations might
|
||||
// block as well due to us holding the volume lock and probably
|
||||
// several node locks as well. A get_vnode*() variant (e.g.
|
||||
// get_vnode_etc() with flags parameter) that wouldn't block and
|
||||
// only get the vnode, if already loaded and non-busy, would be
|
||||
// perfect here.
|
||||
RemoveVNode(node->ID());
|
||||
PutVNode(node->ID());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user