mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Add leak checking to vfs2 structures that cannot use the refs_vfs2 template.
Updates #1486. PiperOrigin-RevId: 338832085
This commit is contained in:
@@ -54,6 +54,7 @@ go_library(
|
||||
"//pkg/log",
|
||||
"//pkg/p9",
|
||||
"//pkg/refs",
|
||||
"//pkg/refsvfs2",
|
||||
"//pkg/safemem",
|
||||
"//pkg/sentry/fs/fsutil",
|
||||
"//pkg/sentry/fs/lock",
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/p9"
|
||||
"gvisor.dev/gvisor/pkg/refsvfs2"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/pipe"
|
||||
"gvisor.dev/gvisor/pkg/sentry/socket/unix/transport"
|
||||
@@ -100,6 +101,9 @@ func (d *dentry) createSyntheticChildLocked(opts *createSyntheticOpts) {
|
||||
hostFD: -1,
|
||||
nlink: uint32(2),
|
||||
}
|
||||
if refsvfs2.LeakCheckEnabled() {
|
||||
refsvfs2.Register(child, "gofer.dentry")
|
||||
}
|
||||
switch opts.mode.FileType() {
|
||||
case linux.S_IFDIR:
|
||||
// Nothing else needs to be done.
|
||||
|
||||
@@ -46,6 +46,8 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
"gvisor.dev/gvisor/pkg/p9"
|
||||
refs_vfs1 "gvisor.dev/gvisor/pkg/refs"
|
||||
"gvisor.dev/gvisor/pkg/refsvfs2"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fs/fsutil"
|
||||
fslock "gvisor.dev/gvisor/pkg/sentry/fs/lock"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
||||
@@ -109,8 +111,8 @@ type filesystem struct {
|
||||
|
||||
// cachedDentries contains all dentries with 0 references. (Due to race
|
||||
// conditions, it may also contain dentries with non-zero references.)
|
||||
// cachedDentriesLen is the number of dentries in cachedDentries. These
|
||||
// fields are protected by renameMu.
|
||||
// cachedDentriesLen is the number of dentries in cachedDentries. These fields
|
||||
// are protected by renameMu.
|
||||
cachedDentries dentryList
|
||||
cachedDentriesLen uint64
|
||||
|
||||
@@ -134,6 +136,10 @@ type filesystem struct {
|
||||
|
||||
// savedDentryRW records open read/write handles during save/restore.
|
||||
savedDentryRW map[*dentry]savedDentryRW
|
||||
|
||||
// released is nonzero once filesystem.Release has been called. It is accessed
|
||||
// with atomic memory operations.
|
||||
released int32
|
||||
}
|
||||
|
||||
// +stateify savable
|
||||
@@ -454,9 +460,8 @@ func (fstype FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
|
||||
return nil, nil, err
|
||||
}
|
||||
// Set the root's reference count to 2. One reference is returned to the
|
||||
// caller, and the other is deliberately leaked to prevent the root from
|
||||
// being "cached" and subsequently evicted. Its resources will still be
|
||||
// cleaned up by fs.Release().
|
||||
// caller, and the other is held by fs to prevent the root from being "cached"
|
||||
// and subsequently evicted.
|
||||
root.refs = 2
|
||||
fs.root = root
|
||||
|
||||
@@ -526,15 +531,16 @@ func (fs *filesystem) dial(ctx context.Context) error {
|
||||
|
||||
// Release implements vfs.FilesystemImpl.Release.
|
||||
func (fs *filesystem) Release(ctx context.Context) {
|
||||
mf := fs.mfp.MemoryFile()
|
||||
atomic.StoreInt32(&fs.released, 1)
|
||||
|
||||
mf := fs.mfp.MemoryFile()
|
||||
fs.syncMu.Lock()
|
||||
for d := range fs.syncableDentries {
|
||||
d.handleMu.Lock()
|
||||
d.dataMu.Lock()
|
||||
if h := d.writeHandleLocked(); h.isOpen() {
|
||||
// Write dirty cached data to the remote file.
|
||||
if err := fsutil.SyncDirtyAll(ctx, &d.cache, &d.dirty, d.size, fs.mfp.MemoryFile(), h.writeFromBlocksAt); err != nil {
|
||||
if err := fsutil.SyncDirtyAll(ctx, &d.cache, &d.dirty, d.size, mf, h.writeFromBlocksAt); err != nil {
|
||||
log.Warningf("gofer.filesystem.Release: failed to flush dentry: %v", err)
|
||||
}
|
||||
// TODO(jamieliu): Do we need to flushf/fsync d?
|
||||
@@ -555,6 +561,21 @@ func (fs *filesystem) Release(ctx context.Context) {
|
||||
// fs.
|
||||
fs.syncMu.Unlock()
|
||||
|
||||
// If leak checking is enabled, release all outstanding references in the
|
||||
// filesystem. We deliberately avoid doing this outside of leak checking; we
|
||||
// have released all external resources above rather than relying on dentry
|
||||
// destructors.
|
||||
if refs_vfs1.GetLeakMode() != refs_vfs1.NoLeakChecking {
|
||||
fs.renameMu.Lock()
|
||||
fs.root.releaseSyntheticRecursiveLocked(ctx)
|
||||
fs.evictAllCachedDentriesLocked(ctx)
|
||||
fs.renameMu.Unlock()
|
||||
|
||||
// An extra reference was held by the filesystem on the root to prevent it from
|
||||
// being cached/evicted.
|
||||
fs.root.DecRef(ctx)
|
||||
}
|
||||
|
||||
if !fs.iopts.LeakConnection {
|
||||
// Close the connection to the server. This implicitly clunks all fids.
|
||||
fs.client.Close()
|
||||
@@ -563,6 +584,31 @@ func (fs *filesystem) Release(ctx context.Context) {
|
||||
fs.vfsfs.VirtualFilesystem().PutAnonBlockDevMinor(fs.devMinor)
|
||||
}
|
||||
|
||||
// releaseSyntheticRecursiveLocked traverses the tree with root d and decrements
|
||||
// the reference count on every synthetic dentry. Synthetic dentries have one
|
||||
// reference for existence that should be dropped during filesystem.Release.
|
||||
//
|
||||
// Precondition: d.fs.renameMu is locked.
|
||||
func (d *dentry) releaseSyntheticRecursiveLocked(ctx context.Context) {
|
||||
if d.isSynthetic() {
|
||||
d.decRefLocked()
|
||||
d.checkCachingLocked(ctx)
|
||||
}
|
||||
if d.isDir() {
|
||||
var children []*dentry
|
||||
d.dirMu.Lock()
|
||||
for _, child := range d.children {
|
||||
children = append(children, child)
|
||||
}
|
||||
d.dirMu.Unlock()
|
||||
for _, child := range children {
|
||||
if child != nil {
|
||||
child.releaseSyntheticRecursiveLocked(ctx)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// dentry implements vfs.DentryImpl.
|
||||
//
|
||||
// +stateify savable
|
||||
@@ -815,6 +861,9 @@ func (fs *filesystem) newDentry(ctx context.Context, file p9file, qid p9.QID, ma
|
||||
d.nlink = uint32(attr.NLink)
|
||||
}
|
||||
d.vfsd.Init(d)
|
||||
if refsvfs2.LeakCheckEnabled() {
|
||||
refsvfs2.Register(d, "gofer.dentry")
|
||||
}
|
||||
|
||||
fs.syncMu.Lock()
|
||||
fs.syncableDentries[d] = struct{}{}
|
||||
@@ -1210,6 +1259,11 @@ func (d *dentry) decRefLocked() {
|
||||
}
|
||||
}
|
||||
|
||||
// LeakMessage implements refsvfs2.CheckedObject.LeakMessage.
|
||||
func (d *dentry) LeakMessage() string {
|
||||
return fmt.Sprintf("[gofer.dentry %p] reference count of %d instead of -1", d, atomic.LoadInt64(&d.refs))
|
||||
}
|
||||
|
||||
// InotifyWithParent implements vfs.DentryImpl.InotifyWithParent.
|
||||
func (d *dentry) InotifyWithParent(ctx context.Context, events, cookie uint32, et vfs.EventType) {
|
||||
if d.isDir() {
|
||||
@@ -1292,6 +1346,16 @@ func (d *dentry) checkCachingLocked(ctx context.Context) {
|
||||
if d.watches.Size() > 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if atomic.LoadInt32(&d.fs.released) != 0 {
|
||||
if d.parent != nil {
|
||||
d.parent.dirMu.Lock()
|
||||
delete(d.parent.children, d.name)
|
||||
d.parent.dirMu.Unlock()
|
||||
}
|
||||
d.destroyLocked(ctx)
|
||||
}
|
||||
|
||||
// If d is already cached, just move it to the front of the LRU.
|
||||
if d.cached {
|
||||
d.fs.cachedDentries.Remove(d)
|
||||
@@ -1310,6 +1374,14 @@ func (d *dentry) checkCachingLocked(ctx context.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
// Precondition: fs.renameMu must be locked for writing; it may be temporarily
|
||||
// unlocked.
|
||||
func (fs *filesystem) evictAllCachedDentriesLocked(ctx context.Context) {
|
||||
for fs.cachedDentriesLen != 0 {
|
||||
fs.evictCachedDentryLocked(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
// Preconditions:
|
||||
// * fs.renameMu must be locked for writing; it may be temporarily unlocked.
|
||||
// * fs.cachedDentriesLen != 0.
|
||||
@@ -1422,6 +1494,10 @@ func (d *dentry) destroyLocked(ctx context.Context) {
|
||||
panic("gofer.dentry.DecRef() called without holding a reference")
|
||||
}
|
||||
}
|
||||
|
||||
if refsvfs2.LeakCheckEnabled() {
|
||||
refsvfs2.Unregister(d, "gofer.dentry")
|
||||
}
|
||||
}
|
||||
|
||||
func (d *dentry) isDeleted() bool {
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/fdnotifier"
|
||||
"gvisor.dev/gvisor/pkg/p9"
|
||||
"gvisor.dev/gvisor/pkg/refsvfs2"
|
||||
"gvisor.dev/gvisor/pkg/safemem"
|
||||
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
||||
"gvisor.dev/gvisor/pkg/syserror"
|
||||
@@ -53,9 +54,7 @@ func (fs *filesystem) PrepareSave(ctx context.Context) error {
|
||||
// Purge cached dentries, which may not be reopenable after restore due to
|
||||
// permission changes.
|
||||
fs.renameMu.Lock()
|
||||
for fs.cachedDentriesLen != 0 {
|
||||
fs.evictCachedDentryLocked(ctx)
|
||||
}
|
||||
fs.evictAllCachedDentriesLocked(ctx)
|
||||
fs.renameMu.Unlock()
|
||||
|
||||
// Buffer pipe data so that it's available for reading after restore. (This
|
||||
@@ -141,6 +140,9 @@ func (d *dentry) beforeSave() {
|
||||
// afterLoad is invoked by stateify.
|
||||
func (d *dentry) afterLoad() {
|
||||
d.hostFD = -1
|
||||
if refsvfs2.LeakCheckEnabled() && atomic.LoadInt64(&d.refs) != -1 {
|
||||
refsvfs2.Register(d, "gofer.dentry")
|
||||
}
|
||||
}
|
||||
|
||||
// afterLoad is invoked by stateify.
|
||||
|
||||
@@ -23,6 +23,7 @@ go_library(
|
||||
"fstree.go",
|
||||
"overlay.go",
|
||||
"regular_file.go",
|
||||
"save_restore.go",
|
||||
],
|
||||
visibility = ["//pkg/sentry:internal"],
|
||||
deps = [
|
||||
@@ -30,6 +31,7 @@ go_library(
|
||||
"//pkg/context",
|
||||
"//pkg/fspath",
|
||||
"//pkg/log",
|
||||
"//pkg/refsvfs2",
|
||||
"//pkg/sentry/arch",
|
||||
"//pkg/sentry/fs/lock",
|
||||
"//pkg/sentry/kernel/auth",
|
||||
|
||||
@@ -33,12 +33,14 @@
|
||||
package overlay
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/fspath"
|
||||
"gvisor.dev/gvisor/pkg/refsvfs2"
|
||||
fslock "gvisor.dev/gvisor/pkg/sentry/fs/lock"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
||||
"gvisor.dev/gvisor/pkg/sentry/memmap"
|
||||
@@ -484,6 +486,9 @@ func (fs *filesystem) newDentry() *dentry {
|
||||
}
|
||||
d.lowerVDs = d.inlineLowerVDs[:0]
|
||||
d.vfsd.Init(d)
|
||||
if refsvfs2.LeakCheckEnabled() {
|
||||
refsvfs2.Register(d, "overlay.dentry")
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
@@ -583,6 +588,14 @@ func (d *dentry) destroyLocked(ctx context.Context) {
|
||||
panic("overlay.dentry.DecRef() called without holding a reference")
|
||||
}
|
||||
}
|
||||
if refsvfs2.LeakCheckEnabled() {
|
||||
refsvfs2.Unregister(d, "overlay.dentry")
|
||||
}
|
||||
}
|
||||
|
||||
// LeakMessage implements refsvfs2.CheckedObject.LeakMessage.
|
||||
func (d *dentry) LeakMessage() string {
|
||||
return fmt.Sprintf("[overlay.dentry %p] reference count of %d instead of -1", d, atomic.LoadInt64(&d.refs))
|
||||
}
|
||||
|
||||
// InotifyWithParent implements vfs.DentryImpl.InotifyWithParent.
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
// Copyright 2020 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package overlay
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/refsvfs2"
|
||||
)
|
||||
|
||||
func (d *dentry) afterLoad() {
|
||||
if refsvfs2.LeakCheckEnabled() && atomic.LoadInt64(&d.refs) != -1 {
|
||||
refsvfs2.Register(d, "overlay.dentry")
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ go_library(
|
||||
name = "verity",
|
||||
srcs = [
|
||||
"filesystem.go",
|
||||
"save_restore.go",
|
||||
"verity.go",
|
||||
],
|
||||
visibility = ["//pkg/sentry:internal"],
|
||||
@@ -15,6 +16,7 @@ go_library(
|
||||
"//pkg/fspath",
|
||||
"//pkg/marshal/primitive",
|
||||
"//pkg/merkletree",
|
||||
"//pkg/refsvfs2",
|
||||
"//pkg/sentry/arch",
|
||||
"//pkg/sentry/fs/lock",
|
||||
"//pkg/sentry/kernel",
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
// Copyright 2020 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package verity
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/refsvfs2"
|
||||
)
|
||||
|
||||
func (d *dentry) afterLoad() {
|
||||
if refsvfs2.LeakCheckEnabled() && atomic.LoadInt64(&d.refs) != -1 {
|
||||
refsvfs2.Register(d, "verity.dentry")
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,7 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/fspath"
|
||||
"gvisor.dev/gvisor/pkg/marshal/primitive"
|
||||
"gvisor.dev/gvisor/pkg/merkletree"
|
||||
"gvisor.dev/gvisor/pkg/refsvfs2"
|
||||
"gvisor.dev/gvisor/pkg/sentry/arch"
|
||||
fslock "gvisor.dev/gvisor/pkg/sentry/fs/lock"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel"
|
||||
@@ -331,6 +332,9 @@ func (fs *filesystem) newDentry() *dentry {
|
||||
fs: fs,
|
||||
}
|
||||
d.vfsd.Init(d)
|
||||
if refsvfs2.LeakCheckEnabled() {
|
||||
refsvfs2.Register(d, "verity.dentry")
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
@@ -393,6 +397,9 @@ func (d *dentry) destroyLocked(ctx context.Context) {
|
||||
if d.lowerVD.Ok() {
|
||||
d.lowerVD.DecRef(ctx)
|
||||
}
|
||||
if refsvfs2.LeakCheckEnabled() {
|
||||
refsvfs2.Unregister(d, "verity.dentry")
|
||||
}
|
||||
|
||||
if d.lowerMerkleVD.Ok() {
|
||||
d.lowerMerkleVD.DecRef(ctx)
|
||||
@@ -412,6 +419,11 @@ func (d *dentry) destroyLocked(ctx context.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
// LeakMessage implements refsvfs2.CheckedObject.LeakMessage.
|
||||
func (d *dentry) LeakMessage() string {
|
||||
return fmt.Sprintf("[verity.dentry %p] reference count of %d instead of -1", d, atomic.LoadInt64(&d.refs))
|
||||
}
|
||||
|
||||
// InotifyWithParent implements vfs.DentryImpl.InotifyWithParent.
|
||||
func (d *dentry) InotifyWithParent(ctx context.Context, events, cookie uint32, et vfs.EventType) {
|
||||
//TODO(b/159261227): Implement InotifyWithParent.
|
||||
|
||||
+32
-16
@@ -24,6 +24,7 @@ import (
|
||||
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/refsvfs2"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
||||
"gvisor.dev/gvisor/pkg/syserror"
|
||||
)
|
||||
@@ -106,6 +107,9 @@ func newMount(vfs *VirtualFilesystem, fs *Filesystem, root *Dentry, mntns *Mount
|
||||
if opts.ReadOnly {
|
||||
mnt.setReadOnlyLocked(true)
|
||||
}
|
||||
if refsvfs2.LeakCheckEnabled() {
|
||||
refsvfs2.Register(mnt, "vfs.Mount")
|
||||
}
|
||||
return mnt
|
||||
}
|
||||
|
||||
@@ -489,26 +493,38 @@ func (mnt *Mount) IncRef() {
|
||||
|
||||
// DecRef decrements mnt's reference count.
|
||||
func (mnt *Mount) DecRef(ctx context.Context) {
|
||||
refs := atomic.AddInt64(&mnt.refs, -1)
|
||||
if refs&^math.MinInt64 == 0 { // mask out MSB
|
||||
var vd VirtualDentry
|
||||
if mnt.parent() != nil {
|
||||
mnt.vfs.mountMu.Lock()
|
||||
mnt.vfs.mounts.seq.BeginWrite()
|
||||
vd = mnt.vfs.disconnectLocked(mnt)
|
||||
mnt.vfs.mounts.seq.EndWrite()
|
||||
mnt.vfs.mountMu.Unlock()
|
||||
}
|
||||
if mnt.root != nil {
|
||||
mnt.root.DecRef(ctx)
|
||||
}
|
||||
mnt.fs.DecRef(ctx)
|
||||
if vd.Ok() {
|
||||
vd.DecRef(ctx)
|
||||
r := atomic.AddInt64(&mnt.refs, -1)
|
||||
if r&^math.MinInt64 == 0 { // mask out MSB
|
||||
if refsvfs2.LeakCheckEnabled() {
|
||||
refsvfs2.Unregister(mnt, "vfs.Mount")
|
||||
}
|
||||
mnt.destroy(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
func (mnt *Mount) destroy(ctx context.Context) {
|
||||
var vd VirtualDentry
|
||||
if mnt.parent() != nil {
|
||||
mnt.vfs.mountMu.Lock()
|
||||
mnt.vfs.mounts.seq.BeginWrite()
|
||||
vd = mnt.vfs.disconnectLocked(mnt)
|
||||
mnt.vfs.mounts.seq.EndWrite()
|
||||
mnt.vfs.mountMu.Unlock()
|
||||
}
|
||||
if mnt.root != nil {
|
||||
mnt.root.DecRef(ctx)
|
||||
}
|
||||
mnt.fs.DecRef(ctx)
|
||||
if vd.Ok() {
|
||||
vd.DecRef(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
// LeakMessage implements refsvfs2.CheckedObject.LeakMessage.
|
||||
func (mnt *Mount) LeakMessage() string {
|
||||
return fmt.Sprintf("[vfs.Mount %p] reference count of %d instead of 0", mnt, atomic.LoadInt64(&mnt.refs))
|
||||
}
|
||||
|
||||
// DecRef decrements mntns' reference count.
|
||||
func (mntns *MountNamespace) DecRef(ctx context.Context) {
|
||||
vfs := mntns.root.fs.VirtualFilesystem()
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"sync/atomic"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/refsvfs2"
|
||||
)
|
||||
|
||||
// FilesystemImplSaveRestoreExtension is an optional extension to
|
||||
@@ -109,6 +110,12 @@ func (vfs *VirtualFilesystem) loadMounts(mounts []*Mount) {
|
||||
}
|
||||
}
|
||||
|
||||
func (mnt *Mount) afterLoad() {
|
||||
if refsvfs2.LeakCheckEnabled() && atomic.LoadInt64(&mnt.refs) != 0 {
|
||||
refsvfs2.Register(mnt, "vfs.Mount")
|
||||
}
|
||||
}
|
||||
|
||||
// afterLoad is called by stateify.
|
||||
func (epi *epollInterest) afterLoad() {
|
||||
// Mark all epollInterests as ready after restore so that the next call to
|
||||
|
||||
Reference in New Issue
Block a user