Add a method for automatically DecRef'ing objects outside of mountMu.

PiperOrigin-RevId: 558912220
This commit is contained in:
Lucas Manning
2023-08-21 15:14:06 -07:00
committed by gVisor bot
parent 485265c9c8
commit 848ec339a4
5 changed files with 138 additions and 144 deletions
+10 -26
View File
@@ -208,13 +208,12 @@ func (d *Dentry) OnZeroWatches(ctx context.Context) {
// AbortDeleteDentry or CommitDeleteDentry depending on the deletion's outcome.
// +checklocksacquire:d.mu
func (vfs *VirtualFilesystem) PrepareDeleteDentry(mntns *MountNamespace, d *Dentry) error {
vfs.mountMu.Lock()
vfs.lockMounts()
defer vfs.unlockMounts(context.Background())
if mntns.mountpoints[d] != 0 {
vfs.mountMu.Unlock()
return linuxerr.EBUSY // +checklocksforce: inconsistent return.
}
d.mu.Lock()
vfs.mountMu.Unlock()
// Return with d.mu locked to block attempts to mount over it; it will be
// unlocked by AbortDeleteDentry or CommitDeleteDentry.
return nil
@@ -266,20 +265,18 @@ func (vfs *VirtualFilesystem) InvalidateDentry(ctx context.Context, d *Dentry) [
// +checklocksacquire:from.mu
// +checklocksacquire:to.mu
func (vfs *VirtualFilesystem) PrepareRenameDentry(mntns *MountNamespace, from, to *Dentry) error {
vfs.mountMu.Lock()
vfs.lockMounts()
defer vfs.unlockMounts(context.Background())
if mntns.mountpoints[from] != 0 {
vfs.mountMu.Unlock()
return linuxerr.EBUSY // +checklocksforce: no locks acquired.
}
if to != nil {
if mntns.mountpoints[to] != 0 {
vfs.mountMu.Unlock()
return linuxerr.EBUSY // +checklocksforce: no locks acquired.
}
to.mu.Lock()
}
from.mu.Lock()
vfs.mountMu.Unlock()
// Return with from.mu and to.mu locked, which will be unlocked by
// AbortRenameDentry, CommitRenameReplaceDentry, or
// CommitRenameExchangeDentry.
@@ -334,29 +331,16 @@ func (vfs *VirtualFilesystem) CommitRenameExchangeDentry(from, to *Dentry) {
// forgetDeadMountpoint is analogous to Linux's
// fs/namespace.c:__detach_mounts().
func (vfs *VirtualFilesystem) forgetDeadMountpoint(ctx context.Context, d *Dentry, skipDecRef bool) []refs.RefCounter {
var (
vdsToDecRef []VirtualDentry
mountsToDecRef []*Mount
)
vfs.mountMu.Lock()
vfs.lockMounts()
defer vfs.unlockMounts(ctx)
vfs.mounts.seq.BeginWrite()
for mnt := range vfs.mountpoints[d] {
vdsToDecRef, mountsToDecRef = vfs.umountRecursiveLocked(mnt, &umountRecursiveOptions{}, vdsToDecRef, mountsToDecRef)
vfs.umountRecursiveLocked(mnt, &umountRecursiveOptions{})
}
vfs.mounts.seq.EndWrite()
vfs.mountMu.Unlock()
rcs := make([]refs.RefCounter, 0, len(vdsToDecRef)+len(mountsToDecRef))
for _, vd := range vdsToDecRef {
rcs = append(rcs, vd)
}
for _, mnt := range mountsToDecRef {
rcs = append(rcs, mnt)
}
var rcs []refs.RefCounter
if skipDecRef {
return rcs
rcs = vfs.PopDelayedDecRefs()
}
for _, rc := range rcs {
rc.DecRef(ctx)
}
return nil
return rcs
}
+51 -89
View File
@@ -135,8 +135,8 @@ func newMount(vfs *VirtualFilesystem, fs *Filesystem, root *Dentry, mntns *Mount
// Options returns a copy of the MountOptions currently applicable to mnt.
func (mnt *Mount) Options() MountOptions {
mnt.vfs.mountMu.Lock()
defer mnt.vfs.mountMu.Unlock()
mnt.vfs.lockMounts()
defer mnt.vfs.unlockMounts(context.Background())
return MountOptions{
Flags: mnt.Flags,
ReadOnly: mnt.ReadOnly(),
@@ -144,8 +144,8 @@ func (mnt *Mount) Options() MountOptions {
}
func (mnt *Mount) generateOptionalTags() string {
mnt.vfs.mountMu.Lock()
defer mnt.vfs.mountMu.Unlock()
mnt.vfs.lockMounts()
defer mnt.vfs.unlockMounts(context.Background())
// TODO(b/249777195): Support MS_SLAVE and MS_UNBINDABLE propagation types.
var optional string
if mnt.isShared {
@@ -203,7 +203,8 @@ func (vfs *VirtualFilesystem) ConnectMountAt(ctx context.Context, creds *auth.Cr
if err != nil {
return err
}
vfs.mountMu.Lock()
vfs.lockMounts()
defer vfs.unlockMounts(ctx)
tree := vfs.preparePropagationTree(mnt, vd)
// Check if the new mount + all the propagation mounts puts us over the max.
if uint32(len(tree)+1)+vd.mount.ns.mounts > MountMax {
@@ -211,23 +212,14 @@ func (vfs *VirtualFilesystem) ConnectMountAt(ctx context.Context, creds *auth.Cr
// filesystem mutex in some implementations, which can lead to circular
// locking.
vfs.abortPropagationTree(ctx, tree)
vfs.mountMu.Unlock()
vd.DecRef(ctx)
vfs.delayDecRef(vd)
return linuxerr.ENOSPC
}
vdsToDecRef, err := vfs.connectMountAtLocked(ctx, mnt, vd)
defer func() {
for _, vd := range vdsToDecRef {
vd.DecRef(ctx)
}
}()
if err != nil {
if err := vfs.connectMountAtLocked(ctx, mnt, vd); err != nil {
vfs.abortPropagationTree(ctx, tree)
vfs.mountMu.Unlock()
return err
}
vfs.commitPropagationTree(ctx, tree)
vfs.mountMu.Unlock()
return nil
}
@@ -240,14 +232,13 @@ func (vfs *VirtualFilesystem) ConnectMountAt(ctx context.Context, creds *auth.Cr
// - vfs.mountMu must be locked.
//
// +checklocks:vfs.mountMu
func (vfs *VirtualFilesystem) connectMountAtLocked(ctx context.Context, mnt *Mount, vd VirtualDentry) ([]VirtualDentry, error) {
var vdsToDecRef []VirtualDentry
func (vfs *VirtualFilesystem) connectMountAtLocked(ctx context.Context, mnt *Mount, vd VirtualDentry) error {
vd.dentry.mu.Lock()
for {
if vd.mount.umounted || vd.dentry.dead {
vd.dentry.mu.Unlock()
vdsToDecRef = append(vdsToDecRef, vd)
return vdsToDecRef, linuxerr.ENOENT
vfs.delayDecRef(vd)
return linuxerr.ENOENT
}
// vd might have been mounted over between vfs.GetDentryAt() and
// vfs.mountMu.Lock().
@@ -268,7 +259,7 @@ func (vfs *VirtualFilesystem) connectMountAtLocked(ctx context.Context, mnt *Mou
// This can't fail since we're holding vfs.mountMu.
nextmnt.root.IncRef()
vd.dentry.mu.Unlock()
vdsToDecRef = append(vdsToDecRef, vd)
vfs.delayDecRef(vd)
vd = VirtualDentry{
mount: nextmnt,
dentry: nextmnt.root,
@@ -283,7 +274,7 @@ func (vfs *VirtualFilesystem) connectMountAtLocked(ctx context.Context, mnt *Mou
vfs.connectLocked(mnt, vd, mntns)
vfs.mounts.seq.EndWrite()
vd.dentry.mu.Unlock()
return vdsToDecRef, nil
return nil
}
// CloneMountAt returns a new mount with the same fs, specified root and
@@ -291,8 +282,8 @@ func (vfs *VirtualFilesystem) connectMountAtLocked(ctx context.Context, mnt *Mou
// automatically made a peer of mnt. If mount options are nil, mnt's
// options are copied.
func (vfs *VirtualFilesystem) CloneMountAt(mnt *Mount, root *Dentry, mopts *MountOptions) *Mount {
vfs.mountMu.Lock()
defer vfs.mountMu.Unlock()
vfs.lockMounts()
defer vfs.unlockMounts(context.Background())
clone := vfs.cloneMount(mnt, root, mopts)
return clone
}
@@ -333,32 +324,24 @@ func (vfs *VirtualFilesystem) BindAt(ctx context.Context, creds *auth.Credential
return nil, err
}
vfs.mountMu.Lock()
vfs.lockMounts()
defer vfs.unlockMounts(ctx)
clone := vfs.cloneMount(sourceVd.mount, sourceVd.dentry, nil)
defer clone.DecRef(ctx)
vfs.delayDecRef(clone)
tree := vfs.preparePropagationTree(clone, targetVd)
if uint32(1+len(tree))+targetVd.mount.ns.mounts > MountMax {
vfs.setPropagation(clone, linux.MS_PRIVATE)
vfs.abortPropagationTree(ctx, tree)
vfs.mountMu.Unlock()
targetVd.DecRef(ctx)
vfs.delayDecRef(targetVd)
return nil, linuxerr.ENOSPC
}
vdsToDecRef, err := vfs.connectMountAtLocked(ctx, clone, targetVd)
defer func() {
for _, vd := range vdsToDecRef {
vd.DecRef(ctx)
}
}()
if err != nil {
if err := vfs.connectMountAtLocked(ctx, clone, targetVd); err != nil {
vfs.setPropagation(clone, linux.MS_PRIVATE)
vfs.abortPropagationTree(ctx, tree)
vfs.mountMu.Unlock()
return nil, err
}
vfs.commitPropagationTree(ctx, tree)
vfs.mountMu.Unlock()
return clone, nil
}
@@ -419,16 +402,15 @@ func (vfs *VirtualFilesystem) UmountAt(ctx context.Context, creds *auth.Credenti
return linuxerr.EINVAL
}
vfs.mountMu.Lock()
vfs.lockMounts()
defer vfs.unlockMounts(ctx)
if mntns := MountNamespaceFromContext(ctx); mntns != nil {
defer mntns.DecRef(ctx)
vfs.delayDecRef(mntns)
if mntns != vd.mount.ns {
vfs.mountMu.Unlock()
return linuxerr.EINVAL
}
if vd.mount == vd.mount.ns.root {
vfs.mountMu.Unlock()
return linuxerr.EINVAL
}
}
@@ -455,7 +437,6 @@ func (vfs *VirtualFilesystem) UmountAt(ctx context.Context, creds *auth.Credenti
if opts.Flags&linux.MNT_DETACH == 0 {
if len(vd.mount.children) != 0 {
vfs.mounts.seq.EndWrite()
vfs.mountMu.Unlock()
return linuxerr.EBUSY
}
// We are holding a reference on vd.mount.
@@ -465,28 +446,16 @@ func (vfs *VirtualFilesystem) UmountAt(ctx context.Context, creds *auth.Credenti
}
if vd.mount.refs.Load()&^math.MinInt64 != expectedRefs { // mask out MSB
vfs.mounts.seq.EndWrite()
vfs.mountMu.Unlock()
return linuxerr.EBUSY
}
}
var (
vdsToDecRef []VirtualDentry
mountsToDecRef []*Mount
)
for _, mnt := range umountTree {
vdsToDecRef, mountsToDecRef = vfs.umountRecursiveLocked(mnt, &umountRecursiveOptions{
vfs.umountRecursiveLocked(mnt, &umountRecursiveOptions{
eager: opts.Flags&linux.MNT_DETACH == 0,
disconnectHierarchy: true,
}, vdsToDecRef, mountsToDecRef)
})
}
vfs.mounts.seq.EndWrite()
vfs.mountMu.Unlock()
for _, vd := range vdsToDecRef {
vd.DecRef(ctx)
}
for _, m := range mountsToDecRef {
m.DecRef(ctx)
}
return nil
}
@@ -508,12 +477,7 @@ type umountRecursiveOptions struct {
disconnectHierarchy bool
}
// umountRecursiveLocked marks mnt and its descendants as umounted. It does not
// release mount or dentry references; instead, it appends VirtualDentries and
// Mounts on which references must be dropped to vdsToDecRef and mountsToDecRef
// respectively, and returns updated slices. (This is necessary because
// filesystem locks possibly taken by DentryImpl.DecRef() may precede
// vfs.mountMu in the lock order, and Mount.DecRef() may lock vfs.mountMu.)
// umountRecursiveLocked marks mnt and its descendants as umounted.
//
// umountRecursiveLocked is analogous to Linux's fs/namespace.c:umount_tree().
//
@@ -522,12 +486,12 @@ type umountRecursiveOptions struct {
// - vfs.mounts.seq must be in a writer critical section.
//
// +checklocks:vfs.mountMu
func (vfs *VirtualFilesystem) umountRecursiveLocked(mnt *Mount, opts *umountRecursiveOptions, vdsToDecRef []VirtualDentry, mountsToDecRef []*Mount) ([]VirtualDentry, []*Mount) {
func (vfs *VirtualFilesystem) umountRecursiveLocked(mnt *Mount, opts *umountRecursiveOptions) {
if !mnt.umounted {
mnt.umounted = true
mountsToDecRef = append(mountsToDecRef, mnt)
vfs.delayDecRef(mnt)
if parent := mnt.parent(); parent != nil && (opts.disconnectHierarchy || !parent.umounted) {
vdsToDecRef = append(vdsToDecRef, vfs.disconnectLocked(mnt))
vfs.delayDecRef(vfs.disconnectLocked(mnt))
}
if mnt.isShared {
vfs.setPropagation(mnt, linux.MS_PRIVATE)
@@ -545,9 +509,8 @@ func (vfs *VirtualFilesystem) umountRecursiveLocked(mnt *Mount, opts *umountRecu
}
}
for child := range mnt.children {
vdsToDecRef, mountsToDecRef = vfs.umountRecursiveLocked(child, opts, vdsToDecRef, mountsToDecRef)
vfs.umountRecursiveLocked(child, opts)
}
return vdsToDecRef, mountsToDecRef
}
// connectLocked makes vd the mount parent/point for mnt. It consumes
@@ -664,21 +627,20 @@ func (mnt *Mount) DecRef(ctx context.Context) {
}
func (mnt *Mount) destroy(ctx context.Context) {
var vd VirtualDentry
if mnt.parent() != nil {
mnt.vfs.mountMu.Lock()
mnt.vfs.lockMounts()
mnt.vfs.mounts.seq.BeginWrite()
vd = mnt.vfs.disconnectLocked(mnt)
vd := mnt.vfs.disconnectLocked(mnt)
if vd.Ok() {
mnt.vfs.delayDecRef(vd)
}
mnt.vfs.mounts.seq.EndWrite()
mnt.vfs.mountMu.Unlock()
mnt.vfs.unlockMounts(ctx)
}
if mnt.root != nil {
mnt.root.DecRef(ctx)
}
mnt.fs.DecRef(ctx)
if vd.Ok() {
vd.DecRef(ctx)
}
}
// RefType implements refs.CheckedObject.Type.
@@ -869,32 +831,32 @@ retry:
// The current root and the new root must be in the context's mount namespace.
ns := MountNamespaceFromContext(ctx)
defer ns.DecRef(ctx)
vfs.mountMu.Lock()
vfs.lockMounts()
if rootVd.mount.ns != ns || newRootVd.mount.ns != ns {
vfs.mountMu.Unlock()
vfs.unlockMounts(ctx)
return linuxerr.EINVAL
}
// Either the mount point at new_root, or the parent mount of that mount
// point, has propagation type MS_SHARED.
if newRootParent := newRootVd.mount.parent(); newRootVd.mount.isShared || newRootParent.isShared {
vfs.mountMu.Unlock()
vfs.unlockMounts(ctx)
return linuxerr.EINVAL
}
// put_old is a mount point and has the propagation type MS_SHARED.
if putOldVd.mount.root == putOldVd.dentry && putOldVd.mount.isShared {
vfs.mountMu.Unlock()
vfs.unlockMounts(ctx)
return linuxerr.EINVAL
}
if !vfs.mounts.seq.BeginWriteOk(epoch) {
// Checks above raced with a mount change.
vfs.mountMu.Unlock()
vfs.unlockMounts(ctx)
goto retry
}
defer vfs.mountMu.Unlock()
defer vfs.unlockMounts(ctx)
mp := vfs.disconnectLocked(newRootVd.mount)
mp.DecRef(ctx)
vfs.delayDecRef(mp)
rootMp := vfs.disconnectLocked(rootVd.mount)
putOldVd.IncRef()
@@ -907,15 +869,15 @@ retry:
rootMp.dentry.mu.Unlock()
vfs.mounts.seq.EndWrite()
newRootVd.mount.DecRef(ctx)
rootVd.mount.DecRef(ctx)
vfs.delayDecRef(newRootVd.mount)
vfs.delayDecRef(rootVd.mount)
return nil
}
// SetMountReadOnly sets the mount as ReadOnly.
func (vfs *VirtualFilesystem) SetMountReadOnly(mnt *Mount, ro bool) error {
vfs.mountMu.Lock()
defer vfs.mountMu.Unlock()
vfs.lockMounts()
defer vfs.unlockMounts(context.Background())
return mnt.setReadOnlyLocked(ro)
}
@@ -991,14 +953,14 @@ func (mnt *Mount) Root() *Dentry {
func (vfs *VirtualFilesystem) GenerateProcMounts(ctx context.Context, taskRootDir VirtualDentry, buf *bytes.Buffer) {
rootMnt := taskRootDir.mount
vfs.mountMu.Lock()
vfs.lockMounts()
mounts := rootMnt.submountsLocked()
// Take a reference on mounts since we need to drop vfs.mountMu before
// calling vfs.PathnameReachable() (=> FilesystemImpl.PrependPath()).
for _, mnt := range mounts {
mnt.IncRef()
}
vfs.mountMu.Unlock()
vfs.unlockMounts(ctx)
defer func() {
for _, mnt := range mounts {
mnt.DecRef(ctx)
@@ -1055,7 +1017,7 @@ func (vfs *VirtualFilesystem) GenerateProcMounts(ctx context.Context, taskRootDi
func (vfs *VirtualFilesystem) GenerateProcMountInfo(ctx context.Context, taskRootDir VirtualDentry, buf *bytes.Buffer) {
rootMnt := taskRootDir.mount
vfs.mountMu.Lock()
vfs.lockMounts()
mounts := rootMnt.submountsLocked()
// Take a reference on mounts since we need to drop vfs.mountMu before
// calling vfs.PathnameReachable() (=> FilesystemImpl.PrependPath()) or
@@ -1063,7 +1025,7 @@ func (vfs *VirtualFilesystem) GenerateProcMountInfo(ctx context.Context, taskRoo
for _, mnt := range mounts {
mnt.IncRef()
}
vfs.mountMu.Unlock()
vfs.unlockMounts(ctx)
defer func() {
for _, mnt := range mounts {
mnt.DecRef(ctx)
+11 -24
View File
@@ -130,14 +130,15 @@ type cloneEntry struct {
parentMount *Mount
}
// +checklocks:vfs.mountMu
func (vfs *VirtualFilesystem) updateRootAndCWD(ctx context.Context, root *VirtualDentry, cwd *VirtualDentry, src *Mount, dst *Mount) {
if root.mount == src {
root.mount.DecRef(ctx)
vfs.delayDecRef(root.mount)
root.mount = dst
root.mount.IncRef()
}
if cwd.mount == src {
cwd.mount.DecRef(ctx)
vfs.delayDecRef(cwd.mount)
cwd.mount = dst
cwd.mount.IncRef()
}
@@ -166,15 +167,8 @@ func (vfs *VirtualFilesystem) CloneMountNamespace(
}
newns.Refs = nsfs.GetNamespaceInode(ctx, newns)
vdsToDecRef := []VirtualDentry{}
defer func() {
for _, vd := range vdsToDecRef {
vd.DecRef(ctx)
}
}()
vfs.mountMu.Lock()
defer vfs.mountMu.Unlock()
vfs.lockMounts()
defer vfs.unlockMounts(ctx)
ns.root.root.IncRef()
ns.root.fs.IncRef()
@@ -196,9 +190,8 @@ func (vfs *VirtualFilesystem) CloneMountNamespace(
}
vd.IncRef()
vds, err := vfs.connectMountAtLocked(ctx, m, vd)
m.DecRef(ctx)
vdsToDecRef = append(vdsToDecRef, vds...)
err := vfs.connectMountAtLocked(ctx, m, vd)
vfs.delayDecRef(m)
if err != nil {
newns.DecRef(ctx)
return nil, err
@@ -215,19 +208,13 @@ func (vfs *VirtualFilesystem) CloneMountNamespace(
// Destroy implements nsfs.Namespace.Destroy.
func (mntns *MountNamespace) Destroy(ctx context.Context) {
vfs := mntns.root.fs.VirtualFilesystem()
vfs.mountMu.Lock()
vfs.lockMounts()
vfs.mounts.seq.BeginWrite()
vdsToDecRef, mountsToDecRef := vfs.umountRecursiveLocked(mntns.root, &umountRecursiveOptions{
vfs.umountRecursiveLocked(mntns.root, &umountRecursiveOptions{
disconnectHierarchy: true,
}, nil, nil)
})
vfs.mounts.seq.EndWrite()
vfs.mountMu.Unlock()
for _, vd := range vdsToDecRef {
vd.DecRef(ctx)
}
for _, mnt := range mountsToDecRef {
mnt.DecRef(ctx)
}
vfs.unlockMounts(ctx)
}
// Type implements nsfs.Namespace.Type.
+5 -5
View File
@@ -136,7 +136,7 @@ func (vfs *VirtualFilesystem) commitPropagationTree(ctx context.Context, tree ma
vfs.connectLocked(mnt, vd, mntns)
}
vd.dentry.mu.Unlock()
mnt.DecRef(ctx)
vfs.delayDecRef(mnt)
}
vfs.mounts.seq.EndWrite()
}
@@ -147,9 +147,9 @@ func (vfs *VirtualFilesystem) commitPropagationTree(ctx context.Context, tree ma
// +checklocks:vfs.mountMu
func (vfs *VirtualFilesystem) abortPropagationTree(ctx context.Context, tree map[*Mount]VirtualDentry) {
for mnt, vd := range tree {
vd.DecRef(ctx)
vfs.delayDecRef(vd)
vfs.delayDecRef(mnt)
vfs.setPropagation(mnt, linux.MS_PRIVATE)
mnt.DecRef(ctx)
}
}
@@ -182,8 +182,8 @@ func (vfs *VirtualFilesystem) SetMountPropagationAt(ctx context.Context, creds *
// SetMountPropagation changes the propagation type of the mount.
func (vfs *VirtualFilesystem) SetMountPropagation(mnt *Mount, propFlags uint32) {
vfs.mountMu.Lock()
defer vfs.mountMu.Unlock()
vfs.lockMounts()
defer vfs.unlockMounts(context.Background())
if propFlags&(linux.MS_SHARED|linux.MS_PRIVATE) != 0 {
vfs.setPropagation(mnt, propFlags)
} else {
+61
View File
@@ -47,6 +47,7 @@ import (
"gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/eventchannel"
"gvisor.dev/gvisor/pkg/fspath"
"gvisor.dev/gvisor/pkg/refs"
"gvisor.dev/gvisor/pkg/sentry/fsmetric"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/socket/unix/transport"
@@ -144,6 +145,13 @@ type VirtualFilesystem struct {
// mountPromises contains all unresolved mount promises.
mountPromisesMu sync.RWMutex `state:"nosave"`
mountPromises map[VirtualDentry]*waiter.Queue
// toDecRef contains all the reference counted objects that needed to be
// DecRefd while mountMu was held. It is cleared every time unlockMounts is
// called and protected by mountMu.
//
// +checklocks:mountMu
toDecRef map[refs.RefCounter]int
}
// Init initializes a new VirtualFilesystem with no mounts or FilesystemTypes.
@@ -161,6 +169,9 @@ func (vfs *VirtualFilesystem) Init(ctx context.Context) error {
vfs.mounts.Init()
vfs.groupIDBitmap = bitmap.New(1024)
vfs.mountPromises = make(map[VirtualDentry]*waiter.Queue)
vfs.mountMu.Lock()
vfs.toDecRef = make(map[refs.RefCounter]int)
vfs.mountMu.Unlock()
// Construct vfs.anonMount.
anonfsDevMinor, err := vfs.GetAnonBlockDevMinor()
@@ -961,6 +972,56 @@ func (vfs *VirtualFilesystem) maybeResolveMountPromise(vd VirtualDentry) {
delete(vfs.mountPromises, vd)
}
// PopDelayedDecRefs returns a list of reference counted objects that collected
// while mountMu was held that must be DecRef'd outside of mountMu.
//
// +checklocks:vfs.mountMu
func (vfs *VirtualFilesystem) PopDelayedDecRefs() []refs.RefCounter {
var rcs []refs.RefCounter
for rc, refs := range vfs.toDecRef {
for i := 0; i < refs; i++ {
rcs = append(rcs, rc)
}
}
return rcs
}
// delayDecRef saves a reference counted object so that it can be DecRef'd
// outside of vfs.mountMu. This is necessary because filesystem locks possibly
// taken by DentryImpl.DecRef() may precede vfs.mountMu in the lock order, and
// Mount.DecRef() may lock vfs.mountMu.
//
// +checklocks:vfs.mountMu
func (vfs *VirtualFilesystem) delayDecRef(rc refs.RefCounter) {
vfs.toDecRef[rc]++
}
// Use this instead of vfs.mountMu.Lock().
//
// +checklocksacquire:vfs.mountMu
func (vfs *VirtualFilesystem) lockMounts() {
vfs.mountMu.Lock()
}
// Use this instead of vfs.mountMu.Unlock(). This method DecRefs any reference
// counted objects that were collected while mountMu was held.
//
// +checklocksrelease:vfs.mountMu
func (vfs *VirtualFilesystem) unlockMounts(ctx context.Context) {
if len(vfs.toDecRef) == 0 {
vfs.mountMu.Unlock()
return
}
toDecRef := vfs.toDecRef
vfs.toDecRef = map[refs.RefCounter]int{}
vfs.mountMu.Unlock()
for rc, refs := range toDecRef {
for i := 0; i < refs; i++ {
rc.DecRef(ctx)
}
}
}
// A VirtualDentry represents a node in a VFS tree, by combining a Dentry
// (which represents a node in a Filesystem's tree) and a Mount (which
// represents the Filesystem's position in a VFS mount tree).