github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fsimpl/eventfd/eventfd.go (about) 1 // Copyright 2020 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 eventfd implements event fds. 16 package eventfd 17 18 import ( 19 "math" 20 "sync" 21 22 "golang.org/x/sys/unix" 23 "github.com/SagerNet/gvisor/pkg/abi/linux" 24 "github.com/SagerNet/gvisor/pkg/context" 25 "github.com/SagerNet/gvisor/pkg/fdnotifier" 26 "github.com/SagerNet/gvisor/pkg/hostarch" 27 "github.com/SagerNet/gvisor/pkg/log" 28 "github.com/SagerNet/gvisor/pkg/sentry/vfs" 29 "github.com/SagerNet/gvisor/pkg/syserror" 30 "github.com/SagerNet/gvisor/pkg/usermem" 31 "github.com/SagerNet/gvisor/pkg/waiter" 32 ) 33 34 // EventFileDescription implements vfs.FileDescriptionImpl for file-based event 35 // notification (eventfd). Eventfds are usually internal to the Sentry but in 36 // certain situations they may be converted into a host-backed eventfd. 37 // 38 // +stateify savable 39 type EventFileDescription struct { 40 vfsfd vfs.FileDescription 41 vfs.FileDescriptionDefaultImpl 42 vfs.DentryMetadataFileDescriptionImpl 43 vfs.NoLockFD 44 45 // queue is used to notify interested parties when the event object 46 // becomes readable or writable. 47 queue waiter.Queue 48 49 // mu protects the fields below. 50 mu sync.Mutex `state:"nosave"` 51 52 // val is the current value of the event counter. 53 val uint64 54 55 // semMode specifies whether the event is in "semaphore" mode. 56 semMode bool 57 58 // hostfd indicates whether this eventfd is passed through to the host. 59 hostfd int 60 } 61 62 var _ vfs.FileDescriptionImpl = (*EventFileDescription)(nil) 63 64 // New creates a new event fd. 65 func New(ctx context.Context, vfsObj *vfs.VirtualFilesystem, initVal uint64, semMode bool, flags uint32) (*vfs.FileDescription, error) { 66 vd := vfsObj.NewAnonVirtualDentry("[eventfd]") 67 defer vd.DecRef(ctx) 68 efd := &EventFileDescription{ 69 val: initVal, 70 semMode: semMode, 71 hostfd: -1, 72 } 73 if err := efd.vfsfd.Init(efd, flags, vd.Mount(), vd.Dentry(), &vfs.FileDescriptionOptions{ 74 UseDentryMetadata: true, 75 DenyPRead: true, 76 DenyPWrite: true, 77 }); err != nil { 78 return nil, err 79 } 80 return &efd.vfsfd, nil 81 } 82 83 // HostFD returns the host eventfd associated with this event. 84 func (efd *EventFileDescription) HostFD() (int, error) { 85 efd.mu.Lock() 86 defer efd.mu.Unlock() 87 if efd.hostfd >= 0 { 88 return efd.hostfd, nil 89 } 90 91 flags := linux.EFD_NONBLOCK 92 if efd.semMode { 93 flags |= linux.EFD_SEMAPHORE 94 } 95 96 fd, _, errno := unix.Syscall(unix.SYS_EVENTFD2, uintptr(efd.val), uintptr(flags), 0) 97 if errno != 0 { 98 return -1, errno 99 } 100 101 if err := fdnotifier.AddFD(int32(fd), &efd.queue); err != nil { 102 if closeErr := unix.Close(int(fd)); closeErr != nil { 103 log.Warningf("close(%d) eventfd failed: %v", fd, closeErr) 104 } 105 return -1, err 106 } 107 108 efd.hostfd = int(fd) 109 return efd.hostfd, nil 110 } 111 112 // Release implements vfs.FileDescriptionImpl.Release. 113 func (efd *EventFileDescription) Release(context.Context) { 114 efd.mu.Lock() 115 defer efd.mu.Unlock() 116 if efd.hostfd >= 0 { 117 fdnotifier.RemoveFD(int32(efd.hostfd)) 118 if closeErr := unix.Close(int(efd.hostfd)); closeErr != nil { 119 log.Warningf("close(%d) eventfd failed: %v", efd.hostfd, closeErr) 120 } 121 efd.hostfd = -1 122 } 123 } 124 125 // Read implements vfs.FileDescriptionImpl.Read. 126 func (efd *EventFileDescription) Read(ctx context.Context, dst usermem.IOSequence, _ vfs.ReadOptions) (int64, error) { 127 if dst.NumBytes() < 8 { 128 return 0, unix.EINVAL 129 } 130 if err := efd.read(ctx, dst); err != nil { 131 return 0, err 132 } 133 return 8, nil 134 } 135 136 // Write implements vfs.FileDescriptionImpl.Write. 137 func (efd *EventFileDescription) Write(ctx context.Context, src usermem.IOSequence, _ vfs.WriteOptions) (int64, error) { 138 if src.NumBytes() < 8 { 139 return 0, unix.EINVAL 140 } 141 if err := efd.write(ctx, src); err != nil { 142 return 0, err 143 } 144 return 8, nil 145 } 146 147 // Preconditions: Must be called with efd.mu locked. 148 func (efd *EventFileDescription) hostReadLocked(ctx context.Context, dst usermem.IOSequence) error { 149 var buf [8]byte 150 if _, err := unix.Read(efd.hostfd, buf[:]); err != nil { 151 if err == unix.EWOULDBLOCK { 152 return syserror.ErrWouldBlock 153 } 154 return err 155 } 156 _, err := dst.CopyOut(ctx, buf[:]) 157 return err 158 } 159 160 func (efd *EventFileDescription) read(ctx context.Context, dst usermem.IOSequence) error { 161 efd.mu.Lock() 162 if efd.hostfd >= 0 { 163 defer efd.mu.Unlock() 164 return efd.hostReadLocked(ctx, dst) 165 } 166 167 // We can't complete the read if the value is currently zero. 168 if efd.val == 0 { 169 efd.mu.Unlock() 170 return syserror.ErrWouldBlock 171 } 172 173 // Update the value based on the mode the event is operating in. 174 var val uint64 175 if efd.semMode { 176 val = 1 177 // Consistent with Linux, this is done even if writing to memory fails. 178 efd.val-- 179 } else { 180 val = efd.val 181 efd.val = 0 182 } 183 184 efd.mu.Unlock() 185 186 // Notify writers. We do this even if we were already writable because 187 // it is possible that a writer is waiting to write the maximum value 188 // to the event. 189 efd.queue.Notify(waiter.WritableEvents) 190 191 var buf [8]byte 192 hostarch.ByteOrder.PutUint64(buf[:], val) 193 _, err := dst.CopyOut(ctx, buf[:]) 194 return err 195 } 196 197 // Preconditions: Must be called with efd.mu locked. 198 func (efd *EventFileDescription) hostWriteLocked(val uint64) error { 199 var buf [8]byte 200 hostarch.ByteOrder.PutUint64(buf[:], val) 201 _, err := unix.Write(efd.hostfd, buf[:]) 202 if err == unix.EWOULDBLOCK { 203 return syserror.ErrWouldBlock 204 } 205 return err 206 } 207 208 func (efd *EventFileDescription) write(ctx context.Context, src usermem.IOSequence) error { 209 var buf [8]byte 210 if _, err := src.CopyIn(ctx, buf[:]); err != nil { 211 return err 212 } 213 val := hostarch.ByteOrder.Uint64(buf[:]) 214 215 return efd.Signal(val) 216 } 217 218 // Signal is an internal function to signal the event fd. 219 func (efd *EventFileDescription) Signal(val uint64) error { 220 if val == math.MaxUint64 { 221 return unix.EINVAL 222 } 223 224 efd.mu.Lock() 225 226 if efd.hostfd >= 0 { 227 defer efd.mu.Unlock() 228 return efd.hostWriteLocked(val) 229 } 230 231 // We only allow writes that won't cause the value to go over the max 232 // uint64 minus 1. 233 if val > math.MaxUint64-1-efd.val { 234 efd.mu.Unlock() 235 return syserror.ErrWouldBlock 236 } 237 238 efd.val += val 239 efd.mu.Unlock() 240 241 // Always trigger a notification. 242 efd.queue.Notify(waiter.ReadableEvents) 243 244 return nil 245 } 246 247 // Readiness implements waiter.Waitable.Readiness. 248 func (efd *EventFileDescription) Readiness(mask waiter.EventMask) waiter.EventMask { 249 efd.mu.Lock() 250 defer efd.mu.Unlock() 251 252 if efd.hostfd >= 0 { 253 return fdnotifier.NonBlockingPoll(int32(efd.hostfd), mask) 254 } 255 256 ready := waiter.EventMask(0) 257 if efd.val > 0 { 258 ready |= waiter.ReadableEvents 259 } 260 261 if efd.val < math.MaxUint64-1 { 262 ready |= waiter.WritableEvents 263 } 264 265 return mask & ready 266 } 267 268 // EventRegister implements waiter.Waitable.EventRegister. 269 func (efd *EventFileDescription) EventRegister(entry *waiter.Entry, mask waiter.EventMask) { 270 efd.queue.EventRegister(entry, mask) 271 272 efd.mu.Lock() 273 defer efd.mu.Unlock() 274 if efd.hostfd >= 0 { 275 fdnotifier.UpdateFD(int32(efd.hostfd)) 276 } 277 } 278 279 // EventUnregister implements waiter.Waitable.EventUnregister. 280 func (efd *EventFileDescription) EventUnregister(entry *waiter.Entry) { 281 efd.queue.EventUnregister(entry) 282 283 efd.mu.Lock() 284 defer efd.mu.Unlock() 285 if efd.hostfd >= 0 { 286 fdnotifier.UpdateFD(int32(efd.hostfd)) 287 } 288 }