diff --git a/pkg/hostarch/BUILD b/pkg/hostarch/BUILD index 2a985f82d..9c6be1cdd 100644 --- a/pkg/hostarch/BUILD +++ b/pkg/hostarch/BUILD @@ -34,6 +34,7 @@ go_library( "hostarch.go", "hostarch_arm64.go", "hostarch_x86.go", + "sizes_util.go", ], visibility = ["//:sandbox"], deps = [ diff --git a/pkg/hostarch/addr.go b/pkg/hostarch/addr.go index ef7b341bb..c39479759 100644 --- a/pkg/hostarch/addr.go +++ b/pkg/hostarch/addr.go @@ -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 -} diff --git a/pkg/hostarch/sizes_util.go b/pkg/hostarch/sizes_util.go new file mode 100644 index 000000000..cce9c002e --- /dev/null +++ b/pkg/hostarch/sizes_util.go @@ -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 +} diff --git a/pkg/sentry/fsimpl/tmpfs/tmpfs.go b/pkg/sentry/fsimpl/tmpfs/tmpfs.go index d1412fd0d..ba1b7d4f6 100644 --- a/pkg/sentry/fsimpl/tmpfs/tmpfs.go +++ b/pkg/sentry/fsimpl/tmpfs/tmpfs.go @@ -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