github.com/MerlinKodo/gvisor@v0.0.0-20231110090155-957f62ecf90e/pkg/sentry/kernel/fd_table_unsafe.go (about) 1 // Copyright 2018 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package kernel 16 17 import ( 18 "math" 19 20 "github.com/MerlinKodo/gvisor/pkg/bitmap" 21 "github.com/MerlinKodo/gvisor/pkg/sentry/vfs" 22 ) 23 24 type descriptorBucket [fdsPerBucket]descriptorAtomicPtr 25 type descriptorBucketSlice []descriptorBucketAtomicPtr 26 27 // descriptorTable is a two level table. The first level is a slice of 28 // *descriptorBucket where each bucket is a slice of *descriptor. 29 // 30 // All objects are updated atomically. 31 type descriptorTable struct { 32 // Changes to the slice itself requiring holding FDTable.mu. 33 slice descriptorBucketSliceAtomicPtr `state:".(map[int32]*descriptor)"` 34 } 35 36 // initNoLeakCheck initializes the table without enabling leak checking. 37 // 38 // This is used when loading an FDTable after S/R, during which the ref count 39 // object itself will enable leak checking if necessary. 40 func (f *FDTable) initNoLeakCheck() { 41 var slice descriptorBucketSlice // Empty slice. 42 f.slice.Store(&slice) 43 } 44 45 // init initializes the table with leak checking. 46 func (f *FDTable) init() { 47 f.initNoLeakCheck() 48 f.InitRefs() 49 f.fdBitmap = bitmap.New(uint32(math.MaxUint16)) 50 } 51 52 const ( 53 // fdsPerBucketShift is chosen in such a way that the size of bucket is 54 // equal to one page. 55 fdsPerBucketShift = 9 56 fdsPerBucket = 1 << fdsPerBucketShift 57 fdsPerBucketMask = fdsPerBucket - 1 58 ) 59 60 // get gets a file entry. 61 // 62 // The boolean indicates whether this was in range. 63 // 64 //go:nosplit 65 func (f *FDTable) get(fd int32) (*vfs.FileDescription, FDFlags, bool) { 66 slice := *f.slice.Load() 67 bucketN := fd >> fdsPerBucketShift 68 if bucketN >= int32(len(slice)) { 69 return nil, FDFlags{}, false 70 } 71 bucket := slice[bucketN].Load() 72 if bucket == nil { 73 return nil, FDFlags{}, false 74 } 75 d := bucket[fd&fdsPerBucketMask].Load() 76 if d == nil { 77 return nil, FDFlags{}, true 78 } 79 return d.file, d.flags, true 80 } 81 82 // CurrentMaxFDs returns the number of file descriptors that may be stored in f 83 // without reallocation. 84 func (f *FDTable) CurrentMaxFDs() int { 85 slice := *f.slice.Load() 86 return len(slice) * fdsPerBucket 87 } 88 89 // set sets the file description referred to by fd to file. If 90 // file is non-nil, it takes a reference on them. If setAll replaces 91 // an existing file description, it returns it with the FDTable's reference 92 // transferred to the caller, which must call f.drop on the returned 93 // file after unlocking f.mu. 94 // 95 // Precondition: mu must be held. 96 func (f *FDTable) set(fd int32, file *vfs.FileDescription, flags FDFlags) *vfs.FileDescription { 97 slicePtr := f.slice.Load() 98 99 bucketN := fd >> fdsPerBucketShift 100 // Grow the table as required. 101 if length := len(*slicePtr); int(bucketN) >= length { 102 newLen := int(bucketN) + 1 103 if newLen < 2*length { 104 // Ensure the table at least doubles in size without going over the limit. 105 newLen = 2 * length 106 if newLen > int(MaxFdLimit) { 107 newLen = int(MaxFdLimit) 108 } 109 } 110 newSlice := append(*slicePtr, make([]descriptorBucketAtomicPtr, newLen-length)...) 111 slicePtr = &newSlice 112 f.slice.Store(slicePtr) 113 } 114 115 slice := *slicePtr 116 117 bucket := slice[bucketN].Load() 118 if bucket == nil { 119 bucket = &descriptorBucket{} 120 slice[bucketN].Store(bucket) 121 } 122 123 var desc *descriptor 124 if file != nil { 125 desc = &descriptor{ 126 file: file, 127 flags: flags, 128 } 129 } 130 131 // Update the single element. 132 orig := bucket[fd%fdsPerBucket].Swap(desc) 133 134 // Acquire a table reference. 135 if desc != nil && desc.file != nil { 136 if orig == nil || desc.file != orig.file { 137 desc.file.IncRef() 138 } 139 } 140 141 if orig != nil && orig.file != nil { 142 if desc == nil || desc.file != orig.file { 143 return orig.file 144 } 145 } 146 return nil 147 }