1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
|
// Copyright 2020 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package timerfd implements timer fds.
package timerfd
import (
"gvisor.dev/gvisor/pkg/atomicbitops"
"gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
ktime "gvisor.dev/gvisor/pkg/sentry/kernel/time"
"gvisor.dev/gvisor/pkg/sentry/vfs"
"gvisor.dev/gvisor/pkg/usermem"
"gvisor.dev/gvisor/pkg/waiter"
)
// TimerFileDescription implements vfs.FileDescriptionImpl for timer fds. It also
// implements ktime.TimerListener.
//
// +stateify savable
type TimerFileDescription struct {
vfsfd vfs.FileDescription
vfs.FileDescriptionDefaultImpl
vfs.DentryMetadataFileDescriptionImpl
vfs.NoLockFD
events waiter.Queue
timer *ktime.Timer
// val is the number of timer expirations since the last successful
// call to PRead, or SetTime. val must be accessed using atomic memory
// operations.
val atomicbitops.Uint64
}
var _ vfs.FileDescriptionImpl = (*TimerFileDescription)(nil)
var _ ktime.Listener = (*TimerFileDescription)(nil)
// New returns a new timer fd.
func New(ctx context.Context, vfsObj *vfs.VirtualFilesystem, clock ktime.Clock, flags uint32) (*vfs.FileDescription, error) {
vd := vfsObj.NewAnonVirtualDentry("[timerfd]")
defer vd.DecRef(ctx)
tfd := &TimerFileDescription{}
tfd.timer = ktime.NewTimer(clock, tfd)
if err := tfd.vfsfd.Init(tfd, flags, vd.Mount(), vd.Dentry(), &vfs.FileDescriptionOptions{
UseDentryMetadata: true,
DenyPRead: true,
DenyPWrite: true,
}); err != nil {
return nil, err
}
return &tfd.vfsfd, nil
}
// Read implements vfs.FileDescriptionImpl.Read.
func (tfd *TimerFileDescription) Read(ctx context.Context, dst usermem.IOSequence, opts vfs.ReadOptions) (int64, error) {
const sizeofUint64 = 8
if dst.NumBytes() < sizeofUint64 {
return 0, linuxerr.EINVAL
}
if val := tfd.val.Swap(0); val != 0 {
var buf [sizeofUint64]byte
hostarch.ByteOrder.PutUint64(buf[:], val)
if _, err := dst.CopyOut(ctx, buf[:]); err != nil {
// Linux does not undo consuming the number of
// expirations even if writing to userspace fails.
return 0, err
}
return sizeofUint64, nil
}
return 0, linuxerr.ErrWouldBlock
}
// Clock returns the timer fd's Clock.
func (tfd *TimerFileDescription) Clock() ktime.Clock {
return tfd.timer.Clock()
}
// GetTime returns the associated Timer's setting and the time at which it was
// observed.
func (tfd *TimerFileDescription) GetTime() (ktime.Time, ktime.Setting) {
return tfd.timer.Get()
}
// SetTime atomically changes the associated Timer's setting, resets the number
// of expirations to 0, and returns the previous setting and the time at which
// it was observed.
func (tfd *TimerFileDescription) SetTime(s ktime.Setting) (ktime.Time, ktime.Setting) {
return tfd.timer.SwapAnd(s, func() { tfd.val.Store(0) })
}
// Readiness implements waiter.Waitable.Readiness.
func (tfd *TimerFileDescription) Readiness(mask waiter.EventMask) waiter.EventMask {
var ready waiter.EventMask
if tfd.val.Load() != 0 {
ready |= waiter.ReadableEvents
}
return ready
}
// EventRegister implements waiter.Waitable.EventRegister.
func (tfd *TimerFileDescription) EventRegister(e *waiter.Entry) error {
tfd.events.EventRegister(e)
return nil
}
// EventUnregister implements waiter.Waitable.EventUnregister.
func (tfd *TimerFileDescription) EventUnregister(e *waiter.Entry) {
tfd.events.EventUnregister(e)
}
// Epollable implements FileDescriptionImpl.Epollable.
func (tfd *TimerFileDescription) Epollable() bool {
return true
}
// PauseTimer pauses the associated Timer.
func (tfd *TimerFileDescription) PauseTimer() {
tfd.timer.Pause()
}
// ResumeTimer resumes the associated Timer.
func (tfd *TimerFileDescription) ResumeTimer() {
tfd.timer.Resume()
}
// Release implements vfs.FileDescriptionImpl.Release.
func (tfd *TimerFileDescription) Release(context.Context) {
tfd.timer.Destroy()
}
// NotifyTimer implements ktime.TimerListener.NotifyTimer.
func (tfd *TimerFileDescription) NotifyTimer(exp uint64, setting ktime.Setting) (ktime.Setting, bool) {
tfd.val.Add(exp)
tfd.events.Notify(waiter.ReadableEvents)
return ktime.Setting{}, false
}
|