mirror of
https://github.com/hanzoai/visor.git
synced 2026-09-24 17:04:14 +00:00
Merge pull request #14002 from luiscape:luis/allign-clocks-in-capprofiling
PiperOrigin-RevId: 968200399
This commit is contained in:
@@ -93,7 +93,7 @@ func Boot() (*kernel.Kernel, error) {
|
||||
// Create timekeeper.
|
||||
tk := kernel.NewTimekeeper()
|
||||
params := kernel.NewVDSOParamPage(k.MemoryFile(), vdso.ParamPage.FileRange())
|
||||
tk.SetClocks(time.NewCalibratedClocks(), params)
|
||||
tk.SetClocks(time.NewCalibratedClocks(false), params)
|
||||
|
||||
creds := auth.NewRootCredentials(auth.NewRootUserNamespace())
|
||||
|
||||
|
||||
@@ -1936,6 +1936,15 @@ func (k *Kernel) MonotonicClock() ktime.SampledClock {
|
||||
return k.timekeeper.monotonicClock
|
||||
}
|
||||
|
||||
// MonotonicRawClock returns the system CLOCK_MONOTONIC_RAW clock. When it is
|
||||
// not enabled as a distinct clock this is the same as MonotonicClock.
|
||||
func (k *Kernel) MonotonicRawClock() ktime.SampledClock {
|
||||
if k.timekeeper.monotonicRawClock != nil {
|
||||
return k.timekeeper.monotonicRawClock
|
||||
}
|
||||
return k.timekeeper.monotonicClock
|
||||
}
|
||||
|
||||
// Syslog returns the syslog.
|
||||
func (k *Kernel) Syslog() *syslog {
|
||||
return &k.syslog
|
||||
|
||||
@@ -59,6 +59,12 @@ type Timekeeper struct {
|
||||
// monotonicClock is a ktime.Clock based on timekeeper's Monotonic.
|
||||
monotonicClock *timekeeperClock
|
||||
|
||||
// monotonicRawClock is a ktime.Clock based on timekeeper's MonotonicRaw.
|
||||
// It is non-nil only when the clock source tracks a distinct
|
||||
// CLOCK_MONOTONIC_RAW; otherwise CLOCK_MONOTONIC_RAW aliases
|
||||
// CLOCK_MONOTONIC. It is derived by SetClocks, anew on restore.
|
||||
monotonicRawClock *timekeeperClock `state:"nosave"`
|
||||
|
||||
// bootTime is the realtime when the system "booted". i.e., when
|
||||
// SetClocks was called in the initial (not restored) run.
|
||||
bootTime ktime.Time
|
||||
@@ -158,6 +164,14 @@ func (t *Timekeeper) SetClocks(c sentrytime.Clocks, params *VDSOParamPage) {
|
||||
|
||||
t.clocks = c
|
||||
|
||||
// Serve CLOCK_MONOTONIC_RAW as a distinct clock iff the clock source
|
||||
// tracks it (see NewCalibratedClocks). Deriving this from the source keeps
|
||||
// boot and restore coherent: a restored sandbox follows its current
|
||||
// configuration, not the checkpointed one.
|
||||
if c.MonotonicRawEnabled() {
|
||||
t.monotonicRawClock = &timekeeperClock{tk: t, c: sentrytime.MonotonicRaw}
|
||||
}
|
||||
|
||||
// Compute the offset of the monotonic clock from the base Clocks.
|
||||
//
|
||||
// In a fresh (not restored) sentry, monotonic time starts at zero.
|
||||
@@ -213,20 +227,30 @@ func (t *Timekeeper) update(parked bool) {
|
||||
// Call Update within a Write block to prevent the VDSO from using the old
|
||||
// params between Update and Write.
|
||||
if err := t.params.Write(func() vdsoParams {
|
||||
monotonicParams, monotonicOk, realtimeParams, realtimeOk := t.clocks.Update(parked)
|
||||
res := t.clocks.Update(parked)
|
||||
|
||||
var p vdsoParams
|
||||
if monotonicOk {
|
||||
if res.MonotonicOk {
|
||||
p.monotonicReady = 1
|
||||
p.monotonicBaseCycles = int64(monotonicParams.BaseCycles)
|
||||
p.monotonicBaseRef = int64(monotonicParams.BaseRef) + t.monotonicOffset
|
||||
p.monotonicFrequency = monotonicParams.Frequency
|
||||
p.monotonicBaseCycles = int64(res.Monotonic.BaseCycles)
|
||||
p.monotonicBaseRef = int64(res.Monotonic.BaseRef) + t.monotonicOffset
|
||||
p.monotonicFrequency = res.Monotonic.Frequency
|
||||
}
|
||||
if realtimeOk {
|
||||
if res.RealtimeOk {
|
||||
p.realtimeReady = 1
|
||||
p.realtimeBaseCycles = int64(realtimeParams.BaseCycles)
|
||||
p.realtimeBaseRef = int64(realtimeParams.BaseRef)
|
||||
p.realtimeFrequency = realtimeParams.Frequency
|
||||
p.realtimeBaseCycles = int64(res.Realtime.BaseCycles)
|
||||
p.realtimeBaseRef = int64(res.Realtime.BaseRef)
|
||||
p.realtimeFrequency = res.Realtime.Frequency
|
||||
}
|
||||
if t.monotonicRawClock == nil {
|
||||
p.monotonicRawAlias = 1
|
||||
} else if res.MonotonicRawOk {
|
||||
// Raw tracks absolute host CLOCK_MONOTONIC_RAW, so unlike
|
||||
// monotonic its base ref is published without monotonicOffset.
|
||||
p.monotonicRawReady = 1
|
||||
p.monotonicRawBaseCycles = int64(res.MonotonicRaw.BaseCycles)
|
||||
p.monotonicRawBaseRef = int64(res.MonotonicRaw.BaseRef)
|
||||
p.monotonicRawFrequency = res.MonotonicRaw.Frequency
|
||||
}
|
||||
return p
|
||||
}); err != nil {
|
||||
@@ -397,6 +421,9 @@ func (t *Timekeeper) GetTime(c sentrytime.ClockID) (int64, error) {
|
||||
}
|
||||
<-t.restored
|
||||
}
|
||||
if c == sentrytime.MonotonicRaw && t.monotonicRawClock == nil {
|
||||
c = sentrytime.Monotonic
|
||||
}
|
||||
|
||||
// update the calibration if needed and keep the timekeeper calibrated
|
||||
// during the read
|
||||
|
||||
@@ -28,13 +28,20 @@ import (
|
||||
// mockClocks is a sentrytime.Clocks that simply returns the times in the
|
||||
// struct.
|
||||
type mockClocks struct {
|
||||
monotonic int64
|
||||
realtime int64
|
||||
monotonic int64
|
||||
realtime int64
|
||||
monotonicRaw int64
|
||||
monotonicRawEnabled bool
|
||||
}
|
||||
|
||||
// Update implements sentrytime.Clocks.Update. It does nothing.
|
||||
func (*mockClocks) Update(parked bool) (monotonicParams sentrytime.Parameters, monotonicOk bool, realtimeParam sentrytime.Parameters, realtimeOk bool) {
|
||||
return
|
||||
func (*mockClocks) Update(parked bool) sentrytime.UpdateResult {
|
||||
return sentrytime.UpdateResult{}
|
||||
}
|
||||
|
||||
// MonotonicRawEnabled implements sentrytime.Clocks.MonotonicRawEnabled.
|
||||
func (c *mockClocks) MonotonicRawEnabled() bool {
|
||||
return c.monotonicRawEnabled
|
||||
}
|
||||
|
||||
// GetTime implements sentrytime.Clocks.GetTime.
|
||||
@@ -44,6 +51,11 @@ func (c *mockClocks) GetTime(id sentrytime.ClockID) (int64, error) {
|
||||
return c.monotonic, nil
|
||||
case sentrytime.Realtime:
|
||||
return c.realtime, nil
|
||||
case sentrytime.MonotonicRaw:
|
||||
if c.monotonicRawEnabled {
|
||||
return c.monotonicRaw, nil
|
||||
}
|
||||
return 0, linuxerr.EINVAL
|
||||
default:
|
||||
return 0, linuxerr.EINVAL
|
||||
}
|
||||
@@ -64,7 +76,7 @@ func stateTestClocklessTimekeeper(tb testing.TB) (*Timekeeper, *VDSOParamPage) {
|
||||
|
||||
func stateTestTimekeeper(tb testing.TB) *Timekeeper {
|
||||
t, params := stateTestClocklessTimekeeper(tb)
|
||||
t.SetClocks(sentrytime.NewCalibratedClocks(), params)
|
||||
t.SetClocks(sentrytime.NewCalibratedClocks(false), params)
|
||||
return t
|
||||
}
|
||||
|
||||
@@ -153,3 +165,44 @@ func TestTimekeeperMonotonicJumpBackwards(t *testing.T) {
|
||||
t.Errorf("GetTime got %d want 100000", now)
|
||||
}
|
||||
}
|
||||
|
||||
// TestTimekeeperMonotonicRawEnabled tests that when the clock source tracks a
|
||||
// distinct CLOCK_MONOTONIC_RAW, GetTime exposes it directly (absolute, not
|
||||
// starting at zero), while CLOCK_MONOTONIC is unaffected.
|
||||
func TestTimekeeperMonotonicRawEnabled(t *testing.T) {
|
||||
c := &mockClocks{
|
||||
monotonic: 100000,
|
||||
monotonicRaw: 999999,
|
||||
monotonicRawEnabled: true,
|
||||
}
|
||||
|
||||
tk, params := stateTestClocklessTimekeeper(t)
|
||||
tk.SetClocks(c, params)
|
||||
defer tk.Destroy()
|
||||
|
||||
// Monotonic is unaffected: still starts at zero.
|
||||
if now, err := tk.GetTime(sentrytime.Monotonic); err != nil || now != 0 {
|
||||
t.Errorf("GetTime(Monotonic) got (%d, %v) want (0, nil)", now, err)
|
||||
}
|
||||
// MonotonicRaw exposes the raw source's absolute value.
|
||||
if now, err := tk.GetTime(sentrytime.MonotonicRaw); err != nil || now != 999999 {
|
||||
t.Errorf("GetTime(MonotonicRaw) got (%d, %v) want (999999, nil)", now, err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestTimekeeperMonotonicRawDisabled tests that when the clock source does not
|
||||
// track CLOCK_MONOTONIC_RAW, it aliases CLOCK_MONOTONIC.
|
||||
func TestTimekeeperMonotonicRawDisabled(t *testing.T) {
|
||||
c := &mockClocks{
|
||||
monotonic: 100000,
|
||||
}
|
||||
|
||||
tk, params := stateTestClocklessTimekeeper(t)
|
||||
tk.SetClocks(c, params)
|
||||
defer tk.Destroy()
|
||||
|
||||
// MonotonicRaw aliases Monotonic: both start at zero.
|
||||
if now, err := tk.GetTime(sentrytime.MonotonicRaw); err != nil || now != 0 {
|
||||
t.Errorf("GetTime(MonotonicRaw) got (%d, %v) want (0, nil)", now, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,12 @@ type vdsoParams struct {
|
||||
realtimeBaseCycles int64
|
||||
realtimeBaseRef int64
|
||||
realtimeFrequency uint64
|
||||
|
||||
monotonicRawAlias uint64
|
||||
monotonicRawReady uint64
|
||||
monotonicRawBaseCycles int64
|
||||
monotonicRawBaseRef int64
|
||||
monotonicRawFrequency uint64
|
||||
}
|
||||
|
||||
// VDSOParamPage manages a VDSO parameter page.
|
||||
|
||||
@@ -123,9 +123,10 @@ func getClock(t *kernel.Task, clockID int32) (ktime.Clock, error) {
|
||||
switch clockID {
|
||||
case linux.CLOCK_REALTIME, linux.CLOCK_REALTIME_COARSE:
|
||||
return t.Kernel().RealtimeClock(), nil
|
||||
case linux.CLOCK_MONOTONIC_RAW:
|
||||
return t.Kernel().MonotonicRawClock(), nil
|
||||
case linux.CLOCK_MONOTONIC, linux.CLOCK_MONOTONIC_COARSE,
|
||||
linux.CLOCK_MONOTONIC_RAW, linux.CLOCK_BOOTTIME:
|
||||
// CLOCK_MONOTONIC approximates CLOCK_MONOTONIC_RAW.
|
||||
linux.CLOCK_BOOTTIME:
|
||||
// CLOCK_BOOTTIME is internally mapped to CLOCK_MONOTONIC, as:
|
||||
// - CLOCK_BOOTTIME should behave as CLOCK_MONOTONIC while also
|
||||
// including suspend time.
|
||||
|
||||
@@ -105,6 +105,12 @@ func TimerCreate(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uin
|
||||
sevp := args[1].Pointer()
|
||||
timerIDp := args[2].Pointer()
|
||||
|
||||
// Linux's clock_monotonic_raw k_clock only defines clock_getres and
|
||||
// clock_get_timespec, so timer_create(CLOCK_MONOTONIC_RAW) is rejected.
|
||||
if clockID == linux.CLOCK_MONOTONIC_RAW {
|
||||
return 0, nil, linuxerr.EOPNOTSUPP
|
||||
}
|
||||
|
||||
c, err := getClock(t, clockID)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
|
||||
@@ -234,22 +234,41 @@ type CalibratedClocks struct {
|
||||
|
||||
// realtime is the realtime equivalent of monotonic.
|
||||
realtime *CalibratedClock
|
||||
|
||||
// monotonicRaw tracks the host CLOCK_MONOTONIC_RAW clock. It is nil unless
|
||||
// enabled via NewCalibratedClocks.
|
||||
monotonicRaw *CalibratedClock
|
||||
}
|
||||
|
||||
// NewCalibratedClocks creates a CalibratedClocks.
|
||||
func NewCalibratedClocks() *CalibratedClocks {
|
||||
return &CalibratedClocks{
|
||||
// NewCalibratedClocks creates a CalibratedClocks. If monotonicRawEnabled is
|
||||
// true, host CLOCK_MONOTONIC_RAW is additionally tracked as a distinct clock
|
||||
// (addressable as MonotonicRaw).
|
||||
func NewCalibratedClocks(monotonicRawEnabled bool) *CalibratedClocks {
|
||||
c := &CalibratedClocks{
|
||||
monotonic: NewCalibratedClock(Monotonic),
|
||||
realtime: NewCalibratedClock(Realtime),
|
||||
}
|
||||
if monotonicRawEnabled {
|
||||
c.monotonicRaw = NewCalibratedClock(MonotonicRaw)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// Update implements Clocks.Update.
|
||||
func (c *CalibratedClocks) Update(parked bool) (Parameters, bool, Parameters, bool) {
|
||||
monotonicParams, monotonicOk := c.monotonic.Update(parked)
|
||||
realtimeParams, realtimeOk := c.realtime.Update(parked)
|
||||
func (c *CalibratedClocks) Update(parked bool) UpdateResult {
|
||||
var res UpdateResult
|
||||
res.Monotonic, res.MonotonicOk = c.monotonic.Update(parked)
|
||||
res.Realtime, res.RealtimeOk = c.realtime.Update(parked)
|
||||
if c.monotonicRaw != nil {
|
||||
res.MonotonicRaw, res.MonotonicRawOk = c.monotonicRaw.Update(parked)
|
||||
}
|
||||
|
||||
return monotonicParams, monotonicOk, realtimeParams, realtimeOk
|
||||
return res
|
||||
}
|
||||
|
||||
// MonotonicRawEnabled implements Clocks.MonotonicRawEnabled.
|
||||
func (c *CalibratedClocks) MonotonicRawEnabled() bool {
|
||||
return c.monotonicRaw != nil
|
||||
}
|
||||
|
||||
// GetTime implements Clocks.GetTime.
|
||||
@@ -259,6 +278,11 @@ func (c *CalibratedClocks) GetTime(id ClockID) (int64, error) {
|
||||
return c.monotonic.GetTime()
|
||||
case Realtime:
|
||||
return c.realtime.GetTime()
|
||||
case MonotonicRaw:
|
||||
if c.monotonicRaw != nil {
|
||||
return c.monotonicRaw.GetTime()
|
||||
}
|
||||
return 0, linuxerr.EINVAL
|
||||
default:
|
||||
return 0, linuxerr.EINVAL
|
||||
}
|
||||
|
||||
@@ -23,8 +23,9 @@ type ClockID int32
|
||||
|
||||
// These are the supported Linux clock identifiers.
|
||||
const (
|
||||
Realtime ClockID = iota
|
||||
Monotonic
|
||||
Realtime ClockID = 0
|
||||
Monotonic ClockID = 1
|
||||
MonotonicRaw ClockID = 4
|
||||
)
|
||||
|
||||
// String implements fmt.Stringer.String.
|
||||
@@ -34,6 +35,8 @@ func (c ClockID) String() string {
|
||||
return "Realtime"
|
||||
case Monotonic:
|
||||
return "Monotonic"
|
||||
case MonotonicRaw:
|
||||
return "MonotonicRaw"
|
||||
default:
|
||||
return strconv.Itoa(int(c))
|
||||
}
|
||||
|
||||
@@ -14,6 +14,21 @@
|
||||
|
||||
package time
|
||||
|
||||
// UpdateResult holds the timekeeping parameters produced by Clocks.Update.
|
||||
//
|
||||
// Each *Ok field reports whether the corresponding *Params were successfully
|
||||
// calibrated and may be published to the VDSO. MonotonicRaw is only populated
|
||||
// by clock sources that track a distinct CLOCK_MONOTONIC_RAW; otherwise
|
||||
// MonotonicRawOk is false.
|
||||
type UpdateResult struct {
|
||||
Monotonic Parameters
|
||||
MonotonicOk bool
|
||||
Realtime Parameters
|
||||
RealtimeOk bool
|
||||
MonotonicRaw Parameters
|
||||
MonotonicRawOk bool
|
||||
}
|
||||
|
||||
// Clocks represents a clock source that contains both a monotonic and realtime
|
||||
// clock.
|
||||
type Clocks interface {
|
||||
@@ -23,11 +38,18 @@ type Clocks interface {
|
||||
// Update should be called at approximately ApproxUpdateInterval.
|
||||
//
|
||||
// parked indicates that the clock was not read for at least ApproxUpdateInterval
|
||||
Update(parked bool) (monotonicParams Parameters, monotonicOk bool, realtimeParam Parameters, realtimeOk bool)
|
||||
Update(parked bool) UpdateResult
|
||||
|
||||
// GetTime returns the current time in nanoseconds for the given clock.
|
||||
//
|
||||
// Clocks implementations must support at least Monotonic and
|
||||
// Realtime.
|
||||
GetTime(c ClockID) (int64, error)
|
||||
|
||||
// MonotonicRawEnabled reports whether this clock source tracks a distinct
|
||||
// CLOCK_MONOTONIC_RAW, i.e. whether GetTime(MonotonicRaw) returns the
|
||||
// host's absolute CLOCK_MONOTONIC_RAW rather than aliasing Monotonic.
|
||||
//
|
||||
// It must return the same value for the lifetime of the Clocks.
|
||||
MonotonicRawEnabled() bool
|
||||
}
|
||||
|
||||
+18
-1
@@ -545,6 +545,23 @@ func getRootCredentials(spec *specs.Spec, conf *config.Config, userNs *auth.User
|
||||
return creds
|
||||
}
|
||||
|
||||
// shouldEnableClockMonotonicRaw reports whether CLOCK_MONOTONIC_RAW should be
|
||||
// exposed as a distinct clock tracking the host's CLOCK_MONOTONIC_RAW, rather
|
||||
// than aliasing CLOCK_MONOTONIC as it does by default.
|
||||
//
|
||||
// This exists for GPU profiling: profilers such as Nsight Systems/CUPTI anchor
|
||||
// the GPU timeline in the host's CLOCK_MONOTONIC_RAW domain, which drifts from
|
||||
// CLOCK_MONOTONIC by NTP frequency adjustment. When nvproxy grants
|
||||
// CapProfiling, the sandbox must therefore serve a CLOCK_MONOTONIC_RAW in that
|
||||
// same (absolute, unadjusted) domain. It is enabled only in that case.
|
||||
func shouldEnableClockMonotonicRaw(spec *specs.Spec, conf *config.Config) bool {
|
||||
if !specutils.NVProxyEnabled(spec, conf) {
|
||||
return false
|
||||
}
|
||||
caps, err := specutils.NVProxyDriverCapsAllowed(conf)
|
||||
return err == nil && caps&nvconf.CapProfiling != 0
|
||||
}
|
||||
|
||||
// New initializes a new kernel loader configured by spec.
|
||||
// New also handles setting up a kernel for restoring a container.
|
||||
func New(args Args) (*Loader, error) {
|
||||
@@ -733,7 +750,7 @@ func New(args Args) (*Loader, error) {
|
||||
// Create timekeeper.
|
||||
tk := kernel.NewTimekeeper()
|
||||
params := kernel.NewVDSOParamPage(l.k.MemoryFile(), vdso.ParamPage.FileRange())
|
||||
tk.SetClocks(time.NewCalibratedClocks(), params)
|
||||
tk.SetClocks(time.NewCalibratedClocks(shouldEnableClockMonotonicRaw(args.Spec, args.Conf)), params)
|
||||
args.StartupTimer.Reached("timekeeper configured")
|
||||
|
||||
if err := enableStrace(args.Conf); err != nil {
|
||||
|
||||
@@ -479,15 +479,16 @@ func (r *restorer) restore(l *Loader) error {
|
||||
}
|
||||
|
||||
// Load the state.
|
||||
clocks := time.NewCalibratedClocks(shouldEnableClockMonotonicRaw(l.root.spec, l.root.conf))
|
||||
r.timer.Reached("loading kernel")
|
||||
if r.extractRootFsMode {
|
||||
if err := l.k.ExtractRootfsUpperLayer(ctx, r.stateFile, r.asyncMFLoader, nil, time.NewCalibratedClocks(), r.rootFsOutputTar); err != nil {
|
||||
if err := l.k.ExtractRootfsUpperLayer(ctx, r.stateFile, r.asyncMFLoader, nil, clocks, r.rootFsOutputTar); err != nil {
|
||||
return fmt.Errorf("failed to extract rootfs upper layer: %w", err)
|
||||
}
|
||||
r.timer.Reached("rootfs upper layer extracted")
|
||||
return nil
|
||||
}
|
||||
if err := l.k.LoadFrom(ctx, r.stateFile, r.asyncMFLoader, nil, l, time.NewCalibratedClocks(), &vfs.CompleteRestoreOptions{}, r.timer.Fork("kernel load")); err != nil {
|
||||
if err := l.k.LoadFrom(ctx, r.stateFile, r.asyncMFLoader, nil, l, clocks, &vfs.CompleteRestoreOptions{}, r.timer.Fork("kernel load")); err != nil {
|
||||
return fmt.Errorf("failed to load kernel: %w", err)
|
||||
}
|
||||
r.timer.Reached("kernel loaded")
|
||||
|
||||
+4
-2
@@ -35,10 +35,12 @@ int __common_clock_gettime(clockid_t clock, struct timespec* ts) {
|
||||
ret = ClockRealtime(ts);
|
||||
break;
|
||||
|
||||
case CLOCK_MONOTONIC_RAW:
|
||||
ret = ClockMonotonicRaw(ts);
|
||||
break;
|
||||
|
||||
case CLOCK_BOOTTIME:
|
||||
// Fallthrough, CLOCK_BOOTTIME is an alias for CLOCK_MONOTONIC
|
||||
case CLOCK_MONOTONIC_RAW:
|
||||
// Fallthrough, CLOCK_MONOTONIC_RAW is an alias for CLOCK_MONOTONIC
|
||||
case CLOCK_MONOTONIC_COARSE:
|
||||
// Fallthrough, CLOCK_MONOTONIC_COARSE is an alias for CLOCK_MONOTONIC
|
||||
case CLOCK_MONOTONIC:
|
||||
|
||||
+60
-8
@@ -41,6 +41,12 @@ struct params {
|
||||
int64_t realtime_base_cycles;
|
||||
int64_t realtime_base_ref;
|
||||
uint64_t realtime_frequency;
|
||||
|
||||
uint64_t monotonic_raw_alias;
|
||||
uint64_t monotonic_raw_ready;
|
||||
int64_t monotonic_raw_base_cycles;
|
||||
int64_t monotonic_raw_base_ref;
|
||||
uint64_t monotonic_raw_frequency;
|
||||
};
|
||||
|
||||
// Returns a pointer to the global parameter page.
|
||||
@@ -91,6 +97,14 @@ inline uint64_t cycles_to_ns(uint64_t frequency, uint64_t cycles) {
|
||||
return ((unsigned __int128)cycles * mult) >> 32;
|
||||
}
|
||||
|
||||
// compute_time projects a calibrated clock's base to now_cycles.
|
||||
inline int64_t compute_time(int64_t base_ref, int64_t base_cycles,
|
||||
uint64_t frequency, int64_t now_cycles) {
|
||||
int64_t delta_cycles =
|
||||
(now_cycles < base_cycles) ? 0 : now_cycles - base_cycles;
|
||||
return base_ref + cycles_to_ns(frequency, delta_cycles);
|
||||
}
|
||||
|
||||
// ClockRealtime() is the VDSO implementation of clock_gettime(CLOCK_REALTIME).
|
||||
int ClockRealtime(struct timespec* ts) {
|
||||
struct params* params = get_params();
|
||||
@@ -116,10 +130,8 @@ int ClockRealtime(struct timespec* ts) {
|
||||
return sys_clock_gettime(CLOCK_REALTIME, ts);
|
||||
}
|
||||
|
||||
int64_t delta_cycles =
|
||||
(now_cycles < base_cycles) ? 0 : now_cycles - base_cycles;
|
||||
int64_t now_ns = base_ref + cycles_to_ns(frequency, delta_cycles);
|
||||
*ts = ns_to_timespec(now_ns);
|
||||
*ts = ns_to_timespec(
|
||||
compute_time(base_ref, base_cycles, frequency, now_cycles));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -149,10 +161,50 @@ int ClockMonotonic(struct timespec* ts) {
|
||||
return sys_clock_gettime(CLOCK_MONOTONIC, ts);
|
||||
}
|
||||
|
||||
int64_t delta_cycles =
|
||||
(now_cycles < base_cycles) ? 0 : now_cycles - base_cycles;
|
||||
int64_t now_ns = base_ref + cycles_to_ns(frequency, delta_cycles);
|
||||
*ts = ns_to_timespec(now_ns);
|
||||
*ts = ns_to_timespec(
|
||||
compute_time(base_ref, base_cycles, frequency, now_cycles));
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ClockMonotonicRaw() is the VDSO implementation of
|
||||
// clock_gettime(CLOCK_MONOTONIC_RAW).
|
||||
//
|
||||
// When aliasing, it serves CLOCK_MONOTONIC in the VDSO. Otherwise, if the raw
|
||||
// params are not yet calibrated, it falls back to a syscall for the brief
|
||||
// startup/restore window; otherwise it is computed in the VDSO from the raw
|
||||
// params.
|
||||
int ClockMonotonicRaw(struct timespec* ts) {
|
||||
struct params* params = get_params();
|
||||
uint64_t seq;
|
||||
uint64_t alias;
|
||||
uint64_t ready;
|
||||
int64_t base_ref;
|
||||
int64_t base_cycles;
|
||||
uint64_t frequency;
|
||||
int64_t now_cycles;
|
||||
|
||||
do {
|
||||
seq = read_seqcount_begin(¶ms->seq_count);
|
||||
alias = params->monotonic_raw_alias;
|
||||
ready = params->monotonic_raw_ready;
|
||||
base_ref = params->monotonic_raw_base_ref;
|
||||
base_cycles = params->monotonic_raw_base_cycles;
|
||||
frequency = params->monotonic_raw_frequency;
|
||||
now_cycles = cycle_clock();
|
||||
} while (read_seqcount_retry(¶ms->seq_count, seq));
|
||||
|
||||
if (alias) {
|
||||
// CLOCK_MONOTONIC_RAW aliases CLOCK_MONOTONIC, served in the VDSO.
|
||||
return ClockMonotonic(ts);
|
||||
}
|
||||
if (!ready) {
|
||||
// The sandbox kernel ensures that we won't compute a time later than this
|
||||
// once the params are ready.
|
||||
return sys_clock_gettime(CLOCK_MONOTONIC_RAW, ts);
|
||||
}
|
||||
|
||||
*ts = ns_to_timespec(
|
||||
compute_time(base_ref, base_cycles, frequency, now_cycles));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ namespace vdso {
|
||||
|
||||
int ClockRealtime(struct timespec* ts);
|
||||
int ClockMonotonic(struct timespec* ts);
|
||||
int ClockMonotonicRaw(struct timespec* ts);
|
||||
|
||||
} // namespace vdso
|
||||
|
||||
|
||||
Reference in New Issue
Block a user