Release FDTable lock before dropping the fds.
This is needed for SO_LINGER, where close() is blocked for linger timeout and we are holding the FDTable lock for the entire timeout which will not allow us to create/delete other fds. We have to release the locks and then drop the fds. PiperOrigin-RevId: 331844185
This commit is contained in:
parent
0d790cbaea
commit
7f89a26e18
|
@ -113,7 +113,9 @@ func (f *FDTable) loadDescriptorTable(m map[int32]descriptor) {
|
||||||
f.init() // Initialize table.
|
f.init() // Initialize table.
|
||||||
f.used = 0
|
f.used = 0
|
||||||
for fd, d := range m {
|
for fd, d := range m {
|
||||||
f.setAll(ctx, fd, d.file, d.fileVFS2, d.flags)
|
if file, fileVFS2 := f.setAll(ctx, fd, d.file, d.fileVFS2, d.flags); file != nil || fileVFS2 != nil {
|
||||||
|
panic("VFS1 or VFS2 files set")
|
||||||
|
}
|
||||||
|
|
||||||
// Note that we do _not_ need to acquire a extra table reference here. The
|
// Note that we do _not_ need to acquire a extra table reference here. The
|
||||||
// table reference will already be accounted for in the file, so we drop the
|
// table reference will already be accounted for in the file, so we drop the
|
||||||
|
@ -273,7 +275,6 @@ func (f *FDTable) NewFDs(ctx context.Context, fd int32, files []*fs.File, flags
|
||||||
}
|
}
|
||||||
|
|
||||||
f.mu.Lock()
|
f.mu.Lock()
|
||||||
defer f.mu.Unlock()
|
|
||||||
|
|
||||||
// From f.next to find available fd.
|
// From f.next to find available fd.
|
||||||
if fd < f.next {
|
if fd < f.next {
|
||||||
|
@ -283,15 +284,25 @@ func (f *FDTable) NewFDs(ctx context.Context, fd int32, files []*fs.File, flags
|
||||||
// Install all entries.
|
// Install all entries.
|
||||||
for i := fd; i < end && len(fds) < len(files); i++ {
|
for i := fd; i < end && len(fds) < len(files); i++ {
|
||||||
if d, _, _ := f.get(i); d == nil {
|
if d, _, _ := f.get(i); d == nil {
|
||||||
f.set(ctx, i, files[len(fds)], flags) // Set the descriptor.
|
// Set the descriptor.
|
||||||
fds = append(fds, i) // Record the file descriptor.
|
f.set(ctx, i, files[len(fds)], flags)
|
||||||
|
fds = append(fds, i) // Record the file descriptor.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Failure? Unwind existing FDs.
|
// Failure? Unwind existing FDs.
|
||||||
if len(fds) < len(files) {
|
if len(fds) < len(files) {
|
||||||
for _, i := range fds {
|
for _, i := range fds {
|
||||||
f.set(ctx, i, nil, FDFlags{}) // Zap entry.
|
f.set(ctx, i, nil, FDFlags{})
|
||||||
|
}
|
||||||
|
f.mu.Unlock()
|
||||||
|
|
||||||
|
// Drop the reference taken by the call to f.set() that
|
||||||
|
// originally installed the file. Don't call f.drop()
|
||||||
|
// (generating inotify events, etc.) since the file should
|
||||||
|
// appear to have never been inserted into f.
|
||||||
|
for _, file := range files[:len(fds)] {
|
||||||
|
file.DecRef(ctx)
|
||||||
}
|
}
|
||||||
return nil, syscall.EMFILE
|
return nil, syscall.EMFILE
|
||||||
}
|
}
|
||||||
|
@ -301,6 +312,7 @@ func (f *FDTable) NewFDs(ctx context.Context, fd int32, files []*fs.File, flags
|
||||||
f.next = fds[len(fds)-1] + 1
|
f.next = fds[len(fds)-1] + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
f.mu.Unlock()
|
||||||
return fds, nil
|
return fds, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -328,7 +340,6 @@ func (f *FDTable) NewFDsVFS2(ctx context.Context, fd int32, files []*vfs.FileDes
|
||||||
}
|
}
|
||||||
|
|
||||||
f.mu.Lock()
|
f.mu.Lock()
|
||||||
defer f.mu.Unlock()
|
|
||||||
|
|
||||||
// From f.next to find available fd.
|
// From f.next to find available fd.
|
||||||
if fd < f.next {
|
if fd < f.next {
|
||||||
|
@ -338,15 +349,25 @@ func (f *FDTable) NewFDsVFS2(ctx context.Context, fd int32, files []*vfs.FileDes
|
||||||
// Install all entries.
|
// Install all entries.
|
||||||
for i := fd; i < end && len(fds) < len(files); i++ {
|
for i := fd; i < end && len(fds) < len(files); i++ {
|
||||||
if d, _, _ := f.getVFS2(i); d == nil {
|
if d, _, _ := f.getVFS2(i); d == nil {
|
||||||
f.setVFS2(ctx, i, files[len(fds)], flags) // Set the descriptor.
|
// Set the descriptor.
|
||||||
fds = append(fds, i) // Record the file descriptor.
|
f.setVFS2(ctx, i, files[len(fds)], flags)
|
||||||
|
fds = append(fds, i) // Record the file descriptor.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Failure? Unwind existing FDs.
|
// Failure? Unwind existing FDs.
|
||||||
if len(fds) < len(files) {
|
if len(fds) < len(files) {
|
||||||
for _, i := range fds {
|
for _, i := range fds {
|
||||||
f.setVFS2(ctx, i, nil, FDFlags{}) // Zap entry.
|
f.setVFS2(ctx, i, nil, FDFlags{})
|
||||||
|
}
|
||||||
|
f.mu.Unlock()
|
||||||
|
|
||||||
|
// Drop the reference taken by the call to f.setVFS2() that
|
||||||
|
// originally installed the file. Don't call f.dropVFS2()
|
||||||
|
// (generating inotify events, etc.) since the file should
|
||||||
|
// appear to have never been inserted into f.
|
||||||
|
for _, file := range files[:len(fds)] {
|
||||||
|
file.DecRef(ctx)
|
||||||
}
|
}
|
||||||
return nil, syscall.EMFILE
|
return nil, syscall.EMFILE
|
||||||
}
|
}
|
||||||
|
@ -356,6 +377,7 @@ func (f *FDTable) NewFDsVFS2(ctx context.Context, fd int32, files []*vfs.FileDes
|
||||||
f.next = fds[len(fds)-1] + 1
|
f.next = fds[len(fds)-1] + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
f.mu.Unlock()
|
||||||
return fds, nil
|
return fds, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -407,34 +429,49 @@ func (f *FDTable) NewFDVFS2(ctx context.Context, minfd int32, file *vfs.FileDesc
|
||||||
// reference for that FD, the ref count for that existing reference is
|
// reference for that FD, the ref count for that existing reference is
|
||||||
// decremented.
|
// decremented.
|
||||||
func (f *FDTable) NewFDAt(ctx context.Context, fd int32, file *fs.File, flags FDFlags) error {
|
func (f *FDTable) NewFDAt(ctx context.Context, fd int32, file *fs.File, flags FDFlags) error {
|
||||||
return f.newFDAt(ctx, fd, file, nil, flags)
|
df, _, err := f.newFDAt(ctx, fd, file, nil, flags)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if df != nil {
|
||||||
|
f.drop(ctx, df)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewFDAtVFS2 sets the file reference for the given FD. If there is an active
|
// NewFDAtVFS2 sets the file reference for the given FD. If there is an active
|
||||||
// reference for that FD, the ref count for that existing reference is
|
// reference for that FD, the ref count for that existing reference is
|
||||||
// decremented.
|
// decremented.
|
||||||
func (f *FDTable) NewFDAtVFS2(ctx context.Context, fd int32, file *vfs.FileDescription, flags FDFlags) error {
|
func (f *FDTable) NewFDAtVFS2(ctx context.Context, fd int32, file *vfs.FileDescription, flags FDFlags) error {
|
||||||
return f.newFDAt(ctx, fd, nil, file, flags)
|
_, dfVFS2, err := f.newFDAt(ctx, fd, nil, file, flags)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if dfVFS2 != nil {
|
||||||
|
f.dropVFS2(ctx, dfVFS2)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *FDTable) newFDAt(ctx context.Context, fd int32, file *fs.File, fileVFS2 *vfs.FileDescription, flags FDFlags) error {
|
func (f *FDTable) newFDAt(ctx context.Context, fd int32, file *fs.File, fileVFS2 *vfs.FileDescription, flags FDFlags) (*fs.File, *vfs.FileDescription, error) {
|
||||||
if fd < 0 {
|
if fd < 0 {
|
||||||
// Don't accept negative FDs.
|
// Don't accept negative FDs.
|
||||||
return syscall.EBADF
|
return nil, nil, syscall.EBADF
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the limit for the provided file.
|
// Check the limit for the provided file.
|
||||||
if limitSet := limits.FromContext(ctx); limitSet != nil {
|
if limitSet := limits.FromContext(ctx); limitSet != nil {
|
||||||
if lim := limitSet.Get(limits.NumberOfFiles); lim.Cur != limits.Infinity && uint64(fd) >= lim.Cur {
|
if lim := limitSet.Get(limits.NumberOfFiles); lim.Cur != limits.Infinity && uint64(fd) >= lim.Cur {
|
||||||
return syscall.EMFILE
|
return nil, nil, syscall.EMFILE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Install the entry.
|
// Install the entry.
|
||||||
f.mu.Lock()
|
f.mu.Lock()
|
||||||
defer f.mu.Unlock()
|
defer f.mu.Unlock()
|
||||||
f.setAll(ctx, fd, file, fileVFS2, flags)
|
|
||||||
return nil
|
df, dfVFS2 := f.setAll(ctx, fd, file, fileVFS2, flags)
|
||||||
|
return df, dfVFS2, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetFlags sets the flags for the given file descriptor.
|
// SetFlags sets the flags for the given file descriptor.
|
||||||
|
@ -552,11 +589,8 @@ func (f *FDTable) Fork(ctx context.Context) *FDTable {
|
||||||
f.forEach(ctx, func(fd int32, file *fs.File, fileVFS2 *vfs.FileDescription, flags FDFlags) {
|
f.forEach(ctx, func(fd int32, file *fs.File, fileVFS2 *vfs.FileDescription, flags FDFlags) {
|
||||||
// The set function here will acquire an appropriate table
|
// The set function here will acquire an appropriate table
|
||||||
// reference for the clone. We don't need anything else.
|
// reference for the clone. We don't need anything else.
|
||||||
switch {
|
if df, dfVFS2 := clone.setAll(ctx, fd, file, fileVFS2, flags); df != nil || dfVFS2 != nil {
|
||||||
case file != nil:
|
panic("VFS1 or VFS2 files set")
|
||||||
clone.set(ctx, fd, file, flags)
|
|
||||||
case fileVFS2 != nil:
|
|
||||||
clone.setVFS2(ctx, fd, fileVFS2, flags)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return clone
|
return clone
|
||||||
|
@ -571,7 +605,6 @@ func (f *FDTable) Remove(ctx context.Context, fd int32) (*fs.File, *vfs.FileDesc
|
||||||
}
|
}
|
||||||
|
|
||||||
f.mu.Lock()
|
f.mu.Lock()
|
||||||
defer f.mu.Unlock()
|
|
||||||
|
|
||||||
// Update current available position.
|
// Update current available position.
|
||||||
if fd < f.next {
|
if fd < f.next {
|
||||||
|
@ -587,24 +620,51 @@ func (f *FDTable) Remove(ctx context.Context, fd int32) (*fs.File, *vfs.FileDesc
|
||||||
case orig2 != nil:
|
case orig2 != nil:
|
||||||
orig2.IncRef()
|
orig2.IncRef()
|
||||||
}
|
}
|
||||||
|
|
||||||
if orig != nil || orig2 != nil {
|
if orig != nil || orig2 != nil {
|
||||||
f.setAll(ctx, fd, nil, nil, FDFlags{}) // Zap entry.
|
orig, orig2 = f.setAll(ctx, fd, nil, nil, FDFlags{}) // Zap entry.
|
||||||
}
|
}
|
||||||
|
f.mu.Unlock()
|
||||||
|
|
||||||
|
if orig != nil {
|
||||||
|
f.drop(ctx, orig)
|
||||||
|
}
|
||||||
|
if orig2 != nil {
|
||||||
|
f.dropVFS2(ctx, orig2)
|
||||||
|
}
|
||||||
|
|
||||||
return orig, orig2
|
return orig, orig2
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveIf removes all FDs where cond is true.
|
// RemoveIf removes all FDs where cond is true.
|
||||||
func (f *FDTable) RemoveIf(ctx context.Context, cond func(*fs.File, *vfs.FileDescription, FDFlags) bool) {
|
func (f *FDTable) RemoveIf(ctx context.Context, cond func(*fs.File, *vfs.FileDescription, FDFlags) bool) {
|
||||||
f.mu.Lock()
|
// TODO(gvisor.dev/issue/1624): Remove fs.File slice.
|
||||||
defer f.mu.Unlock()
|
var files []*fs.File
|
||||||
|
var filesVFS2 []*vfs.FileDescription
|
||||||
|
|
||||||
|
f.mu.Lock()
|
||||||
f.forEach(ctx, func(fd int32, file *fs.File, fileVFS2 *vfs.FileDescription, flags FDFlags) {
|
f.forEach(ctx, func(fd int32, file *fs.File, fileVFS2 *vfs.FileDescription, flags FDFlags) {
|
||||||
if cond(file, fileVFS2, flags) {
|
if cond(file, fileVFS2, flags) {
|
||||||
f.set(ctx, fd, nil, FDFlags{}) // Clear from table.
|
df, dfVFS2 := f.setAll(ctx, fd, nil, nil, FDFlags{}) // Clear from table.
|
||||||
|
if df != nil {
|
||||||
|
files = append(files, df)
|
||||||
|
}
|
||||||
|
if dfVFS2 != nil {
|
||||||
|
filesVFS2 = append(filesVFS2, dfVFS2)
|
||||||
|
}
|
||||||
// Update current available position.
|
// Update current available position.
|
||||||
if fd < f.next {
|
if fd < f.next {
|
||||||
f.next = fd
|
f.next = fd
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
f.mu.Unlock()
|
||||||
|
|
||||||
|
for _, file := range files {
|
||||||
|
f.drop(ctx, file)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, file := range filesVFS2 {
|
||||||
|
f.dropVFS2(ctx, file)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,33 +86,30 @@ func (f *FDTable) CurrentMaxFDs() int {
|
||||||
return len(slice)
|
return len(slice)
|
||||||
}
|
}
|
||||||
|
|
||||||
// set sets an entry.
|
// set sets an entry for VFS1, refer to setAll().
|
||||||
//
|
|
||||||
// This handles accounting changes, as well as acquiring and releasing the
|
|
||||||
// reference needed by the table iff the file is different.
|
|
||||||
//
|
//
|
||||||
// Precondition: mu must be held.
|
// Precondition: mu must be held.
|
||||||
func (f *FDTable) set(ctx context.Context, fd int32, file *fs.File, flags FDFlags) {
|
func (f *FDTable) set(ctx context.Context, fd int32, file *fs.File, flags FDFlags) *fs.File {
|
||||||
f.setAll(ctx, fd, file, nil, flags)
|
dropFile, _ := f.setAll(ctx, fd, file, nil, flags)
|
||||||
|
return dropFile
|
||||||
}
|
}
|
||||||
|
|
||||||
// setVFS2 sets an entry.
|
// setVFS2 sets an entry for VFS2, refer to setAll().
|
||||||
//
|
|
||||||
// This handles accounting changes, as well as acquiring and releasing the
|
|
||||||
// reference needed by the table iff the file is different.
|
|
||||||
//
|
//
|
||||||
// Precondition: mu must be held.
|
// Precondition: mu must be held.
|
||||||
func (f *FDTable) setVFS2(ctx context.Context, fd int32, file *vfs.FileDescription, flags FDFlags) {
|
func (f *FDTable) setVFS2(ctx context.Context, fd int32, file *vfs.FileDescription, flags FDFlags) *vfs.FileDescription {
|
||||||
f.setAll(ctx, fd, nil, file, flags)
|
_, dropFile := f.setAll(ctx, fd, nil, file, flags)
|
||||||
|
return dropFile
|
||||||
}
|
}
|
||||||
|
|
||||||
// setAll sets an entry.
|
// setAll sets the file description referred to by fd to file/fileVFS2. If
|
||||||
//
|
// file/fileVFS2 are non-nil, it takes a reference on them. If setAll replaces
|
||||||
// This handles accounting changes, as well as acquiring and releasing the
|
// an existing file description, it returns it with the FDTable's reference
|
||||||
// reference needed by the table iff the file is different.
|
// transferred to the caller, which must call f.drop/dropVFS2() on the returned
|
||||||
|
// file after unlocking f.mu.
|
||||||
//
|
//
|
||||||
// Precondition: mu must be held.
|
// Precondition: mu must be held.
|
||||||
func (f *FDTable) setAll(ctx context.Context, fd int32, file *fs.File, fileVFS2 *vfs.FileDescription, flags FDFlags) {
|
func (f *FDTable) setAll(ctx context.Context, fd int32, file *fs.File, fileVFS2 *vfs.FileDescription, flags FDFlags) (*fs.File, *vfs.FileDescription) {
|
||||||
if file != nil && fileVFS2 != nil {
|
if file != nil && fileVFS2 != nil {
|
||||||
panic("VFS1 and VFS2 files set")
|
panic("VFS1 and VFS2 files set")
|
||||||
}
|
}
|
||||||
|
@ -155,20 +152,6 @@ func (f *FDTable) setAll(ctx context.Context, fd int32, file *fs.File, fileVFS2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Drop the table reference.
|
|
||||||
if orig != nil {
|
|
||||||
switch {
|
|
||||||
case orig.file != nil:
|
|
||||||
if desc == nil || desc.file != orig.file {
|
|
||||||
f.drop(ctx, orig.file)
|
|
||||||
}
|
|
||||||
case orig.fileVFS2 != nil:
|
|
||||||
if desc == nil || desc.fileVFS2 != orig.fileVFS2 {
|
|
||||||
f.dropVFS2(ctx, orig.fileVFS2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adjust used.
|
// Adjust used.
|
||||||
switch {
|
switch {
|
||||||
case orig == nil && desc != nil:
|
case orig == nil && desc != nil:
|
||||||
|
@ -176,4 +159,18 @@ func (f *FDTable) setAll(ctx context.Context, fd int32, file *fs.File, fileVFS2
|
||||||
case orig != nil && desc == nil:
|
case orig != nil && desc == nil:
|
||||||
atomic.AddInt32(&f.used, -1)
|
atomic.AddInt32(&f.used, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if orig != nil {
|
||||||
|
switch {
|
||||||
|
case orig.file != nil:
|
||||||
|
if desc == nil || desc.file != orig.file {
|
||||||
|
return orig.file, nil
|
||||||
|
}
|
||||||
|
case orig.fileVFS2 != nil:
|
||||||
|
if desc == nil || desc.fileVFS2 != orig.fileVFS2 {
|
||||||
|
return nil, orig.fileVFS2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue