Skip to content

Commit ca55c18

Browse files
nixprimegvisor-bot
authored andcommitted
Use Task blocking timer for nanosleep(2).
kernel/time.Timer allocation is expensive and not sync.Poolable (since time.Timer only supports notification through a channel, requiring a goroutine to receive from the channel, and sync.Pool doesn't invoke any kind of cleanup on discarded items in the pool so it would leak timer goroutines). Using the existing Task.blockingTimer for nanosleep(), and applicable cases in clock_nanosleep(), at least avoids Timer allocation in common cases. PiperOrigin-RevId: 406248394
1 parent d350c95 commit ca55c18

File tree

1 file changed

+71
-68
lines changed

1 file changed

+71
-68
lines changed

pkg/sentry/syscalls/linux/sys_time.go

Lines changed: 71 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -175,73 +175,6 @@ func Time(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallC
175175
return uintptr(r), nil, nil
176176
}
177177

178-
// clockNanosleepRestartBlock encapsulates the state required to restart
179-
// clock_nanosleep(2) via restart_syscall(2).
180-
//
181-
// +stateify savable
182-
type clockNanosleepRestartBlock struct {
183-
c ktime.Clock
184-
end ktime.Time
185-
rem hostarch.Addr
186-
}
187-
188-
// Restart implements kernel.SyscallRestartBlock.Restart.
189-
func (n *clockNanosleepRestartBlock) Restart(t *kernel.Task) (uintptr, error) {
190-
return 0, clockNanosleepUntil(t, n.c, n.end, n.rem, true)
191-
}
192-
193-
// clockNanosleepUntil blocks until a specified time.
194-
//
195-
// If blocking is interrupted, the syscall is restarted with the original
196-
// arguments.
197-
func clockNanosleepUntil(t *kernel.Task, c ktime.Clock, end ktime.Time, rem hostarch.Addr, needRestartBlock bool) error {
198-
notifier, tchan := ktime.NewChannelNotifier()
199-
timer := ktime.NewTimer(c, notifier)
200-
201-
// Turn on the timer.
202-
timer.Swap(ktime.Setting{
203-
Period: 0,
204-
Enabled: true,
205-
Next: end,
206-
})
207-
208-
err := t.BlockWithTimer(nil, tchan)
209-
210-
timer.Destroy()
211-
212-
switch {
213-
case linuxerr.Equals(linuxerr.ETIMEDOUT, err):
214-
// Slept for entire timeout.
215-
return nil
216-
case err == linuxerr.ErrInterrupted:
217-
// Interrupted.
218-
remaining := end.Sub(c.Now())
219-
if remaining <= 0 {
220-
return nil
221-
}
222-
223-
// Copy out remaining time.
224-
if rem != 0 {
225-
timeleft := linux.NsecToTimespec(remaining.Nanoseconds())
226-
if err := copyTimespecOut(t, rem, &timeleft); err != nil {
227-
return err
228-
}
229-
}
230-
if needRestartBlock {
231-
// Arrange for a restart with the remaining duration.
232-
t.SetSyscallRestartBlock(&clockNanosleepRestartBlock{
233-
c: c,
234-
end: end,
235-
rem: rem,
236-
})
237-
return linuxerr.ERESTART_RESTARTBLOCK
238-
}
239-
return linuxerr.ERESTARTNOHAND
240-
default:
241-
panic(fmt.Sprintf("Impossible BlockWithTimer error %v", err))
242-
}
243-
}
244-
245178
// Nanosleep implements linux syscall Nanosleep(2).
246179
func Nanosleep(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
247180
addr := args[0].Pointer()
@@ -279,10 +212,12 @@ func ClockNanosleep(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kerne
279212
return 0, nil, linuxerr.EINVAL
280213
}
281214

282-
// Only allow clock constants also allowed by Linux.
215+
// Only allow clock constants also allowed by Linux. (CLOCK_TAI is
216+
// unimplemented.)
283217
if clockID > 0 {
284218
if clockID != linux.CLOCK_REALTIME &&
285219
clockID != linux.CLOCK_MONOTONIC &&
220+
clockID != linux.CLOCK_BOOTTIME &&
286221
clockID != linux.CLOCK_PROCESS_CPUTIME_ID {
287222
return 0, nil, linuxerr.EINVAL
288223
}
@@ -301,6 +236,74 @@ func ClockNanosleep(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kerne
301236
return 0, nil, clockNanosleepUntil(t, c, c.Now().Add(dur), rem, true)
302237
}
303238

239+
// clockNanosleepUntil blocks until a specified time.
240+
//
241+
// If blocking is interrupted, the syscall is restarted with the original
242+
// arguments.
243+
func clockNanosleepUntil(t *kernel.Task, c ktime.Clock, end ktime.Time, rem hostarch.Addr, needRestartBlock bool) error {
244+
var err error
245+
if c == t.Kernel().MonotonicClock() {
246+
err = t.BlockWithDeadline(nil, true, end)
247+
} else {
248+
notifier, tchan := ktime.NewChannelNotifier()
249+
timer := ktime.NewTimer(c, notifier)
250+
timer.Swap(ktime.Setting{
251+
Period: 0,
252+
Enabled: true,
253+
Next: end,
254+
})
255+
err = t.BlockWithTimer(nil, tchan)
256+
timer.Destroy()
257+
}
258+
259+
switch {
260+
case linuxerr.Equals(linuxerr.ETIMEDOUT, err):
261+
// Slept for entire timeout.
262+
return nil
263+
case err == linuxerr.ErrInterrupted:
264+
// Interrupted.
265+
remaining := end.Sub(c.Now())
266+
if remaining <= 0 {
267+
return nil
268+
}
269+
270+
// Copy out remaining time.
271+
if rem != 0 {
272+
timeleft := linux.NsecToTimespec(remaining.Nanoseconds())
273+
if err := copyTimespecOut(t, rem, &timeleft); err != nil {
274+
return err
275+
}
276+
}
277+
if needRestartBlock {
278+
// Arrange for a restart with the remaining duration.
279+
t.SetSyscallRestartBlock(&clockNanosleepRestartBlock{
280+
c: c,
281+
end: end,
282+
rem: rem,
283+
})
284+
return linuxerr.ERESTART_RESTARTBLOCK
285+
}
286+
return linuxerr.ERESTARTNOHAND
287+
default:
288+
panic(fmt.Sprintf("Impossible BlockWithTimer error %v", err))
289+
}
290+
}
291+
292+
// clockNanosleepRestartBlock encapsulates the state required to restart
293+
// clock_nanosleep(2) via restart_syscall(2).
294+
//
295+
// +stateify savable
296+
type clockNanosleepRestartBlock struct {
297+
c ktime.Clock
298+
end ktime.Time
299+
rem hostarch.Addr
300+
}
301+
302+
// Restart implements kernel.SyscallRestartBlock.Restart.
303+
func (n *clockNanosleepRestartBlock) Restart(t *kernel.Task) (uintptr, error) {
304+
return 0, clockNanosleepUntil(t, n.c, n.end, n.rem, true)
305+
}
306+
304307
// Gettimeofday implements linux syscall gettimeofday(2).
305308
func Gettimeofday(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
306309
tv := args[0].Pointer()

0 commit comments

Comments
 (0)