summaryrefslogtreecommitdiffhomepage
path: root/pkg/sentry/time
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/sentry/time')
-rw-r--r--pkg/sentry/time/BUILD54
-rw-r--r--pkg/sentry/time/LICENSE27
-rw-r--r--pkg/sentry/time/calibrated_clock_test.go186
-rw-r--r--pkg/sentry/time/parameters_test.go501
-rw-r--r--pkg/sentry/time/sampler_test.go183
-rw-r--r--pkg/sentry/time/seqatomic_parameters_unsafe.go38
-rw-r--r--pkg/sentry/time/time_amd64_state_autogen.go5
-rw-r--r--pkg/sentry/time/time_arm64_state_autogen.go5
-rw-r--r--pkg/sentry/time/time_state_autogen.go3
-rw-r--r--pkg/sentry/time/time_unsafe_state_autogen.go3
10 files changed, 54 insertions, 951 deletions
diff --git a/pkg/sentry/time/BUILD b/pkg/sentry/time/BUILD
deleted file mode 100644
index 202486a1e..000000000
--- a/pkg/sentry/time/BUILD
+++ /dev/null
@@ -1,54 +0,0 @@
-load("//tools:defs.bzl", "go_library", "go_test")
-load("//tools/go_generics:defs.bzl", "go_template_instance")
-
-package(licenses = ["notice"])
-
-go_template_instance(
- name = "seqatomic_parameters",
- out = "seqatomic_parameters_unsafe.go",
- package = "time",
- suffix = "Parameters",
- template = "//pkg/sync/seqatomic:generic_seqatomic",
- types = {
- "Value": "Parameters",
- },
-)
-
-go_library(
- name = "time",
- srcs = [
- "arith_arm64.go",
- "calibrated_clock.go",
- "clock_id.go",
- "clocks.go",
- "muldiv_amd64.s",
- "muldiv_arm64.s",
- "parameters.go",
- "sampler.go",
- "sampler_amd64.go",
- "sampler_arm64.go",
- "sampler_unsafe.go",
- "seqatomic_parameters_unsafe.go",
- "tsc_amd64.s",
- "tsc_arm64.s",
- ],
- visibility = ["//:sandbox"],
- deps = [
- "//pkg/gohacks",
- "//pkg/log",
- "//pkg/metric",
- "//pkg/sync",
- "//pkg/syserror",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
-
-go_test(
- name = "time_test",
- srcs = [
- "calibrated_clock_test.go",
- "parameters_test.go",
- "sampler_test.go",
- ],
- library = ":time",
-)
diff --git a/pkg/sentry/time/LICENSE b/pkg/sentry/time/LICENSE
deleted file mode 100644
index 6a66aea5e..000000000
--- a/pkg/sentry/time/LICENSE
+++ /dev/null
@@ -1,27 +0,0 @@
-Copyright (c) 2009 The Go Authors. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
- * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
- * Neither the name of Google Inc. nor the names of its
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/pkg/sentry/time/calibrated_clock_test.go b/pkg/sentry/time/calibrated_clock_test.go
deleted file mode 100644
index d6622bfe2..000000000
--- a/pkg/sentry/time/calibrated_clock_test.go
+++ /dev/null
@@ -1,186 +0,0 @@
-// 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 time
-
-import (
- "testing"
- "time"
-)
-
-// newTestCalibratedClock returns a CalibratedClock that collects samples from
-// the given sample list and cycle counts from the given cycle list.
-func newTestCalibratedClock(samples []sample, cycles []TSCValue) *CalibratedClock {
- return &CalibratedClock{
- ref: newTestSampler(samples, cycles),
- }
-}
-
-func TestConstantFrequency(t *testing.T) {
- // Perfectly constant frequency.
- samples := []sample{
- {before: 100000, after: 100000 + defaultOverheadCycles, ref: 100},
- {before: 200000, after: 200000 + defaultOverheadCycles, ref: 200},
- {before: 300000, after: 300000 + defaultOverheadCycles, ref: 300},
- {before: 400000, after: 400000 + defaultOverheadCycles, ref: 400},
- {before: 500000, after: 500000 + defaultOverheadCycles, ref: 500},
- {before: 600000, after: 600000 + defaultOverheadCycles, ref: 600},
- {before: 700000, after: 700000 + defaultOverheadCycles, ref: 700},
- }
-
- c := newTestCalibratedClock(samples, nil)
-
- // Update from all samples.
- for range samples {
- c.Update()
- }
-
- c.mu.RLock()
- if !c.ready {
- c.mu.RUnlock()
- t.Fatalf("clock not ready")
- }
- // A bit after the last sample.
- now, ok := c.params.ComputeTime(750000)
- c.mu.RUnlock()
- if !ok {
- t.Fatalf("ComputeTime ok got %v want true", ok)
- }
-
- t.Logf("now: %v", now)
-
- // Time should be between the current sample and where we'd expect the
- // next sample.
- if now < 700 || now > 800 {
- t.Errorf("now got %v want > 700 && < 800", now)
- }
-}
-
-func TestErrorCorrection(t *testing.T) {
- testCases := []struct {
- name string
- samples [5]sample
- projectedTimeStart int64
- projectedTimeEnd int64
- }{
- // Initial calibration should be ~1MHz for each of these, and
- // the reference clock changes in samples[2].
- {
- name: "slow-down",
- samples: [5]sample{
- {before: 1000000, after: 1000001, ref: ReferenceNS(1 * ApproxUpdateInterval.Nanoseconds())},
- {before: 2000000, after: 2000001, ref: ReferenceNS(2 * ApproxUpdateInterval.Nanoseconds())},
- // Reference clock has slowed down, causing 100ms of error.
- {before: 3010000, after: 3010001, ref: ReferenceNS(3 * ApproxUpdateInterval.Nanoseconds())},
- {before: 4020000, after: 4020001, ref: ReferenceNS(4 * ApproxUpdateInterval.Nanoseconds())},
- {before: 5030000, after: 5030001, ref: ReferenceNS(5 * ApproxUpdateInterval.Nanoseconds())},
- },
- projectedTimeStart: 3005 * time.Millisecond.Nanoseconds(),
- projectedTimeEnd: 3015 * time.Millisecond.Nanoseconds(),
- },
- {
- name: "speed-up",
- samples: [5]sample{
- {before: 1000000, after: 1000001, ref: ReferenceNS(1 * ApproxUpdateInterval.Nanoseconds())},
- {before: 2000000, after: 2000001, ref: ReferenceNS(2 * ApproxUpdateInterval.Nanoseconds())},
- // Reference clock has sped up, causing 100ms of error.
- {before: 2990000, after: 2990001, ref: ReferenceNS(3 * ApproxUpdateInterval.Nanoseconds())},
- {before: 3980000, after: 3980001, ref: ReferenceNS(4 * ApproxUpdateInterval.Nanoseconds())},
- {before: 4970000, after: 4970001, ref: ReferenceNS(5 * ApproxUpdateInterval.Nanoseconds())},
- },
- projectedTimeStart: 2985 * time.Millisecond.Nanoseconds(),
- projectedTimeEnd: 2995 * time.Millisecond.Nanoseconds(),
- },
- }
- for _, tc := range testCases {
- t.Run(tc.name, func(t *testing.T) {
- c := newTestCalibratedClock(tc.samples[:], nil)
-
- // Initial calibration takes two updates.
- _, ok := c.Update()
- if ok {
- t.Fatalf("Update ready too early")
- }
-
- params, ok := c.Update()
- if !ok {
- t.Fatalf("Update not ready")
- }
-
- // Initial calibration is ~1MHz.
- hz := params.Frequency
- if hz < 990000 || hz > 1010000 {
- t.Fatalf("Frequency got %v want > 990kHz && < 1010kHz", hz)
- }
-
- // Project time at the next update. Given the 1MHz
- // calibration, it is expected to be ~3.1s/2.9s, not
- // the actual 3s.
- //
- // N.B. the next update time is the "after" time above.
- projected, ok := params.ComputeTime(tc.samples[2].after)
- if !ok {
- t.Fatalf("ComputeTime ok got %v want true", ok)
- }
- if projected < tc.projectedTimeStart || projected > tc.projectedTimeEnd {
- t.Fatalf("ComputeTime(%v) got %v want > %v && < %v", tc.samples[2].after, projected, tc.projectedTimeStart, tc.projectedTimeEnd)
- }
-
- // Update again to see the changed reference clock.
- params, ok = c.Update()
- if !ok {
- t.Fatalf("Update not ready")
- }
-
- // We now know that TSC = tc.samples[2].after -> 3s,
- // but with the previous params indicated that TSC
- // tc.samples[2].after -> 3.5s/2.5s. We can't allow the
- // clock to go backwards, and having the clock jump
- // forwards is undesirable. There should be a smooth
- // transition that corrects the clock error over time.
- // Check that the clock is continuous at TSC =
- // tc.samples[2].after.
- newProjected, ok := params.ComputeTime(tc.samples[2].after)
- if !ok {
- t.Fatalf("ComputeTime ok got %v want true", ok)
- }
- if newProjected != projected {
- t.Errorf("Discontinuous time; ComputeTime(%v) got %v want %v", tc.samples[2].after, newProjected, projected)
- }
-
- // As the reference clock stablizes, ensure that the clock error
- // decreases.
- initialErr := c.errorNS
- t.Logf("initial error: %v ns", initialErr)
-
- _, ok = c.Update()
- if !ok {
- t.Fatalf("Update not ready")
- }
- if c.errorNS.Magnitude() > initialErr.Magnitude() {
- t.Errorf("errorNS increased, got %v want |%v| <= |%v|", c.errorNS, c.errorNS, initialErr)
- }
-
- _, ok = c.Update()
- if !ok {
- t.Fatalf("Update not ready")
- }
- if c.errorNS.Magnitude() > initialErr.Magnitude() {
- t.Errorf("errorNS increased, got %v want |%v| <= |%v|", c.errorNS, c.errorNS, initialErr)
- }
-
- t.Logf("final error: %v ns", c.errorNS)
- })
- }
-}
diff --git a/pkg/sentry/time/parameters_test.go b/pkg/sentry/time/parameters_test.go
deleted file mode 100644
index 0ce1257f6..000000000
--- a/pkg/sentry/time/parameters_test.go
+++ /dev/null
@@ -1,501 +0,0 @@
-// 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 time
-
-import (
- "math"
- "testing"
- "time"
-)
-
-func TestParametersComputeTime(t *testing.T) {
- testCases := []struct {
- name string
- params Parameters
- now TSCValue
- want int64
- }{
- {
- // Now is the same as the base cycles.
- name: "base-cycles",
- params: Parameters{
- BaseCycles: 10000,
- BaseRef: ReferenceNS(5000 * time.Millisecond.Nanoseconds()),
- Frequency: 10000,
- },
- now: 10000,
- want: 5000 * time.Millisecond.Nanoseconds(),
- },
- {
- // Now is the behind the base cycles. Time is frozen.
- name: "backwards",
- params: Parameters{
- BaseCycles: 10000,
- BaseRef: ReferenceNS(5000 * time.Millisecond.Nanoseconds()),
- Frequency: 10000,
- },
- now: 9000,
- want: 5000 * time.Millisecond.Nanoseconds(),
- },
- {
- // Now is ahead of the base cycles.
- name: "ahead",
- params: Parameters{
- BaseCycles: 10000,
- BaseRef: ReferenceNS(5000 * time.Millisecond.Nanoseconds()),
- Frequency: 10000,
- },
- now: 15000,
- want: 5500 * time.Millisecond.Nanoseconds(),
- },
- }
- for _, tc := range testCases {
- t.Run(tc.name, func(t *testing.T) {
- got, ok := tc.params.ComputeTime(tc.now)
- if !ok {
- t.Errorf("ComputeTime ok got %v want true", got)
- }
- if got != tc.want {
- t.Errorf("ComputeTime got %+v want %+v", got, tc.want)
- }
- })
- }
-}
-
-func TestParametersErrorAdjust(t *testing.T) {
- testCases := []struct {
- name string
- oldParams Parameters
- now TSCValue
- newParams Parameters
- want Parameters
- errorNS ReferenceNS
- wantErr bool
- }{
- {
- // newParams are perfectly continuous with oldParams
- // and don't need adjustment.
- name: "continuous",
- oldParams: Parameters{
- BaseCycles: 0,
- BaseRef: 0,
- Frequency: 10000,
- },
- now: 50000,
- newParams: Parameters{
- BaseCycles: 50000,
- BaseRef: ReferenceNS(5000 * time.Millisecond.Nanoseconds()),
- Frequency: 10000,
- },
- want: Parameters{
- BaseCycles: 50000,
- BaseRef: ReferenceNS(5000 * time.Millisecond.Nanoseconds()),
- Frequency: 10000,
- },
- },
- {
- // Same as "continuous", but with now ahead of
- // newParams.BaseCycles. The result is the same as
- // there is no error to correct.
- name: "continuous-nowdiff",
- oldParams: Parameters{
- BaseCycles: 0,
- BaseRef: 0,
- Frequency: 10000,
- },
- now: 60000,
- newParams: Parameters{
- BaseCycles: 50000,
- BaseRef: ReferenceNS(5000 * time.Millisecond.Nanoseconds()),
- Frequency: 10000,
- },
- want: Parameters{
- BaseCycles: 50000,
- BaseRef: ReferenceNS(5000 * time.Millisecond.Nanoseconds()),
- Frequency: 10000,
- },
- },
- {
- // errorAdjust bails out if the TSC goes backwards.
- name: "tsc-backwards",
- oldParams: Parameters{
- BaseCycles: 10000,
- BaseRef: ReferenceNS(1000 * time.Millisecond.Nanoseconds()),
- Frequency: 10000,
- },
- now: 9000,
- newParams: Parameters{
- BaseCycles: 9000,
- BaseRef: ReferenceNS(1100 * time.Millisecond.Nanoseconds()),
- Frequency: 10000,
- },
- wantErr: true,
- },
- {
- // errorAdjust bails out if new params are from after now.
- name: "params-after-now",
- oldParams: Parameters{
- BaseCycles: 10000,
- BaseRef: ReferenceNS(1000 * time.Millisecond.Nanoseconds()),
- Frequency: 10000,
- },
- now: 11000,
- newParams: Parameters{
- BaseCycles: 12000,
- BaseRef: ReferenceNS(1200 * time.Millisecond.Nanoseconds()),
- Frequency: 10000,
- },
- wantErr: true,
- },
- {
- // Host clock sped up.
- name: "speed-up",
- oldParams: Parameters{
- BaseCycles: 0,
- BaseRef: 0,
- Frequency: 10000,
- },
- now: 45000,
- // Host frequency changed to 9000 immediately after
- // oldParams was returned.
- newParams: Parameters{
- BaseCycles: 45000,
- // From oldParams, we think ref = 4.5s at cycles = 45000.
- BaseRef: ReferenceNS(5000 * time.Millisecond.Nanoseconds()),
- Frequency: 9000,
- },
- want: Parameters{
- BaseCycles: 45000,
- BaseRef: ReferenceNS(4500 * time.Millisecond.Nanoseconds()),
- // We must decrease the new frequency by 50% to
- // correct 0.5s of error in 1s
- // (ApproxUpdateInterval).
- Frequency: 4500,
- },
- errorNS: ReferenceNS(-500 * time.Millisecond.Nanoseconds()),
- },
- {
- // Host clock sped up, with now ahead of newParams.
- name: "speed-up-nowdiff",
- oldParams: Parameters{
- BaseCycles: 0,
- BaseRef: 0,
- Frequency: 10000,
- },
- now: 50000,
- // Host frequency changed to 9000 immediately after
- // oldParams was returned.
- newParams: Parameters{
- BaseCycles: 45000,
- BaseRef: ReferenceNS(5000 * time.Millisecond.Nanoseconds()),
- Frequency: 9000,
- },
- // nextRef = 6000ms
- // nextCycles = 9000 * (6000ms - 5000ms) + 45000
- // nextCycles = 9000 * (1s) + 45000
- // nextCycles = 54000
- // f = (54000 - 50000) / 1s = 4000
- //
- // ref = 5000ms - (50000 - 45000) / 4000
- // ref = 3.75s
- want: Parameters{
- BaseCycles: 45000,
- BaseRef: ReferenceNS(3750 * time.Millisecond.Nanoseconds()),
- Frequency: 4000,
- },
- // oldNow = 50000 * 10000 = 5s
- // newNow = (50000 - 45000) / 9000 + 5s = 5.555s
- errorNS: ReferenceNS((5000*time.Millisecond - 5555555555).Nanoseconds()),
- },
- {
- // Host clock sped up. The new parameters are so far
- // ahead that the next update time already passed.
- name: "speed-up-uncorrectable-baseref",
- oldParams: Parameters{
- BaseCycles: 0,
- BaseRef: 0,
- Frequency: 10000,
- },
- now: 50000,
- // Host frequency changed to 5000 immediately after
- // oldParams was returned.
- newParams: Parameters{
- BaseCycles: 45000,
- BaseRef: ReferenceNS(9000 * time.Millisecond.Nanoseconds()),
- Frequency: 5000,
- },
- // The next update should be at 10s, but newParams
- // already passed 6s. Thus it is impossible to correct
- // the clock by then.
- wantErr: true,
- },
- {
- // Host clock sped up. The new parameters are moving so
- // fast that the next update should be before now.
- name: "speed-up-uncorrectable-frequency",
- oldParams: Parameters{
- BaseCycles: 0,
- BaseRef: 0,
- Frequency: 10000,
- },
- now: 55000,
- // Host frequency changed to 7500 immediately after
- // oldParams was returned.
- newParams: Parameters{
- BaseCycles: 45000,
- BaseRef: ReferenceNS(6000 * time.Millisecond.Nanoseconds()),
- Frequency: 7500,
- },
- // The next update should be at 6.5s, but newParams are
- // so far ahead and fast that they reach 6.5s at cycle
- // 48750, which before now! Thus it is impossible to
- // correct the clock by then.
- wantErr: true,
- },
- {
- // Host clock slowed down.
- name: "slow-down",
- oldParams: Parameters{
- BaseCycles: 0,
- BaseRef: 0,
- Frequency: 10000,
- },
- now: 55000,
- // Host frequency changed to 11000 immediately after
- // oldParams was returned.
- newParams: Parameters{
- BaseCycles: 55000,
- // From oldParams, we think ref = 5.5s at cycles = 55000.
- BaseRef: ReferenceNS(5000 * time.Millisecond.Nanoseconds()),
- Frequency: 11000,
- },
- want: Parameters{
- BaseCycles: 55000,
- BaseRef: ReferenceNS(5500 * time.Millisecond.Nanoseconds()),
- // We must increase the new frequency by 50% to
- // correct 0.5s of error in 1s
- // (ApproxUpdateInterval).
- Frequency: 16500,
- },
- errorNS: ReferenceNS(500 * time.Millisecond.Nanoseconds()),
- },
- {
- // Host clock slowed down, with now ahead of newParams.
- name: "slow-down-nowdiff",
- oldParams: Parameters{
- BaseCycles: 0,
- BaseRef: 0,
- Frequency: 10000,
- },
- now: 60000,
- // Host frequency changed to 11000 immediately after
- // oldParams was returned.
- newParams: Parameters{
- BaseCycles: 55000,
- BaseRef: ReferenceNS(5000 * time.Millisecond.Nanoseconds()),
- Frequency: 11000,
- },
- // nextRef = 7000ms
- // nextCycles = 11000 * (7000ms - 5000ms) + 55000
- // nextCycles = 11000 * (2000ms) + 55000
- // nextCycles = 77000
- // f = (77000 - 60000) / 1s = 17000
- //
- // ref = 6000ms - (60000 - 55000) / 17000
- // ref = 5705882353ns
- want: Parameters{
- BaseCycles: 55000,
- BaseRef: ReferenceNS(5705882353),
- Frequency: 17000,
- },
- // oldNow = 60000 * 10000 = 6s
- // newNow = (60000 - 55000) / 11000 + 5s = 5.4545s
- errorNS: ReferenceNS((6*time.Second - 5454545454).Nanoseconds()),
- },
- {
- // Host time went backwards.
- name: "time-backwards",
- oldParams: Parameters{
- BaseCycles: 50000,
- BaseRef: ReferenceNS(5000 * time.Millisecond.Nanoseconds()),
- Frequency: 10000,
- },
- now: 60000,
- newParams: Parameters{
- BaseCycles: 60000,
- // From oldParams, we think ref = 6s at cycles = 60000.
- BaseRef: ReferenceNS(4000 * time.Millisecond.Nanoseconds()),
- Frequency: 10000,
- },
- want: Parameters{
- BaseCycles: 60000,
- BaseRef: ReferenceNS(6000 * time.Millisecond.Nanoseconds()),
- // We must increase the frequency by 200% to
- // correct 2s of error in 1s
- // (ApproxUpdateInterval).
- Frequency: 30000,
- },
- errorNS: ReferenceNS(2000 * time.Millisecond.Nanoseconds()),
- },
- {
- // Host time went backwards, with now ahead of newParams.
- name: "time-backwards-nowdiff",
- oldParams: Parameters{
- BaseCycles: 50000,
- BaseRef: ReferenceNS(5000 * time.Millisecond.Nanoseconds()),
- Frequency: 10000,
- },
- now: 65000,
- // nextRef = 7500ms
- // nextCycles = 10000 * (7500ms - 4000ms) + 60000
- // nextCycles = 10000 * (3500ms) + 60000
- // nextCycles = 95000
- // f = (95000 - 65000) / 1s = 30000
- //
- // ref = 6500ms - (65000 - 60000) / 30000
- // ref = 6333333333ns
- newParams: Parameters{
- BaseCycles: 60000,
- BaseRef: ReferenceNS(4000 * time.Millisecond.Nanoseconds()),
- Frequency: 10000,
- },
- want: Parameters{
- BaseCycles: 60000,
- BaseRef: ReferenceNS(6333333334),
- Frequency: 30000,
- },
- // oldNow = 65000 * 10000 = 6.5s
- // newNow = (65000 - 60000) / 10000 + 4s = 4.5s
- errorNS: ReferenceNS(2000 * time.Millisecond.Nanoseconds()),
- },
- }
- for _, tc := range testCases {
- t.Run(tc.name, func(t *testing.T) {
- got, errorNS, err := errorAdjust(tc.oldParams, tc.newParams, tc.now)
- if err != nil && !tc.wantErr {
- t.Errorf("err got %v want nil", err)
- } else if err == nil && tc.wantErr {
- t.Errorf("err got nil want non-nil")
- }
-
- if got != tc.want {
- t.Errorf("Parameters got %+v want %+v", got, tc.want)
- }
- if errorNS != tc.errorNS {
- t.Errorf("errorNS got %v want %v", errorNS, tc.errorNS)
- }
- })
- }
-}
-
-func testMuldiv(t *testing.T, v uint64) {
- for i := uint64(1); i <= 1000000; i++ {
- mult := uint64(1000000000)
- div := i * mult
- res, ok := muldiv64(v, mult, div)
- if !ok {
- t.Errorf("Result of %v * %v / %v ok got false want true", v, mult, div)
- }
- if want := v / i; res != want {
- t.Errorf("Bad result of %v * %v / %v: got %v, want %v", v, mult, div, res, want)
- }
- }
-}
-
-func TestMulDiv(t *testing.T) {
- testMuldiv(t, math.MaxUint64)
- for i := int64(-10); i <= 10; i++ {
- testMuldiv(t, uint64(i))
- }
-}
-
-func TestMulDivZero(t *testing.T) {
- if r, ok := muldiv64(2, 4, 0); ok {
- t.Errorf("muldiv64(2, 4, 0) got %d, ok want !ok", r)
- }
-
- if r, ok := muldiv64(0, 0, 0); ok {
- t.Errorf("muldiv64(0, 0, 0) got %d, ok want !ok", r)
- }
-}
-
-func TestMulDivOverflow(t *testing.T) {
- testCases := []struct {
- name string
- val uint64
- mult uint64
- div uint64
- ok bool
- ret uint64
- }{
- {
- name: "2^62",
- val: 1 << 63,
- mult: 4,
- div: 8,
- ok: true,
- ret: 1 << 62,
- },
- {
- name: "2^64-1",
- val: 0xffffffffffffffff,
- mult: 1,
- div: 1,
- ok: true,
- ret: 0xffffffffffffffff,
- },
- {
- name: "2^64",
- val: 1 << 63,
- mult: 4,
- div: 2,
- ok: false,
- },
- {
- name: "2^125",
- val: 1 << 63,
- mult: 1 << 63,
- div: 2,
- ok: false,
- },
- }
-
- for _, tc := range testCases {
- t.Run(tc.name, func(t *testing.T) {
- r, ok := muldiv64(tc.val, tc.mult, tc.div)
- if ok != tc.ok {
- t.Errorf("ok got %v want %v", ok, tc.ok)
- }
- if tc.ok && r != tc.ret {
- t.Errorf("ret got %v want %v", r, tc.ret)
- }
- })
- }
-}
-
-func BenchmarkMuldiv64(b *testing.B) {
- var v uint64 = math.MaxUint64
- for i := uint64(1); i <= 1000000; i++ {
- mult := uint64(1000000000)
- div := i * mult
- res, ok := muldiv64(v, mult, div)
- if !ok {
- b.Errorf("Result of %v * %v / %v ok got false want true", v, mult, div)
- }
- if want := v / i; res != want {
- b.Errorf("Bad result of %v * %v / %v: got %v, want %v", v, mult, div, res, want)
- }
- }
-}
diff --git a/pkg/sentry/time/sampler_test.go b/pkg/sentry/time/sampler_test.go
deleted file mode 100644
index 3e70a1134..000000000
--- a/pkg/sentry/time/sampler_test.go
+++ /dev/null
@@ -1,183 +0,0 @@
-// 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 time
-
-import (
- "errors"
- "testing"
-)
-
-// errNoSamples is returned when testReferenceClocks runs out of samples.
-var errNoSamples = errors.New("no samples available")
-
-// testReferenceClocks returns a preset list of samples and cycle counts.
-type testReferenceClocks struct {
- samples []sample
- cycles []TSCValue
-}
-
-// Sample implements referenceClocks.Sample, returning the next sample in the list.
-func (t *testReferenceClocks) Sample(_ ClockID) (sample, error) {
- if len(t.samples) == 0 {
- return sample{}, errNoSamples
- }
-
- s := t.samples[0]
- if len(t.samples) == 1 {
- t.samples = nil
- } else {
- t.samples = t.samples[1:]
- }
-
- return s, nil
-}
-
-// Cycles implements referenceClocks.Cycles, returning the next TSCValue in the list.
-func (t *testReferenceClocks) Cycles() TSCValue {
- if len(t.cycles) == 0 {
- return 0
- }
-
- c := t.cycles[0]
- if len(t.cycles) == 1 {
- t.cycles = nil
- } else {
- t.cycles = t.cycles[1:]
- }
-
- return c
-}
-
-// newTestSampler returns a sampler that collects samples from
-// the given sample list and cycle counts from the given cycle list.
-func newTestSampler(samples []sample, cycles []TSCValue) *sampler {
- return &sampler{
- clocks: &testReferenceClocks{
- samples: samples,
- cycles: cycles,
- },
- overhead: defaultOverheadCycles,
- }
-}
-
-// generateSamples generates n samples with the given overhead.
-func generateSamples(n int, overhead TSCValue) []sample {
- samples := []sample{{before: 1000000, after: 1000000 + overhead, ref: 100}}
- for i := 0; i < n-1; i++ {
- prev := samples[len(samples)-1]
- samples = append(samples, sample{
- before: prev.before + 1000000,
- after: prev.after + 1000000,
- ref: prev.ref + 100,
- })
- }
- return samples
-}
-
-// TestSample ensures that samples can be collected.
-func TestSample(t *testing.T) {
- testCases := []struct {
- name string
- samples []sample
- err error
- }{
- {
- name: "basic",
- samples: []sample{
- {before: 100000, after: 100000 + defaultOverheadCycles, ref: 100},
- },
- err: nil,
- },
- {
- // Sample with backwards TSC ignored.
- // referenceClock should retry and get errNoSamples.
- name: "backwards-tsc-ignored",
- samples: []sample{
- {before: 100000, after: 90000, ref: 100},
- },
- err: errNoSamples,
- },
- {
- // Sample far above overhead skipped.
- // referenceClock should retry and get errNoSamples.
- name: "reject-overhead",
- samples: []sample{
- {before: 100000, after: 100000 + 5*defaultOverheadCycles, ref: 100},
- },
- err: errNoSamples,
- },
- {
- // Maximum overhead allowed is bounded.
- name: "over-max-overhead",
- // Generate a bunch of samples. The reference clock
- // needs a while to ramp up its expected overhead.
- samples: generateSamples(100, 2*maxOverheadCycles),
- err: errOverheadTooHigh,
- },
- {
- // Overhead at maximum overhead is allowed.
- name: "max-overhead",
- // Generate a bunch of samples. The reference clock
- // needs a while to ramp up its expected overhead.
- samples: generateSamples(100, maxOverheadCycles),
- err: nil,
- },
- }
- for _, tc := range testCases {
- t.Run(tc.name, func(t *testing.T) {
- s := newTestSampler(tc.samples, nil)
- err := s.Sample()
- if err != tc.err {
- t.Errorf("Sample err got %v want %v", err, tc.err)
- }
- })
- }
-}
-
-// TestOutliersIgnored tests that referenceClock ignores samples with very high
-// overhead.
-func TestOutliersIgnored(t *testing.T) {
- s := newTestSampler([]sample{
- {before: 100000, after: 100000 + defaultOverheadCycles, ref: 100},
- {before: 200000, after: 200000 + defaultOverheadCycles, ref: 200},
- {before: 300000, after: 300000 + defaultOverheadCycles, ref: 300},
- {before: 400000, after: 400000 + defaultOverheadCycles, ref: 400},
- {before: 500000, after: 500000 + 5*defaultOverheadCycles, ref: 500}, // Ignored
- {before: 600000, after: 600000 + defaultOverheadCycles, ref: 600},
- {before: 700000, after: 700000 + defaultOverheadCycles, ref: 700},
- }, nil)
-
- // Collect 5 samples.
- for i := 0; i < 5; i++ {
- err := s.Sample()
- if err != nil {
- t.Fatalf("Unexpected error while sampling: %v", err)
- }
- }
-
- oldest, newest, ok := s.Range()
- if !ok {
- t.Fatalf("Range not ok")
- }
-
- if oldest.ref != 100 {
- t.Errorf("oldest.ref got %v want %v", oldest.ref, 100)
- }
-
- // We skipped the high-overhead sample.
- if newest.ref != 600 {
- t.Errorf("newest.ref got %v want %v", newest.ref, 600)
- }
-}
diff --git a/pkg/sentry/time/seqatomic_parameters_unsafe.go b/pkg/sentry/time/seqatomic_parameters_unsafe.go
new file mode 100644
index 000000000..357e476ec
--- /dev/null
+++ b/pkg/sentry/time/seqatomic_parameters_unsafe.go
@@ -0,0 +1,38 @@
+package time
+
+import (
+ "unsafe"
+
+ "gvisor.dev/gvisor/pkg/gohacks"
+ "gvisor.dev/gvisor/pkg/sync"
+)
+
+// SeqAtomicLoad returns a copy of *ptr, ensuring that the read does not race
+// with any writer critical sections in seq.
+//
+//go:nosplit
+func SeqAtomicLoadParameters(seq *sync.SeqCount, ptr *Parameters) Parameters {
+ for {
+ if val, ok := SeqAtomicTryLoadParameters(seq, seq.BeginRead(), ptr); ok {
+ return val
+ }
+ }
+}
+
+// SeqAtomicTryLoad returns a copy of *ptr while in a reader critical section
+// in seq initiated by a call to seq.BeginRead() that returned epoch. If the
+// read would race with a writer critical section, SeqAtomicTryLoad returns
+// (unspecified, false).
+//
+//go:nosplit
+func SeqAtomicTryLoadParameters(seq *sync.SeqCount, epoch sync.SeqCountEpoch, ptr *Parameters) (val Parameters, ok bool) {
+ if sync.RaceEnabled {
+
+ gohacks.Memmove(unsafe.Pointer(&val), unsafe.Pointer(ptr), unsafe.Sizeof(val))
+ } else {
+
+ val = *ptr
+ }
+ ok = seq.ReadOk(epoch)
+ return
+}
diff --git a/pkg/sentry/time/time_amd64_state_autogen.go b/pkg/sentry/time/time_amd64_state_autogen.go
new file mode 100644
index 000000000..a4d3199ac
--- /dev/null
+++ b/pkg/sentry/time/time_amd64_state_autogen.go
@@ -0,0 +1,5 @@
+// automatically generated by stateify.
+
+// +build amd64
+
+package time
diff --git a/pkg/sentry/time/time_arm64_state_autogen.go b/pkg/sentry/time/time_arm64_state_autogen.go
new file mode 100644
index 000000000..6929b58cd
--- /dev/null
+++ b/pkg/sentry/time/time_arm64_state_autogen.go
@@ -0,0 +1,5 @@
+// automatically generated by stateify.
+
+// +build arm64
+
+package time
diff --git a/pkg/sentry/time/time_state_autogen.go b/pkg/sentry/time/time_state_autogen.go
new file mode 100644
index 000000000..2adc9c9e0
--- /dev/null
+++ b/pkg/sentry/time/time_state_autogen.go
@@ -0,0 +1,3 @@
+// automatically generated by stateify.
+
+package time
diff --git a/pkg/sentry/time/time_unsafe_state_autogen.go b/pkg/sentry/time/time_unsafe_state_autogen.go
new file mode 100644
index 000000000..2adc9c9e0
--- /dev/null
+++ b/pkg/sentry/time/time_unsafe_state_autogen.go
@@ -0,0 +1,3 @@
+// automatically generated by stateify.
+
+package time