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 152 153 154 155 156 157 158
|
// 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 kernel
import (
"fmt"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/safemem"
"gvisor.dev/gvisor/pkg/sentry/memmap"
"gvisor.dev/gvisor/pkg/sentry/pgalloc"
)
// vdsoParams are the parameters exposed to the VDSO.
//
// They are exposed to the VDSO via a parameter page managed by VDSOParamPage,
// which also includes a sequence counter.
//
// +marshal
type vdsoParams struct {
monotonicReady uint64
monotonicBaseCycles int64
monotonicBaseRef int64
monotonicFrequency uint64
realtimeReady uint64
realtimeBaseCycles int64
realtimeBaseRef int64
realtimeFrequency uint64
}
// VDSOParamPage manages a VDSO parameter page.
//
// Its memory layout looks like:
//
// type page struct {
// // seq is a sequence counter that protects the fields below.
// seq uint64
// vdsoParams
// }
//
// Everything in the struct is 8 bytes for easy alignment.
//
// It must be kept in sync with params in vdso/vdso_time.cc.
//
// +stateify savable
type VDSOParamPage struct {
// The parameter page is fr, allocated from mfp.MemoryFile().
mfp pgalloc.MemoryFileProvider
fr memmap.FileRange
// seq is the current sequence count written to the page.
//
// A write is in progress if bit 1 of the counter is set.
//
// Timekeeper's updater goroutine may call Write before equality is
// checked in state_test_util tests, causing this field to change across
// save / restore.
seq uint64
// copyScratchBuffer is a temporary buffer used to marshal the params before
// copying it to the real parameter page. The parameter page is typically
// updated at a moderate frequency of ~O(seconds) throughout the lifetime of
// the sentry, so reusing this buffer is a good tradeoff between memory
// usage and the cost of allocation.
copyScratchBuffer []byte
}
// NewVDSOParamPage returns a VDSOParamPage.
//
// Preconditions:
// - fr is a single page allocated from mfp.MemoryFile(). VDSOParamPage does
// not take ownership of fr; it must remain allocated for the lifetime of the
// VDSOParamPage.
// - VDSOParamPage must be the only writer to fr.
// - mfp.MemoryFile().MapInternal(fr) must return a single safemem.Block.
func NewVDSOParamPage(mfp pgalloc.MemoryFileProvider, fr memmap.FileRange) *VDSOParamPage {
return &VDSOParamPage{
mfp: mfp,
fr: fr,
copyScratchBuffer: make([]byte, (*vdsoParams)(nil).SizeBytes()),
}
}
// access returns a mapping of the param page.
func (v *VDSOParamPage) access() (safemem.Block, error) {
bs, err := v.mfp.MemoryFile().MapInternal(v.fr, hostarch.ReadWrite)
if err != nil {
return safemem.Block{}, err
}
if bs.NumBlocks() != 1 {
panic(fmt.Sprintf("Multiple blocks (%d) in VDSO param BlockSeq", bs.NumBlocks()))
}
return bs.Head(), nil
}
// incrementSeq increments the sequence counter in the param page.
func (v *VDSOParamPage) incrementSeq(paramPage safemem.Block) error {
next := v.seq + 1
old, err := safemem.SwapUint64(paramPage, next)
if err != nil {
return err
}
if old != v.seq {
return fmt.Errorf("unexpected VDSOParamPage seq value: got %d expected %d; application may hang or get incorrect time from the VDSO", old, v.seq)
}
v.seq = next
return nil
}
// Write updates the VDSO parameters.
//
// Write starts a write block, calls f to get the new parameters, writes
// out the new parameters, then ends the write block.
func (v *VDSOParamPage) Write(f func() vdsoParams) error {
paramPage, err := v.access()
if err != nil {
return err
}
// Write begin.
next := v.seq + 1
if next%2 != 1 {
panic("Out-of-order sequence count")
}
err = v.incrementSeq(paramPage)
if err != nil {
return err
}
// Get the new params.
p := f()
buf := v.copyScratchBuffer[:p.SizeBytes()]
p.MarshalUnsafe(buf)
// Skip the sequence counter.
if _, err := safemem.Copy(paramPage.DropFirst(8), safemem.BlockFromSafeSlice(buf)); err != nil {
panic(fmt.Sprintf("Unable to get set VDSO parameters: %v", err))
}
// Write end.
return v.incrementSeq(paramPage)
}
|