Make hostarch.Page/HugePage/CacheLine* functions generic and canonical.

Also add the missing HugePage equivalents of Page functions.

The fix for golang/go#56280, which first appears in Go 1.20, is needed to
prevent this CL from regressing performance:

Before fix:
```
TEXT gvisor/pkg/sentry/mm/mm.(*MemoryManager).SetNumaPolicy(SB) gvisor/pkg/sentry/mm/syscalls.go
  ...
  addr.go:75            0x7f000054e35e          488d053bc2b100          LEAQ gvisor/pkg/hostarch/hostarch..dict.IsPageAligned[gvisor/pkg/hostarch/hostarch.Addr](SB), AX
  addr.go:75            0x7f000054e365          e8961cc4ff              CALL gvisor/pkg/hostarch/hostarch.IsPageAligned[go.shape.uintptr](SB)
```

After fix:
```
TEXT gvisor/pkg/sentry/mm/mm.(*MemoryManager).SetNumaPolicy(SB) gvisor/pkg/sentry/mm/syscalls.go
  ...
  addr.go:75            0x7f00004da61a          90                      NOPL
  addr.go:75            0x7f00004da61b          0f1f440000              NOPL 0(AX)(AX*1)
  sizes_util.go:57      0x7f00004da620          48f7c3ff0f0000          TESTQ $0xfff, BX
  syscalls.go:1021      0x7f00004da627          0f855f010000            JNE 0x7f00004da78c
```

PiperOrigin-RevId: 507608080
This commit is contained in:
Jamie Liu
2023-02-06 16:09:19 -08:00
committed by gVisor bot
parent d4843e167a
commit 0721ca2fe3
4 changed files with 133 additions and 70 deletions
+1
View File
@@ -34,6 +34,7 @@ go_library(
"hostarch.go",
"hostarch_arm64.go",
"hostarch_x86.go",
"sizes_util.go",
],
visibility = ["//:sandbox"],
deps = [
+18 -69
View File
@@ -18,7 +18,7 @@ import (
"fmt"
)
// Addr represents a generic virtual address.
// Addr represents an address in an unspecified address space.
//
// +stateify savable
type Addr uintptr
@@ -39,51 +39,40 @@ func (v Addr) AddLength(length uint64) (end Addr, ok bool) {
return
}
// RoundDown returns the address rounded down to the nearest page boundary.
// RoundDown is equivalent to function PageRoundDown.
func (v Addr) RoundDown() Addr {
return v & ^Addr(PageSize-1)
return PageRoundDown(v)
}
// RoundUp returns the address rounded up to the nearest page boundary. ok is
// true iff rounding up did not wrap around.
func (v Addr) RoundUp() (addr Addr, ok bool) {
addr = Addr(v + PageSize - 1).RoundDown()
ok = addr >= v
return
// RoundUp is equivalent to function PageRoundUp.
func (v Addr) RoundUp() (Addr, bool) {
return PageRoundUp(v)
}
// MustRoundUp is equivalent to RoundUp, but panics if rounding up wraps
// around.
// MustRoundUp is equivalent to function MustPageRoundUp.
func (v Addr) MustRoundUp() Addr {
addr, ok := v.RoundUp()
if !ok {
panic(fmt.Sprintf("hostarch.Addr(%d).RoundUp() wraps", v))
}
return addr
return MustPageRoundUp(v)
}
// HugeRoundDown returns the address rounded down to the nearest huge page
// boundary.
// HugeRoundDown is equivalent to function HugePageRoundDown.
func (v Addr) HugeRoundDown() Addr {
return v & ^Addr(HugePageSize-1)
return HugePageRoundDown(v)
}
// HugeRoundUp returns the address rounded up to the nearest huge page boundary.
// ok is true iff rounding up did not wrap around.
func (v Addr) HugeRoundUp() (addr Addr, ok bool) {
addr = Addr(v + HugePageSize - 1).HugeRoundDown()
ok = addr >= v
return
// HugeRoundUp is equivalent to function HugePageRoundUp.
func (v Addr) HugeRoundUp() (Addr, bool) {
return HugePageRoundUp(v)
}
// PageOffset returns the offset of v into the current page.
// PageOffset is equivalent to function PageOffset, except that it casts the
// result to uint64.
func (v Addr) PageOffset() uint64 {
return uint64(v & Addr(PageSize-1))
return uint64(PageOffset(v))
}
// IsPageAligned returns true if v.PageOffset() == 0.
// IsPageAligned is equivalent to function IsPageAligned.
func (v Addr) IsPageAligned() bool {
return v.PageOffset() == 0
return IsPageAligned(v)
}
// AddrRange is a range of Addrs.
@@ -106,43 +95,3 @@ func (ar AddrRange) IsPageAligned() bool {
func (ar AddrRange) String() string {
return fmt.Sprintf("[%#x, %#x)", ar.Start, ar.End)
}
// PageRoundDown/Up are equivalent to Addr.RoundDown/Up, but without the
// potentially truncating conversion from uint64 to Addr. This is necessary
// because there is no way to define generic "PageRoundDown/Up" functions in Go.
// PageRoundDown returns x rounded down to the nearest page boundary.
func PageRoundDown(x uint64) uint64 {
return x &^ (PageSize - 1)
}
// PageRoundUp returns x rounded up to the nearest page boundary.
// ok is true iff rounding up did not wrap around.
func PageRoundUp(x uint64) (addr uint64, ok bool) {
addr = PageRoundDown(x + PageSize - 1)
ok = addr >= x
return
}
// ToPages returns number of Pages for x.
// x is rounded up to the nearest page boundary.
func ToPages(x uint64) (uint64, bool) {
xRoundedUp, ok := PageRoundUp(x)
if !ok {
return 0, false
}
return xRoundedUp / PageSize, true
}
// CacheLineRoundDown returns the offset rounded down to the nearest cache line boundary.
func CacheLineRoundDown(x uint64) uint64 {
return x & ^uint64(CacheLineSize-1)
}
// CacheLineRoundUp returns the offset rounded up to the nearest cache line boundary. ok is true iff
// rounding up did not wrap around.
func CacheLineRoundUp(x uint64) (val uint64, ok bool) {
val = CacheLineRoundDown(x + uint64(CacheLineSize-1))
ok = val >= x
return
}
+113
View File
@@ -0,0 +1,113 @@
// Copyright 2022 The gVisor Authors.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package hostarch
// Masks often used when working with alignment in constant expressions.
const (
PageMask = PageSize - 1
HugePageMask = HugePageSize - 1
CacheLineMask = CacheLineSize - 1
)
type bytecount interface {
~uint | ~uint16 | ~uint32 | ~uint64 | ~uintptr
}
type hugebytecount interface {
~uint | ~uint32 | ~uint64 | ~uintptr
}
// PageRoundDown returns x rounded down to the nearest multiple of PageSize.
func PageRoundDown[T bytecount](x T) T {
return x &^ PageMask
}
// PageRoundUp returns x rounded up to the nearest multiple of PageSize. ok is
// true iff rounding up does not overflow the range of T.
func PageRoundUp[T bytecount](x T) (val T, ok bool) {
val = PageRoundDown(x + PageMask)
ok = val >= x
return
}
// MustPageRoundUp is equivalent to PageRoundUp, but panics if rounding up
// overflows.
func MustPageRoundUp[T bytecount](x T) T {
val, ok := PageRoundUp(x)
if !ok {
panic("PageRoundUp overflows")
}
return val
}
// PageOffset returns the offset of x into its containing page.
func PageOffset[T bytecount](x T) T {
return x & PageMask
}
// IsPageAligned returns true if x is a multiple of PageSize.
func IsPageAligned[T bytecount](x T) bool {
return PageOffset(x) == 0
}
// ToPagesRoundUp returns (the number of pages equal to x bytes rounded up,
// true). If rounding x up to a multiple of PageSize overflows the range of T,
// ToPagesRoundUp returns (unspecified, false).
func ToPagesRoundUp[T bytecount](x T) (T, bool) {
y := x + PageMask
if y < x {
return x, false
}
return y / PageSize, true
}
// HugePageRoundDown returns x rounded down to the nearest multiple of
// HugePageSize.
func HugePageRoundDown[T hugebytecount](x T) T {
return x &^ HugePageMask
}
// HugePageRoundUp returns x rounded up to the nearest multiple of
// HugePageSize. ok is true iff rounding up does not overflow the range of T.
func HugePageRoundUp[T hugebytecount](x T) (val T, ok bool) {
val = HugePageRoundDown(x + HugePageMask)
ok = val >= x
return
}
// MustHugePageRoundUp is equivalent to HugePageRoundUp, but panics if rounding
// up overflows.
func MustHugePageRoundUp[T hugebytecount](x T) T {
val, ok := HugePageRoundUp(x)
if !ok {
panic("HugePageRoundUp overflows")
}
return val
}
// HugePageOffset returns the offset of x into its containing page.
func HugePageOffset[T hugebytecount](x T) T {
return x & HugePageMask
}
// IsHugePageAligned returns true if x is a multiple of HugePageSize.
func IsHugePageAligned[T hugebytecount](x T) bool {
return HugePageOffset(x) == 0
}
// CacheLineRoundDown returns the offset rounded down to the nearest multiple
// of CacheLineSize.
func CacheLineRoundDown[T bytecount](x T) T {
return x &^ CacheLineMask
}
// CacheLineRoundUp returns the offset rounded up to the nearest multiple of
// CacheLineSize. ok is true iff rounding up does not overflow the range of T.
func CacheLineRoundUp[T bytecount](x T) (val T, ok bool) {
val = CacheLineRoundDown(x + CacheLineMask)
ok = val >= x
return
}
+1 -1
View File
@@ -244,7 +244,7 @@ func (fstype FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
}
// Convert size in bytes to nearest Page Size bytes
// as Linux allocates memory in terms of Page size.
maxSizeInPages, ok = hostarch.ToPages(maxSizeInBytes)
maxSizeInPages, ok = hostarch.ToPagesRoundUp(maxSizeInBytes)
if !ok {
ctx.Warningf("tmpfs.FilesystemType.GetFilesystem: Pages RoundUp Overflow error: %q", ok)
return nil, nil, linuxerr.EINVAL