Avoid holding locks when opening files in VFS2.

Fixes #3243, #3521

PiperOrigin-RevId: 327308890
This commit is contained in:
Jamie Liu 2020-08-18 14:34:15 -07:00 committed by gVisor bot
parent 760c131da1
commit 6405525b04
4 changed files with 100 additions and 25 deletions

View File

@ -834,7 +834,14 @@ func (fs *filesystem) OpenAt(ctx context.Context, rp *vfs.ResolvingPath, opts vf
var ds *[]*dentry
fs.renameMu.RLock()
defer fs.renameMuRUnlockAndCheckCaching(ctx, &ds)
unlocked := false
unlock := func() {
if !unlocked {
fs.renameMuRUnlockAndCheckCaching(ctx, &ds)
unlocked = true
}
}
defer unlock()
start := rp.Start().Impl().(*dentry)
if !start.cachedMetadataAuthoritative() {
@ -851,7 +858,10 @@ func (fs *filesystem) OpenAt(ctx context.Context, rp *vfs.ResolvingPath, opts vf
if mustCreate {
return nil, syserror.EEXIST
}
return start.openLocked(ctx, rp, &opts)
start.IncRef()
defer start.DecRef(ctx)
unlock()
return start.open(ctx, rp, &opts)
}
afterTrailingSymlink:
@ -901,11 +911,15 @@ afterTrailingSymlink:
if rp.MustBeDir() && !child.isDir() {
return nil, syserror.ENOTDIR
}
return child.openLocked(ctx, rp, &opts)
child.IncRef()
defer child.DecRef(ctx)
unlock()
return child.open(ctx, rp, &opts)
}
// Preconditions: fs.renameMu must be locked.
func (d *dentry) openLocked(ctx context.Context, rp *vfs.ResolvingPath, opts *vfs.OpenOptions) (*vfs.FileDescription, error) {
// Preconditions: The caller must hold no locks (since opening pipes may block
// indefinitely).
func (d *dentry) open(ctx context.Context, rp *vfs.ResolvingPath, opts *vfs.OpenOptions) (*vfs.FileDescription, error) {
ats := vfs.AccessTypesForOpenFlags(opts)
if err := d.checkPermissions(rp.Credentials(), ats); err != nil {
return nil, err
@ -968,7 +982,7 @@ func (d *dentry) openLocked(ctx context.Context, rp *vfs.ResolvingPath, opts *vf
return nil, syserror.ENXIO
}
if d.fs.iopts.OpenSocketsByConnecting {
return d.connectSocketLocked(ctx, opts)
return d.openSocketByConnecting(ctx, opts)
}
case linux.S_IFIFO:
if d.isSynthetic() {
@ -977,7 +991,7 @@ func (d *dentry) openLocked(ctx context.Context, rp *vfs.ResolvingPath, opts *vf
}
if vfd == nil {
if vfd, err = d.openSpecialFileLocked(ctx, mnt, opts); err != nil {
if vfd, err = d.openSpecialFile(ctx, mnt, opts); err != nil {
return nil, err
}
}
@ -996,7 +1010,7 @@ func (d *dentry) openLocked(ctx context.Context, rp *vfs.ResolvingPath, opts *vf
return vfd, err
}
func (d *dentry) connectSocketLocked(ctx context.Context, opts *vfs.OpenOptions) (*vfs.FileDescription, error) {
func (d *dentry) openSocketByConnecting(ctx context.Context, opts *vfs.OpenOptions) (*vfs.FileDescription, error) {
if opts.Flags&linux.O_DIRECT != 0 {
return nil, syserror.EINVAL
}
@ -1016,7 +1030,7 @@ func (d *dentry) connectSocketLocked(ctx context.Context, opts *vfs.OpenOptions)
return fd, nil
}
func (d *dentry) openSpecialFileLocked(ctx context.Context, mnt *vfs.Mount, opts *vfs.OpenOptions) (*vfs.FileDescription, error) {
func (d *dentry) openSpecialFile(ctx context.Context, mnt *vfs.Mount, opts *vfs.OpenOptions) (*vfs.FileDescription, error) {
ats := vfs.AccessTypesForOpenFlags(opts)
if opts.Flags&linux.O_DIRECT != 0 {
return nil, syserror.EINVAL

View File

@ -397,15 +397,21 @@ func (fs *Filesystem) OpenAt(ctx context.Context, rp *vfs.ResolvingPath, opts vf
// Do not create new file.
if opts.Flags&linux.O_CREAT == 0 {
fs.mu.RLock()
defer fs.processDeferredDecRefs(ctx)
defer fs.mu.RUnlock()
vfsd, inode, err := fs.walkExistingLocked(ctx, rp)
if err != nil {
fs.mu.RUnlock()
fs.processDeferredDecRefs(ctx)
return nil, err
}
if err := inode.CheckPermissions(ctx, rp.Credentials(), ats); err != nil {
fs.mu.RUnlock()
fs.processDeferredDecRefs(ctx)
return nil, err
}
inode.IncRef()
defer inode.DecRef(ctx)
fs.mu.RUnlock()
fs.processDeferredDecRefs(ctx)
return inode.Open(ctx, rp, vfsd, opts)
}
@ -414,7 +420,14 @@ func (fs *Filesystem) OpenAt(ctx context.Context, rp *vfs.ResolvingPath, opts vf
vfsd := rp.Start()
inode := vfsd.Impl().(*Dentry).inode
fs.mu.Lock()
defer fs.mu.Unlock()
unlocked := false
unlock := func() {
if !unlocked {
fs.mu.Unlock()
unlocked = true
}
}
defer unlock()
if rp.Done() {
if rp.MustBeDir() {
return nil, syserror.EISDIR
@ -425,6 +438,9 @@ func (fs *Filesystem) OpenAt(ctx context.Context, rp *vfs.ResolvingPath, opts vf
if err := inode.CheckPermissions(ctx, rp.Credentials(), ats); err != nil {
return nil, err
}
inode.IncRef()
defer inode.DecRef(ctx)
unlock()
return inode.Open(ctx, rp, vfsd, opts)
}
afterTrailingSymlink:
@ -466,6 +482,9 @@ afterTrailingSymlink:
}
child := childVFSD.Impl().(*Dentry)
parentVFSD.Impl().(*Dentry).InsertChild(pc, child)
child.inode.IncRef()
defer child.inode.DecRef(ctx)
unlock()
return child.inode.Open(ctx, rp, childVFSD, opts)
}
if err != nil {
@ -499,6 +518,9 @@ afterTrailingSymlink:
if err := child.inode.CheckPermissions(ctx, rp.Credentials(), ats); err != nil {
return nil, err
}
child.inode.IncRef()
defer child.inode.DecRef(ctx)
unlock()
return child.inode.Open(ctx, rp, &child.vfsd, opts)
}

View File

@ -717,17 +717,33 @@ func (fs *filesystem) MknodAt(ctx context.Context, rp *vfs.ResolvingPath, opts v
func (fs *filesystem) OpenAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.OpenOptions) (*vfs.FileDescription, error) {
mayCreate := opts.Flags&linux.O_CREAT != 0
mustCreate := opts.Flags&(linux.O_CREAT|linux.O_EXCL) == (linux.O_CREAT | linux.O_EXCL)
mayWrite := vfs.AccessTypesForOpenFlags(&opts).MayWrite()
var ds *[]*dentry
fs.renameMu.RLock()
defer fs.renameMuRUnlockAndCheckDrop(ctx, &ds)
unlocked := false
unlock := func() {
if !unlocked {
fs.renameMuRUnlockAndCheckDrop(ctx, &ds)
unlocked = true
}
}
defer unlock()
start := rp.Start().Impl().(*dentry)
if rp.Done() {
if mustCreate {
return nil, syserror.EEXIST
}
return start.openLocked(ctx, rp, &opts)
if mayWrite {
if err := start.copyUpLocked(ctx); err != nil {
return nil, err
}
}
start.IncRef()
defer start.DecRef(ctx)
unlock()
return start.openCopiedUp(ctx, rp, &opts)
}
afterTrailingSymlink:
@ -767,20 +783,24 @@ afterTrailingSymlink:
start = parent
goto afterTrailingSymlink
}
return child.openLocked(ctx, rp, &opts)
if mayWrite {
if err := child.copyUpLocked(ctx); err != nil {
return nil, err
}
}
child.IncRef()
defer child.DecRef(ctx)
unlock()
return child.openCopiedUp(ctx, rp, &opts)
}
// Preconditions: fs.renameMu must be locked.
func (d *dentry) openLocked(ctx context.Context, rp *vfs.ResolvingPath, opts *vfs.OpenOptions) (*vfs.FileDescription, error) {
// Preconditions: If vfs.AccessTypesForOpenFlags(opts).MayWrite(), then d has
// been copied up.
func (d *dentry) openCopiedUp(ctx context.Context, rp *vfs.ResolvingPath, opts *vfs.OpenOptions) (*vfs.FileDescription, error) {
ats := vfs.AccessTypesForOpenFlags(opts)
if err := d.checkPermissions(rp.Credentials(), ats); err != nil {
return nil, err
}
if ats.MayWrite() {
if err := d.copyUpLocked(ctx); err != nil {
return nil, err
}
}
mnt := rp.Mount()
// Directory FDs open FDs from each layer when directory entries are read,
@ -792,7 +812,7 @@ func (d *dentry) openLocked(ctx context.Context, rp *vfs.ResolvingPath, opts *vf
return nil, syserror.EISDIR
}
// Can't open directories writably.
if ats&vfs.MayWrite != 0 {
if ats.MayWrite() {
return nil, syserror.EISDIR
}
if opts.Flags&linux.O_DIRECT != 0 {

View File

@ -307,18 +307,28 @@ func (fs *filesystem) OpenAt(ctx context.Context, rp *vfs.ResolvingPath, opts vf
// don't need fs.mu for writing.
if opts.Flags&linux.O_CREAT == 0 {
fs.mu.RLock()
defer fs.mu.RUnlock()
d, err := resolveLocked(ctx, rp)
if err != nil {
fs.mu.RUnlock()
return nil, err
}
d.IncRef()
defer d.DecRef(ctx)
fs.mu.RUnlock()
return d.open(ctx, rp, &opts, false /* afterCreate */)
}
mustCreate := opts.Flags&linux.O_EXCL != 0
start := rp.Start().Impl().(*dentry)
fs.mu.Lock()
defer fs.mu.Unlock()
unlocked := false
unlock := func() {
if !unlocked {
fs.mu.Unlock()
unlocked = true
}
}
defer unlock()
if rp.Done() {
// Reject attempts to open mount root directory with O_CREAT.
if rp.MustBeDir() {
@ -327,6 +337,9 @@ func (fs *filesystem) OpenAt(ctx context.Context, rp *vfs.ResolvingPath, opts vf
if mustCreate {
return nil, syserror.EEXIST
}
start.IncRef()
defer start.DecRef(ctx)
unlock()
return start.open(ctx, rp, &opts, false /* afterCreate */)
}
afterTrailingSymlink:
@ -364,6 +377,7 @@ afterTrailingSymlink:
creds := rp.Credentials()
child := fs.newDentry(fs.newRegularFile(creds.EffectiveKUID, creds.EffectiveKGID, opts.Mode))
parentDir.insertChildLocked(child, name)
unlock()
fd, err := child.open(ctx, rp, &opts, true)
if err != nil {
return nil, err
@ -392,9 +406,14 @@ afterTrailingSymlink:
if rp.MustBeDir() && !child.inode.isDir() {
return nil, syserror.ENOTDIR
}
child.IncRef()
defer child.DecRef(ctx)
unlock()
return child.open(ctx, rp, &opts, false)
}
// Preconditions: The caller must hold no locks (since opening pipes may block
// indefinitely).
func (d *dentry) open(ctx context.Context, rp *vfs.ResolvingPath, opts *vfs.OpenOptions, afterCreate bool) (*vfs.FileDescription, error) {
ats := vfs.AccessTypesForOpenFlags(opts)
if !afterCreate {