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
|
void
|
||||||
Volume::_RemoveNodeAndVNode(Node* node)
|
Volume::_RemoveNodeAndVNode(Node* node)
|
||||||
{
|
{
|
||||||
// we get and put the vnode to notify the VFS
|
// If the node is known to the VFS, we get the vnode, remove it, and put it,
|
||||||
// TODO: We should probably only do that, if the node is known to the
|
// so that the VFS will discard it as soon as possible (i.e. now, if no one
|
||||||
// VFS in the first place.
|
// else is using it).
|
||||||
Node* dummyNode;
|
NodeWriteLocker nodeWriteLocker(node);
|
||||||
bool gotVNode = GetVNode(node->ID(), dummyNode) == B_OK;
|
|
||||||
|
|
||||||
|
// Remove the node from its parent and the volume. This makes the node
|
||||||
|
// inaccessible via the get_vnode() and lookup() hooks.
|
||||||
_RemoveNode(node);
|
_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());
|
RemoveVNode(node->ID());
|
||||||
PutVNode(node->ID());
|
PutVNode(node->ID());
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user