Completed vfs_bind_mount_directory() support

We don't need to explicitly track the covered/covering nodes per mount
after all. In fs_unmount() we iterate through all vnodes multiple times
anyway and can deal with the covers/covered_by vnodes there. Also, the
root vnode doesn't need to be handled specially anymore.
This commit is contained in:
Ingo Weinhold
2011-07-17 16:54:23 +02:00
parent 0be138ab2f
commit fe5928847a
+69 -24
View File
@@ -5034,8 +5034,6 @@ vfs_bind_mount_directory(dev_t mountID, ino_t nodeID, dev_t coveredMountID,
inc_vnode_ref_count(vnode); inc_vnode_ref_count(vnode);
inc_vnode_ref_count(coveredVnode); inc_vnode_ref_count(coveredVnode);
// TODO: This relationship must be tracked, so it can be dissolved on unmount.
return B_OK; return B_OK;
} }
@@ -7473,11 +7471,20 @@ fs_unmount(char* path, dev_t mountID, uint32 flags, bool kernel)
// make sure all of them are not busy or have refs on them // make sure all of them are not busy or have refs on them
VnodeList::Iterator iterator = mount->vnodes.GetIterator(); VnodeList::Iterator iterator = mount->vnodes.GetIterator();
while (struct vnode* vnode = iterator.Next()) { while (struct vnode* vnode = iterator.Next()) {
// The root vnode ref_count needs to be 1 here (the mount has a if (vnode->IsBusy()) {
// reference). busy = true;
if (vnode->IsBusy() break;
|| ((vnode->ref_count != 0 && mount->root_vnode != vnode) }
|| (vnode->ref_count != 1 && mount->root_vnode == vnode))) {
// check the vnode's ref count -- subtract additional references for
// covering
int32 refCount = vnode->ref_count;
if (vnode->covers != NULL)
refCount--;
if (vnode->covered_by != NULL)
refCount--;
if (refCount != 0) {
// there are still vnodes in use on this mount, so we cannot // there are still vnodes in use on this mount, so we cannot
// unmount yet // unmount yet
busy = true; busy = true;
@@ -7515,37 +7522,75 @@ fs_unmount(char* path, dev_t mountID, uint32 flags, bool kernel)
vnodesWriteLocker.Lock(); vnodesWriteLocker.Lock();
} }
// we can safely continue, mark all of the vnodes busy and this mount // We can safely continue. Mark all of the vnodes busy and this mount
// structure in unmounting state // structure in unmounting state. Also undo the vnode covers/covered_by
// links.
mount->unmounting = true; mount->unmounting = true;
VnodeList::Iterator iterator = mount->vnodes.GetIterator(); VnodeList::Iterator iterator = mount->vnodes.GetIterator();
while (struct vnode* vnode = iterator.Next()) { while (struct vnode* vnode = iterator.Next()) {
// Remove all covers/covered_by links from other mounts' nodes to this
// vnode and adjust the node ref count accordingly. We will release the
// references to the external vnodes below.
if (Vnode* coveredNode = vnode->covers) {
if (Vnode* coveringNode = vnode->covered_by) {
// We have both covered and covering vnodes, so just remove us
// from the chain.
coveredNode->covered_by = coveringNode;
coveringNode->covers = coveredNode;
vnode->ref_count -= 2;
vnode->covered_by = NULL;
vnode->covers = NULL;
vnode->SetCovering(false);
vnode->SetCovered(false);
} else {
// We only have a covered vnode. Remove its link to us.
coveredNode->covered_by = NULL;
coveredNode->SetCovered(false);
vnode->ref_count--;
// If the other node is an external vnode, we keep its link
// link around so we can put the reference later on. Otherwise
// we get rid of it right now.
if (coveredNode->mount == mount) {
vnode->covers = NULL;
coveredNode->ref_count--;
}
}
} else if (Vnode* coveringNode = vnode->covered_by) {
// We only have a covering vnode. Remove its link to us.
coveringNode->covers = NULL;
coveringNode->SetCovering(false);
vnode->ref_count--;
// If the other node is an external vnode, we keep its link
// link around so we can put the reference later on. Otherwise
// we get rid of it right now.
if (coveringNode->mount == mount) {
vnode->covered_by = NULL;
coveringNode->ref_count--;
}
}
vnode->SetBusy(true); vnode->SetBusy(true);
vnode_to_be_freed(vnode); vnode_to_be_freed(vnode);
} }
// The ref_count of the root node is 1 at this point, see above why this is
mount->root_vnode->ref_count--;
vnode_to_be_freed(mount->root_vnode);
Vnode* coveredNode = mount->root_vnode->covers;
coveredNode->covered_by = mount->root_vnode->covered_by;
coveredNode->SetCovered(coveredNode->covered_by != NULL);
mount->root_vnode->covered_by = NULL;
mount->root_vnode->SetCovered(false);
mount->root_vnode->SetCovering(false);
vnodesWriteLocker.Unlock(); vnodesWriteLocker.Unlock();
put_vnode(coveredNode);
// Free all vnodes associated with this mount. // Free all vnodes associated with this mount.
// They will be removed from the mount list by free_vnode(), so // They will be removed from the mount list by free_vnode(), so
// we don't have to do this. // we don't have to do this.
while (struct vnode* vnode = mount->vnodes.Head()) while (struct vnode* vnode = mount->vnodes.Head()) {
// Put the references to external covered/covering vnodes we kept above.
if (Vnode* coveredNode = vnode->covers)
put_vnode(coveredNode);
if (Vnode* coveringNode = vnode->covered_by)
put_vnode(coveringNode);
free_vnode(vnode, false); free_vnode(vnode, false);
}
// remove the mount structure from the hash table // remove the mount structure from the hash table
mutex_lock(&sMountMutex); mutex_lock(&sMountMutex);