143 lines
4.4 KiB
Go
143 lines
4.4 KiB
Go
// 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 vfs
|
|
|
|
import (
|
|
"sync/atomic"
|
|
|
|
"gvisor.dev/gvisor/pkg/context"
|
|
ktime "gvisor.dev/gvisor/pkg/sentry/kernel/time"
|
|
"gvisor.dev/gvisor/pkg/syserror"
|
|
"gvisor.dev/gvisor/pkg/usermem"
|
|
"gvisor.dev/gvisor/pkg/waiter"
|
|
)
|
|
|
|
// TimerFileDescription implements FileDescriptionImpl for timer fds. It also
|
|
// implements ktime.TimerListener.
|
|
type TimerFileDescription struct {
|
|
vfsfd FileDescription
|
|
FileDescriptionDefaultImpl
|
|
DentryMetadataFileDescriptionImpl
|
|
|
|
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 uint64
|
|
}
|
|
|
|
var _ FileDescriptionImpl = (*TimerFileDescription)(nil)
|
|
var _ ktime.TimerListener = (*TimerFileDescription)(nil)
|
|
|
|
// NewTimerFD returns a new timer fd.
|
|
func (vfs *VirtualFilesystem) NewTimerFD(clock ktime.Clock, flags uint32) (*FileDescription, error) {
|
|
vd := vfs.NewAnonVirtualDentry("[timerfd]")
|
|
defer vd.DecRef()
|
|
tfd := &TimerFileDescription{}
|
|
tfd.timer = ktime.NewTimer(clock, tfd)
|
|
if err := tfd.vfsfd.Init(tfd, flags, vd.Mount(), vd.Dentry(), &FileDescriptionOptions{
|
|
UseDentryMetadata: true,
|
|
DenyPRead: true,
|
|
DenyPWrite: true,
|
|
InvalidWrite: true,
|
|
}); err != nil {
|
|
return nil, err
|
|
}
|
|
return &tfd.vfsfd, nil
|
|
}
|
|
|
|
// Read implements FileDescriptionImpl.Read.
|
|
func (tfd *TimerFileDescription) Read(ctx context.Context, dst usermem.IOSequence, opts ReadOptions) (int64, error) {
|
|
const sizeofUint64 = 8
|
|
if dst.NumBytes() < sizeofUint64 {
|
|
return 0, syserror.EINVAL
|
|
}
|
|
if val := atomic.SwapUint64(&tfd.val, 0); val != 0 {
|
|
var buf [sizeofUint64]byte
|
|
usermem.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, syserror.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() { atomic.StoreUint64(&tfd.val, 0) })
|
|
}
|
|
|
|
// Readiness implements waiter.Waitable.Readiness.
|
|
func (tfd *TimerFileDescription) Readiness(mask waiter.EventMask) waiter.EventMask {
|
|
var ready waiter.EventMask
|
|
if atomic.LoadUint64(&tfd.val) != 0 {
|
|
ready |= waiter.EventIn
|
|
}
|
|
return ready
|
|
}
|
|
|
|
// EventRegister implements waiter.Waitable.EventRegister.
|
|
func (tfd *TimerFileDescription) EventRegister(e *waiter.Entry, mask waiter.EventMask) {
|
|
tfd.events.EventRegister(e, mask)
|
|
}
|
|
|
|
// EventUnregister implements waiter.Waitable.EventUnregister.
|
|
func (tfd *TimerFileDescription) EventUnregister(e *waiter.Entry) {
|
|
tfd.events.EventUnregister(e)
|
|
}
|
|
|
|
// 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 FileDescriptionImpl.Release()
|
|
func (tfd *TimerFileDescription) Release() {
|
|
tfd.timer.Destroy()
|
|
}
|
|
|
|
// Notify implements ktime.TimerListener.Notify.
|
|
func (tfd *TimerFileDescription) Notify(exp uint64, setting ktime.Setting) (ktime.Setting, bool) {
|
|
atomic.AddUint64(&tfd.val, exp)
|
|
tfd.events.Notify(waiter.EventIn)
|
|
return ktime.Setting{}, false
|
|
}
|
|
|
|
// Destroy implements ktime.TimerListener.Destroy.
|
|
func (tfd *TimerFileDescription) Destroy() {}
|