summaryrefslogtreecommitdiffhomepage
path: root/pkg/sentry/fs/timerfd/timerfd.go
blob: 88c34408949666a86bfeecdc71d7cc463967d0e8 (plain)
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
150
151
// Copyright 2018 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 the semantics of Linux timerfd objects as
// described by timerfd_create(2).
package timerfd

import (
	"sync/atomic"

	"gvisor.dev/gvisor/pkg/context"
	"gvisor.dev/gvisor/pkg/sentry/fs"
	"gvisor.dev/gvisor/pkg/sentry/fs/anon"
	"gvisor.dev/gvisor/pkg/sentry/fs/fsutil"
	ktime "gvisor.dev/gvisor/pkg/sentry/kernel/time"
	"gvisor.dev/gvisor/pkg/syserror"
	"gvisor.dev/gvisor/pkg/usermem"
	"gvisor.dev/gvisor/pkg/waiter"
)

// TimerOperations implements fs.FileOperations for timerfds.
//
// +stateify savable
type TimerOperations struct {
	fsutil.FileZeroSeek             `state:"nosave"`
	fsutil.FileNotDirReaddir        `state:"nosave"`
	fsutil.FileNoFsync              `state:"nosave"`
	fsutil.FileNoIoctl              `state:"nosave"`
	fsutil.FileNoMMap               `state:"nosave"`
	fsutil.FileNoSplice             `state:"nosave"`
	fsutil.FileNoopFlush            `state:"nosave"`
	fsutil.FileUseInodeUnstableAttr `state:"nosave"`

	events waiter.Queue `state:"zerovalue"`
	timer  *ktime.Timer

	// val is the number of timer expirations since the last successful call to
	// Readv, Preadv, or SetTime. val is accessed using atomic memory
	// operations.
	val uint64
}

// NewFile returns a timerfd File that receives time from c.
func NewFile(ctx context.Context, c ktime.Clock) *fs.File {
	dirent := fs.NewDirent(ctx, anon.NewInode(ctx), "anon_inode:[timerfd]")
	// Release the initial dirent reference after NewFile takes a reference.
	defer dirent.DecRef()
	tops := &TimerOperations{}
	tops.timer = ktime.NewTimer(c, tops)
	// Timerfds reject writes, but the Write flag must be set in order to
	// ensure that our Writev/Pwritev methods actually get called to return
	// the correct errors.
	return fs.NewFile(ctx, dirent, fs.FileFlags{Read: true, Write: true}, tops)
}

// Release implements fs.FileOperations.Release.
func (t *TimerOperations) Release() {
	t.timer.Destroy()
}

// PauseTimer pauses the associated Timer.
func (t *TimerOperations) PauseTimer() {
	t.timer.Pause()
}

// ResumeTimer resumes the associated Timer.
func (t *TimerOperations) ResumeTimer() {
	t.timer.Resume()
}

// Clock returns the associated Timer's Clock.
func (t *TimerOperations) Clock() ktime.Clock {
	return t.timer.Clock()
}

// GetTime returns the associated Timer's setting and the time at which it was
// observed.
func (t *TimerOperations) GetTime() (ktime.Time, ktime.Setting) {
	return t.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 (t *TimerOperations) SetTime(s ktime.Setting) (ktime.Time, ktime.Setting) {
	return t.timer.SwapAnd(s, func() { atomic.StoreUint64(&t.val, 0) })
}

// Readiness implements waiter.Waitable.Readiness.
func (t *TimerOperations) Readiness(mask waiter.EventMask) waiter.EventMask {
	var ready waiter.EventMask
	if atomic.LoadUint64(&t.val) != 0 {
		ready |= waiter.EventIn
	}
	return ready
}

// EventRegister implements waiter.Waitable.EventRegister.
func (t *TimerOperations) EventRegister(e *waiter.Entry, mask waiter.EventMask) {
	t.events.EventRegister(e, mask)
}

// EventUnregister implements waiter.Waitable.EventUnregister.
func (t *TimerOperations) EventUnregister(e *waiter.Entry) {
	t.events.EventUnregister(e)
}

// Read implements fs.FileOperations.Read.
func (t *TimerOperations) Read(ctx context.Context, file *fs.File, dst usermem.IOSequence, offset int64) (int64, error) {
	const sizeofUint64 = 8
	if dst.NumBytes() < sizeofUint64 {
		return 0, syserror.EINVAL
	}
	if val := atomic.SwapUint64(&t.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
}

// Write implements fs.FileOperations.Write.
func (t *TimerOperations) Write(context.Context, *fs.File, usermem.IOSequence, int64) (int64, error) {
	return 0, syserror.EINVAL
}

// Notify implements ktime.TimerListener.Notify.
func (t *TimerOperations) Notify(exp uint64, setting ktime.Setting) (ktime.Setting, bool) {
	atomic.AddUint64(&t.val, exp)
	t.events.Notify(waiter.EventIn)
	return ktime.Setting{}, false
}

// Destroy implements ktime.TimerListener.Destroy.
func (t *TimerOperations) Destroy() {}