github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fsimpl/timerfd/timerfd.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 timerfd implements timer fds. 16 package timerfd 17 18 import ( 19 "sync/atomic" 20 21 "github.com/SagerNet/gvisor/pkg/context" 22 "github.com/SagerNet/gvisor/pkg/errors/linuxerr" 23 "github.com/SagerNet/gvisor/pkg/hostarch" 24 ktime "github.com/SagerNet/gvisor/pkg/sentry/kernel/time" 25 "github.com/SagerNet/gvisor/pkg/sentry/vfs" 26 "github.com/SagerNet/gvisor/pkg/syserror" 27 "github.com/SagerNet/gvisor/pkg/usermem" 28 "github.com/SagerNet/gvisor/pkg/waiter" 29 ) 30 31 // TimerFileDescription implements vfs.FileDescriptionImpl for timer fds. It also 32 // implements ktime.TimerListener. 33 // 34 // +stateify savable 35 type TimerFileDescription struct { 36 vfsfd vfs.FileDescription 37 vfs.FileDescriptionDefaultImpl 38 vfs.DentryMetadataFileDescriptionImpl 39 vfs.NoLockFD 40 41 events waiter.Queue 42 timer *ktime.Timer 43 44 // val is the number of timer expirations since the last successful 45 // call to PRead, or SetTime. val must be accessed using atomic memory 46 // operations. 47 val uint64 48 } 49 50 var _ vfs.FileDescriptionImpl = (*TimerFileDescription)(nil) 51 var _ ktime.TimerListener = (*TimerFileDescription)(nil) 52 53 // New returns a new timer fd. 54 func New(ctx context.Context, vfsObj *vfs.VirtualFilesystem, clock ktime.Clock, flags uint32) (*vfs.FileDescription, error) { 55 vd := vfsObj.NewAnonVirtualDentry("[timerfd]") 56 defer vd.DecRef(ctx) 57 tfd := &TimerFileDescription{} 58 tfd.timer = ktime.NewTimer(clock, tfd) 59 if err := tfd.vfsfd.Init(tfd, flags, vd.Mount(), vd.Dentry(), &vfs.FileDescriptionOptions{ 60 UseDentryMetadata: true, 61 DenyPRead: true, 62 DenyPWrite: true, 63 }); err != nil { 64 return nil, err 65 } 66 return &tfd.vfsfd, nil 67 } 68 69 // Read implements vfs.FileDescriptionImpl.Read. 70 func (tfd *TimerFileDescription) Read(ctx context.Context, dst usermem.IOSequence, opts vfs.ReadOptions) (int64, error) { 71 const sizeofUint64 = 8 72 if dst.NumBytes() < sizeofUint64 { 73 return 0, linuxerr.EINVAL 74 } 75 if val := atomic.SwapUint64(&tfd.val, 0); val != 0 { 76 var buf [sizeofUint64]byte 77 hostarch.ByteOrder.PutUint64(buf[:], val) 78 if _, err := dst.CopyOut(ctx, buf[:]); err != nil { 79 // Linux does not undo consuming the number of 80 // expirations even if writing to userspace fails. 81 return 0, err 82 } 83 return sizeofUint64, nil 84 } 85 return 0, syserror.ErrWouldBlock 86 } 87 88 // Clock returns the timer fd's Clock. 89 func (tfd *TimerFileDescription) Clock() ktime.Clock { 90 return tfd.timer.Clock() 91 } 92 93 // GetTime returns the associated Timer's setting and the time at which it was 94 // observed. 95 func (tfd *TimerFileDescription) GetTime() (ktime.Time, ktime.Setting) { 96 return tfd.timer.Get() 97 } 98 99 // SetTime atomically changes the associated Timer's setting, resets the number 100 // of expirations to 0, and returns the previous setting and the time at which 101 // it was observed. 102 func (tfd *TimerFileDescription) SetTime(s ktime.Setting) (ktime.Time, ktime.Setting) { 103 return tfd.timer.SwapAnd(s, func() { atomic.StoreUint64(&tfd.val, 0) }) 104 } 105 106 // Readiness implements waiter.Waitable.Readiness. 107 func (tfd *TimerFileDescription) Readiness(mask waiter.EventMask) waiter.EventMask { 108 var ready waiter.EventMask 109 if atomic.LoadUint64(&tfd.val) != 0 { 110 ready |= waiter.ReadableEvents 111 } 112 return ready 113 } 114 115 // EventRegister implements waiter.Waitable.EventRegister. 116 func (tfd *TimerFileDescription) EventRegister(e *waiter.Entry, mask waiter.EventMask) { 117 tfd.events.EventRegister(e, mask) 118 } 119 120 // EventUnregister implements waiter.Waitable.EventUnregister. 121 func (tfd *TimerFileDescription) EventUnregister(e *waiter.Entry) { 122 tfd.events.EventUnregister(e) 123 } 124 125 // PauseTimer pauses the associated Timer. 126 func (tfd *TimerFileDescription) PauseTimer() { 127 tfd.timer.Pause() 128 } 129 130 // ResumeTimer resumes the associated Timer. 131 func (tfd *TimerFileDescription) ResumeTimer() { 132 tfd.timer.Resume() 133 } 134 135 // Release implements vfs.FileDescriptionImpl.Release. 136 func (tfd *TimerFileDescription) Release(context.Context) { 137 tfd.timer.Destroy() 138 } 139 140 // Notify implements ktime.TimerListener.Notify. 141 func (tfd *TimerFileDescription) Notify(exp uint64, setting ktime.Setting) (ktime.Setting, bool) { 142 atomic.AddUint64(&tfd.val, exp) 143 tfd.events.Notify(waiter.ReadableEvents) 144 return ktime.Setting{}, false 145 } 146 147 // Destroy implements ktime.TimerListener.Destroy. 148 func (tfd *TimerFileDescription) Destroy() {}