Compare commits
3 Commits
irbekrm/fi
...
josh/fast-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
197edcedec | ||
|
|
2a8ca8a592 | ||
|
|
a43f939828 |
@@ -18,6 +18,7 @@ import (
|
||||
"golang.zx2c4.com/wireguard/tun"
|
||||
"inet.af/netaddr"
|
||||
"tailscale.com/net/packet"
|
||||
"tailscale.com/tstime"
|
||||
"tailscale.com/types/ipproto"
|
||||
"tailscale.com/types/logger"
|
||||
"tailscale.com/wgengine/filter"
|
||||
@@ -363,7 +364,7 @@ func (t *Wrapper) filterOut(p *packet.Parsed) filter.Response {
|
||||
|
||||
// noteActivity records that there was a read or write at the current time.
|
||||
func (t *Wrapper) noteActivity() {
|
||||
atomic.StoreInt64(&t.lastActivityAtomic, time.Now().Unix())
|
||||
atomic.StoreInt64(&t.lastActivityAtomic, tstime.MonotonicCoarse())
|
||||
}
|
||||
|
||||
// IdleDuration reports how long it's been since the last read or write to this device.
|
||||
@@ -371,8 +372,8 @@ func (t *Wrapper) noteActivity() {
|
||||
// Its value is only accurate to roughly second granularity.
|
||||
// If there's never been activity, the duration is since 1970.
|
||||
func (t *Wrapper) IdleDuration() time.Duration {
|
||||
sec := atomic.LoadInt64(&t.lastActivityAtomic)
|
||||
return time.Since(time.Unix(sec, 0))
|
||||
elapsed := tstime.MonotonicCoarse() - atomic.LoadInt64(&t.lastActivityAtomic)
|
||||
return time.Duration(elapsed) * time.Second
|
||||
}
|
||||
|
||||
func (t *Wrapper) Read(buf []byte, offset int) (int, error) {
|
||||
|
||||
34
tstime/monocoarse_asm.go
Normal file
34
tstime/monocoarse_asm.go
Normal file
@@ -0,0 +1,34 @@
|
||||
// +build go1.16,!go1.17
|
||||
// +build linux,amd64 linux,arm64
|
||||
|
||||
package tstime
|
||||
|
||||
const (
|
||||
CLOCK_MONOTONIC = 1
|
||||
CLOCK_MONOTONIC_COARSE = 6
|
||||
)
|
||||
|
||||
// MonotonicCoarse returns the number of monotonic seconds elapsed
|
||||
// since an unspecified starting point, at low precision.
|
||||
// It is only meaningful when compared to the
|
||||
// result of previous MonotonicCoarse calls.
|
||||
// On some platforms, MonotonicCoarse is much faster than time.Now.
|
||||
func monoClock(clock int) int64
|
||||
|
||||
// Monotonic returns the number of monotonic seconds elapsed
|
||||
// since an unspecified starting point, at low precision.
|
||||
// It is only meaningful when compared to the
|
||||
// result of previous Monotonic calls.
|
||||
// On some platforms, Monotonic is much faster than time.Now.
|
||||
func Monotonic() int64 {
|
||||
return monoClock(CLOCK_MONOTONIC)
|
||||
}
|
||||
|
||||
// MonotonicCoarse returns the number of monotonic seconds elapsed
|
||||
// since an unspecified starting point, at low precision.
|
||||
// It is only meaningful when compared to the
|
||||
// result of previous MonotonicCoarse calls.
|
||||
// On some platforms, MonotonicCoarse is much faster than time.Now.
|
||||
func MonotonicCoarse() int64 {
|
||||
return monoClock(CLOCK_MONOTONIC_COARSE)
|
||||
}
|
||||
107
tstime/monocoarse_linux_amd64.s
Normal file
107
tstime/monocoarse_linux_amd64.s
Normal file
@@ -0,0 +1,107 @@
|
||||
// Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Adapted from code in the Go runtime package at Go 1.16.6:
|
||||
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build go1.16
|
||||
// +build !go1.17
|
||||
|
||||
#include "go_asm.h"
|
||||
#include "textflag.h"
|
||||
|
||||
#define get_tls(r) MOVQ TLS, r
|
||||
#define g(r) 0(r)(TLS*1)
|
||||
|
||||
#define SYS_clock_gettime 228
|
||||
|
||||
// Hard-coded offsets into runtime structs.
|
||||
// Generated by adding the following code
|
||||
// to package runtime and then executing
|
||||
// and empty func main:
|
||||
//
|
||||
// func init() {
|
||||
// println("#define g_m", unsafe.Offsetof(g{}.m))
|
||||
// println("#define g_sched", unsafe.Offsetof(g{}.sched))
|
||||
// println("#define gobuf_sp", unsafe.Offsetof(g{}.sched.sp))
|
||||
// println("#define m_g0", unsafe.Offsetof(m{}.g0))
|
||||
// println("#define m_curg", unsafe.Offsetof(m{}.curg))
|
||||
// println("#define m_vdsoSP", unsafe.Offsetof(m{}.vdsoSP))
|
||||
// println("#define m_vdsoPC", unsafe.Offsetof(m{}.vdsoPC))
|
||||
// }
|
||||
|
||||
#define g_m 48
|
||||
#define g_sched 56
|
||||
#define gobuf_sp 0
|
||||
#define m_g0 0
|
||||
#define m_curg 192
|
||||
#define m_vdsoSP 832
|
||||
#define m_vdsoPC 840
|
||||
|
||||
// func monoClock(clock int) int64
|
||||
TEXT ·monoClock(SB),NOSPLIT,$16-16
|
||||
MOVQ clock+0(FP), DI
|
||||
|
||||
// Switch to g0 stack.
|
||||
|
||||
MOVQ SP, R12 // Save old SP; R12 unchanged by C code.
|
||||
|
||||
get_tls(CX)
|
||||
MOVQ g(CX), AX
|
||||
MOVQ g_m(AX), BX // BX unchanged by C code.
|
||||
|
||||
// Set vdsoPC and vdsoSP for SIGPROF traceback.
|
||||
// Save the old values on stack and restore them on exit,
|
||||
// so this function is reentrant.
|
||||
MOVQ m_vdsoPC(BX), CX
|
||||
MOVQ m_vdsoSP(BX), DX
|
||||
MOVQ CX, 0(SP)
|
||||
MOVQ DX, 8(SP)
|
||||
|
||||
LEAQ ret+8(FP), DX
|
||||
MOVQ -8(DX), CX
|
||||
MOVQ CX, m_vdsoPC(BX)
|
||||
MOVQ DX, m_vdsoSP(BX)
|
||||
|
||||
CMPQ AX, m_curg(BX) // Only switch if on curg.
|
||||
JNE noswitch
|
||||
|
||||
MOVQ m_g0(BX), DX
|
||||
MOVQ (g_sched+gobuf_sp)(DX), SP // Set SP to g0 stack
|
||||
|
||||
noswitch:
|
||||
SUBQ $16, SP // Space for results
|
||||
ANDQ $~15, SP // Align for C code
|
||||
|
||||
LEAQ 0(SP), SI
|
||||
MOVQ runtime·vdsoClockgettimeSym(SB), AX
|
||||
CMPQ AX, $0
|
||||
JEQ fallback
|
||||
CALL AX
|
||||
ret:
|
||||
MOVQ 0(SP), AX // sec
|
||||
MOVQ 8(SP), DX // nsec
|
||||
MOVQ R12, SP // Restore real SP
|
||||
// Restore vdsoPC, vdsoSP
|
||||
// We don't worry about being signaled between the two stores.
|
||||
// If we are not in a signal handler, we'll restore vdsoSP to 0,
|
||||
// and no one will care about vdsoPC. If we are in a signal handler,
|
||||
// we cannot receive another signal.
|
||||
MOVQ 8(SP), CX
|
||||
MOVQ CX, m_vdsoSP(BX)
|
||||
MOVQ 0(SP), CX
|
||||
MOVQ CX, m_vdsoPC(BX)
|
||||
// sec is in AX, nsec in DX
|
||||
// return nsec in AX
|
||||
IMULQ $1000000000, AX
|
||||
ADDQ DX, AX
|
||||
MOVQ AX, ret+8(FP)
|
||||
RET
|
||||
fallback:
|
||||
MOVQ $SYS_clock_gettime, AX
|
||||
SYSCALL
|
||||
JMP ret
|
||||
137
tstime/monocoarse_linux_arm64.s
Normal file
137
tstime/monocoarse_linux_arm64.s
Normal file
@@ -0,0 +1,137 @@
|
||||
// Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Adapted from code in the Go runtime package at Go 1.16.6:
|
||||
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build go1.16
|
||||
// +build !go1.17
|
||||
|
||||
#include "go_asm.h"
|
||||
#include "textflag.h"
|
||||
|
||||
#define SYS_clock_gettime 113
|
||||
|
||||
// Hard-coded offsets into runtime structs.
|
||||
// Generated by adding the following code
|
||||
// to package runtime and then executing
|
||||
// and empty func main:
|
||||
//
|
||||
// func init() {
|
||||
// println("#define g_m", unsafe.Offsetof(g{}.m))
|
||||
// println("#define g_sched", unsafe.Offsetof(g{}.sched))
|
||||
// println("#define gobuf_sp", unsafe.Offsetof(g{}.sched.sp))
|
||||
// println("#define g_stack", unsafe.Offsetof(g{}.stack))
|
||||
// println("#define stack_lo", unsafe.Offsetof(g{}.stack.lo))
|
||||
// println("#define m_g0", unsafe.Offsetof(m{}.g0))
|
||||
// println("#define m_curg", unsafe.Offsetof(m{}.curg))
|
||||
// println("#define m_vdsoSP", unsafe.Offsetof(m{}.vdsoSP))
|
||||
// println("#define m_vdsoPC", unsafe.Offsetof(m{}.vdsoPC))
|
||||
// println("#define m_gsignal", unsafe.Offsetof(m{}.gsignal))
|
||||
// }
|
||||
|
||||
#define g_m 48
|
||||
#define g_sched 56
|
||||
#define gobuf_sp 0
|
||||
#define g_stack 0
|
||||
#define stack_lo 0
|
||||
#define m_g0 0
|
||||
#define m_curg 192
|
||||
#define m_vdsoSP 832
|
||||
#define m_vdsoPC 840
|
||||
#define m_gsignal 80
|
||||
|
||||
|
||||
// func monoClock(clock int) int64
|
||||
TEXT ·monoClock(SB),NOSPLIT,$24-16
|
||||
MOVD clock+0(FP), R22
|
||||
|
||||
MOVD RSP, R20 // R20 is unchanged by C code
|
||||
MOVD RSP, R1
|
||||
|
||||
MOVD g_m(g), R21 // R21 = m
|
||||
|
||||
// Set vdsoPC and vdsoSP for SIGPROF traceback.
|
||||
// Save the old values on stack and restore them on exit,
|
||||
// so this function is reentrant.
|
||||
MOVD m_vdsoPC(R21), R2
|
||||
MOVD m_vdsoSP(R21), R3
|
||||
MOVD R2, 8(RSP)
|
||||
MOVD R3, 16(RSP)
|
||||
|
||||
MOVD LR, m_vdsoPC(R21)
|
||||
MOVD R20, m_vdsoSP(R21)
|
||||
|
||||
MOVD m_curg(R21), R0
|
||||
CMP g, R0
|
||||
BNE noswitch
|
||||
|
||||
MOVD m_g0(R21), R3
|
||||
MOVD (g_sched+gobuf_sp)(R3), R1 // Set RSP to g0 stack
|
||||
|
||||
noswitch:
|
||||
SUB $32, R1
|
||||
BIC $15, R1
|
||||
MOVD R1, RSP
|
||||
|
||||
MOVW R22, R0
|
||||
MOVD runtime·vdsoClockgettimeSym(SB), R2
|
||||
CBZ R2, fallback
|
||||
|
||||
// Store g on gsignal's stack, so if we receive a signal
|
||||
// during VDSO code we can find the g.
|
||||
// If we don't have a signal stack, we won't receive signal,
|
||||
// so don't bother saving g.
|
||||
// When using cgo, we already saved g on TLS, also don't save
|
||||
// g here.
|
||||
// Also don't save g if we are already on the signal stack.
|
||||
// We won't get a nested signal.
|
||||
MOVBU runtime·iscgo(SB), R22
|
||||
CBNZ R22, nosaveg
|
||||
MOVD m_gsignal(R21), R22 // g.m.gsignal
|
||||
CBZ R22, nosaveg
|
||||
CMP g, R22
|
||||
BEQ nosaveg
|
||||
MOVD (g_stack+stack_lo)(R22), R22 // g.m.gsignal.stack.lo
|
||||
MOVD g, (R22)
|
||||
|
||||
BL (R2)
|
||||
|
||||
MOVD ZR, (R22) // clear g slot, R22 is unchanged by C code
|
||||
|
||||
B finish
|
||||
|
||||
nosaveg:
|
||||
BL (R2)
|
||||
B finish
|
||||
|
||||
fallback:
|
||||
MOVD $SYS_clock_gettime, R8
|
||||
SVC
|
||||
|
||||
finish:
|
||||
MOVD 0(RSP), R3 // sec
|
||||
MOVD 8(RSP), R5 // nsec
|
||||
|
||||
MOVD R20, RSP // restore SP
|
||||
// Restore vdsoPC, vdsoSP
|
||||
// We don't worry about being signaled between the two stores.
|
||||
// If we are not in a signal handler, we'll restore vdsoSP to 0,
|
||||
// and no one will care about vdsoPC. If we are in a signal handler,
|
||||
// we cannot receive another signal.
|
||||
MOVD 16(RSP), R1
|
||||
MOVD R1, m_vdsoSP(R21)
|
||||
MOVD 8(RSP), R1
|
||||
MOVD R1, m_vdsoPC(R21)
|
||||
|
||||
// sec is in R3, nsec in R5
|
||||
// return nsec in R3
|
||||
MOVD $1000000000, R4
|
||||
MUL R4, R3
|
||||
ADD R5, R3
|
||||
MOVD R3, ret+8(FP)
|
||||
RET
|
||||
25
tstime/monocoarse_std.go
Normal file
25
tstime/monocoarse_std.go
Normal file
@@ -0,0 +1,25 @@
|
||||
// +build !go1.16 go1.17 !linux !amd64,!arm64
|
||||
|
||||
package tstime
|
||||
|
||||
import "time"
|
||||
|
||||
var referenceTime = time.Now()
|
||||
|
||||
// MonotonicCoarse returns the number of monotonic seconds elapsed
|
||||
// since an unspecified starting point, at low precision.
|
||||
// It is only meaningful when compared to the
|
||||
// result of previous MonotonicCoarse calls.
|
||||
// On some platforms, MonotonicCoarse is much faster than time.Now.
|
||||
func MonotonicCoarse() int64 {
|
||||
return int64(time.Since(referenceTime).Seconds())
|
||||
}
|
||||
|
||||
// Monotonic returns the number of monotonic seconds elapsed
|
||||
// since an unspecified starting point, at low precision.
|
||||
// It is only meaningful when compared to the
|
||||
// result of previous MonotonicCoarse calls.
|
||||
// On some platforms, MonotonicCoarse is much faster than time.Now.
|
||||
func Monotonic() int64 {
|
||||
return int64(time.Since(referenceTime).Seconds())
|
||||
}
|
||||
35
tstime/monocoarse_test.go
Normal file
35
tstime/monocoarse_test.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package tstime
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestMonotonicCoarse(t *testing.T) {
|
||||
t.Parallel()
|
||||
start := MonotonicCoarse()
|
||||
for n := 0; n < 30; n++ {
|
||||
end := MonotonicCoarse()
|
||||
if end == start {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
continue
|
||||
}
|
||||
if end-start != 1 {
|
||||
t.Errorf("monotonic coarse time jumped: %v seconds", end-start)
|
||||
}
|
||||
return // success
|
||||
}
|
||||
t.Errorf("monotonic coarse time did not progress after 3s")
|
||||
}
|
||||
|
||||
func BenchmarkMonotonic(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
Monotonic()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMonotonicCoarse(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
MonotonicCoarse()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user