github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fs/timerfd/timerfd.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 timerfd implements the semantics of Linux timerfd objects as 16 // described by timerfd_create(2). 17 package timerfd 18 19 import ( 20 "sync/atomic" 21 22 "github.com/SagerNet/gvisor/pkg/context" 23 "github.com/SagerNet/gvisor/pkg/errors/linuxerr" 24 "github.com/SagerNet/gvisor/pkg/hostarch" 25 "github.com/SagerNet/gvisor/pkg/sentry/fs" 26 "github.com/SagerNet/gvisor/pkg/sentry/fs/anon" 27 "github.com/SagerNet/gvisor/pkg/sentry/fs/fsutil" 28 ktime "github.com/SagerNet/gvisor/pkg/sentry/kernel/time" 29 "github.com/SagerNet/gvisor/pkg/syserror" 30 "github.com/SagerNet/gvisor/pkg/usermem" 31 "github.com/SagerNet/gvisor/pkg/waiter" 32 ) 33 34 // TimerOperations implements fs.FileOperations for timerfds. 35 // 36 // +stateify savable 37 type TimerOperations struct { 38 fsutil.FileZeroSeek `state:"nosave"` 39 fsutil.FileNotDirReaddir `state:"nosave"` 40 fsutil.FileNoFsync `state:"nosave"` 41 fsutil.FileNoIoctl `state:"nosave"` 42 fsutil.FileNoMMap `state:"nosave"` 43 fsutil.FileNoSplice `state:"nosave"` 44 fsutil.FileNoopFlush `state:"nosave"` 45 fsutil.FileUseInodeUnstableAttr `state:"nosave"` 46 47 events waiter.Queue `state:"zerovalue"` 48 timer *ktime.Timer 49 50 // val is the number of timer expirations since the last successful call to 51 // Readv, Preadv, or SetTime. val is accessed using atomic memory 52 // operations. 53 val uint64 54 } 55 56 // NewFile returns a timerfd File that receives time from c. 57 func NewFile(ctx context.Context, c ktime.Clock) *fs.File { 58 dirent := fs.NewDirent(ctx, anon.NewInode(ctx), "anon_inode:[timerfd]") 59 // Release the initial dirent reference after NewFile takes a reference. 60 defer dirent.DecRef(ctx) 61 tops := &TimerOperations{} 62 tops.timer = ktime.NewTimer(c, tops) 63 // Timerfds reject writes, but the Write flag must be set in order to 64 // ensure that our Writev/Pwritev methods actually get called to return 65 // the correct errors. 66 return fs.NewFile(ctx, dirent, fs.FileFlags{Read: true, Write: true}, tops) 67 } 68 69 // Release implements fs.FileOperations.Release. 70 func (t *TimerOperations) Release(context.Context) { 71 t.timer.Destroy() 72 } 73 74 // PauseTimer pauses the associated Timer. 75 func (t *TimerOperations) PauseTimer() { 76 t.timer.Pause() 77 } 78 79 // ResumeTimer resumes the associated Timer. 80 func (t *TimerOperations) ResumeTimer() { 81 t.timer.Resume() 82 } 83 84 // Clock returns the associated Timer's Clock. 85 func (t *TimerOperations) Clock() ktime.Clock { 86 return t.timer.Clock() 87 } 88 89 // GetTime returns the associated Timer's setting and the time at which it was 90 // observed. 91 func (t *TimerOperations) GetTime() (ktime.Time, ktime.Setting) { 92 return t.timer.Get() 93 } 94 95 // SetTime atomically changes the associated Timer's setting, resets the number 96 // of expirations to 0, and returns the previous setting and the time at which 97 // it was observed. 98 func (t *TimerOperations) SetTime(s ktime.Setting) (ktime.Time, ktime.Setting) { 99 return t.timer.SwapAnd(s, func() { atomic.StoreUint64(&t.val, 0) }) 100 } 101 102 // Readiness implements waiter.Waitable.Readiness. 103 func (t *TimerOperations) Readiness(mask waiter.EventMask) waiter.EventMask { 104 var ready waiter.EventMask 105 if atomic.LoadUint64(&t.val) != 0 { 106 ready |= waiter.ReadableEvents 107 } 108 return ready 109 } 110 111 // EventRegister implements waiter.Waitable.EventRegister. 112 func (t *TimerOperations) EventRegister(e *waiter.Entry, mask waiter.EventMask) { 113 t.events.EventRegister(e, mask) 114 } 115 116 // EventUnregister implements waiter.Waitable.EventUnregister. 117 func (t *TimerOperations) EventUnregister(e *waiter.Entry) { 118 t.events.EventUnregister(e) 119 } 120 121 // Read implements fs.FileOperations.Read. 122 func (t *TimerOperations) Read(ctx context.Context, file *fs.File, dst usermem.IOSequence, offset int64) (int64, error) { 123 const sizeofUint64 = 8 124 if dst.NumBytes() < sizeofUint64 { 125 return 0, linuxerr.EINVAL 126 } 127 if val := atomic.SwapUint64(&t.val, 0); val != 0 { 128 var buf [sizeofUint64]byte 129 hostarch.ByteOrder.PutUint64(buf[:], val) 130 if _, err := dst.CopyOut(ctx, buf[:]); err != nil { 131 // Linux does not undo consuming the number of expirations even if 132 // writing to userspace fails. 133 return 0, err 134 } 135 return sizeofUint64, nil 136 } 137 return 0, syserror.ErrWouldBlock 138 } 139 140 // Write implements fs.FileOperations.Write. 141 func (t *TimerOperations) Write(context.Context, *fs.File, usermem.IOSequence, int64) (int64, error) { 142 return 0, linuxerr.EINVAL 143 } 144 145 // Notify implements ktime.TimerListener.Notify. 146 func (t *TimerOperations) Notify(exp uint64, setting ktime.Setting) (ktime.Setting, bool) { 147 atomic.AddUint64(&t.val, exp) 148 t.events.Notify(waiter.ReadableEvents) 149 return ktime.Setting{}, false 150 } 151 152 // Destroy implements ktime.TimerListener.Destroy. 153 func (t *TimerOperations) Destroy() {}