1
0
mirror of https://github.com/golang/go synced 2024-11-23 19:30:05 -07:00

runtime: make maxOffAddr reflect the actual address space upper bound

Currently maxOffAddr is defined in terms of the whole 64-bit address
space, assuming that it's all supported, by using ^uintptr(0) as the
maximal address in the offset space. In reality, the maximal address in
the offset space is (1<<heapAddrBits)-1 because we don't have more than
that actually available to us on a given platform.

On most platforms this is fine, because arenaBaseOffset is just
connecting two segments of address space, but on AIX we use it as an
actual offset for the starting address of the available address space,
which is limited. This means using ^uintptr(0) as the maximal address in
the offset address space causes wrap-around, especially when we just
want to represent a range approximately like [addr, infinity), which
today we do by using maxOffAddr.

To fix this, we define maxOffAddr more appropriately, in terms of
(1<<heapAddrBits)-1.

This change also redefines arenaBaseOffset to not be the negation of the
virtual address corresponding to address zero in the virtual address
space, but instead directly as the virtual address corresponding to
zero. This matches the existing documentation more closely and makes the
logic around arenaBaseOffset decidedly simpler, especially when trying
to reason about its use on AIX.

Fixes #38966.

Change-Id: I1336e5036a39de846f64cc2d253e8536dee57611
Reviewed-on: https://go-review.googlesource.com/c/go/+/233497
Run-TryBot: Michael Knyszek <mknyszek@google.com>
Reviewed-by: Austin Clements <austin@google.com>
Reviewed-by: Michael Pratt <mpratt@google.com>
This commit is contained in:
Michael Anthony Knyszek 2020-05-12 16:08:50 +00:00 committed by Michael Knyszek
parent 364a05e2fe
commit 796786cd0c
5 changed files with 20 additions and 36 deletions

View File

@ -876,13 +876,9 @@ func FreePageAlloc(pp *PageAlloc) {
// 64 bit and 32 bit platforms, allowing the tests to share code // 64 bit and 32 bit platforms, allowing the tests to share code
// between the two. // between the two.
// //
// On AIX, the arenaBaseOffset is 0x0a00000000000000. However, this
// constant can't be used here because it is negative and will cause
// a constant overflow.
//
// This should not be higher than 0x100*pallocChunkBytes to support // This should not be higher than 0x100*pallocChunkBytes to support
// mips and mipsle, which only have 31-bit address spaces. // mips and mipsle, which only have 31-bit address spaces.
var BaseChunkIdx = ChunkIdx(chunkIndex(((0xc000*pageAlloc64Bit + 0x100*pageAlloc32Bit) * pallocChunkBytes) + 0x0a00000000000000*sys.GoosAix)) var BaseChunkIdx = ChunkIdx(chunkIndex(((0xc000*pageAlloc64Bit + 0x100*pageAlloc32Bit) * pallocChunkBytes) + arenaBaseOffset*sys.GoosAix))
// PageBase returns an address given a chunk index and a page index // PageBase returns an address given a chunk index and a page index
// relative to that chunk. // relative to that chunk.

View File

@ -302,7 +302,7 @@ const (
// //
// On other platforms, the user address space is contiguous // On other platforms, the user address space is contiguous
// and starts at 0, so no offset is necessary. // and starts at 0, so no offset is necessary.
arenaBaseOffset = sys.GoarchAmd64*(1<<47) + (^0x0a00000000000000+1)&uintptrMask*sys.GoosAix arenaBaseOffset = 0xffff800000000000*sys.GoarchAmd64 + 0x0a00000000000000*sys.GoosAix
// Max number of threads to run garbage collection. // Max number of threads to run garbage collection.
// 2, 3, and 4 are all plausible maximums depending // 2, 3, and 4 are all plausible maximums depending

View File

@ -576,13 +576,13 @@ func (sc spanClass) noscan() bool {
// //
//go:nosplit //go:nosplit
func arenaIndex(p uintptr) arenaIdx { func arenaIndex(p uintptr) arenaIdx {
return arenaIdx((p + arenaBaseOffset) / heapArenaBytes) return arenaIdx((p - arenaBaseOffset) / heapArenaBytes)
} }
// arenaBase returns the low address of the region covered by heap // arenaBase returns the low address of the region covered by heap
// arena i. // arena i.
func arenaBase(i arenaIdx) uintptr { func arenaBase(i arenaIdx) uintptr {
return uintptr(i)*heapArenaBytes - arenaBaseOffset return uintptr(i)*heapArenaBytes + arenaBaseOffset
} }
type arenaIdx uint type arenaIdx uint

View File

@ -99,12 +99,12 @@ type chunkIdx uint
// chunkIndex returns the global index of the palloc chunk containing the // chunkIndex returns the global index of the palloc chunk containing the
// pointer p. // pointer p.
func chunkIndex(p uintptr) chunkIdx { func chunkIndex(p uintptr) chunkIdx {
return chunkIdx((p + arenaBaseOffset) / pallocChunkBytes) return chunkIdx((p - arenaBaseOffset) / pallocChunkBytes)
} }
// chunkIndex returns the base address of the palloc chunk at index ci. // chunkIndex returns the base address of the palloc chunk at index ci.
func chunkBase(ci chunkIdx) uintptr { func chunkBase(ci chunkIdx) uintptr {
return uintptr(ci)*pallocChunkBytes - arenaBaseOffset return uintptr(ci)*pallocChunkBytes + arenaBaseOffset
} }
// chunkPageIndex computes the index of the page that contains p, // chunkPageIndex computes the index of the page that contains p,
@ -136,13 +136,13 @@ func (i chunkIdx) l2() uint {
// offAddrToLevelIndex converts an address in the offset address space // offAddrToLevelIndex converts an address in the offset address space
// to the index into summary[level] containing addr. // to the index into summary[level] containing addr.
func offAddrToLevelIndex(level int, addr offAddr) int { func offAddrToLevelIndex(level int, addr offAddr) int {
return int((addr.a + arenaBaseOffset) >> levelShift[level]) return int((addr.a - arenaBaseOffset) >> levelShift[level])
} }
// levelIndexToOffAddr converts an index into summary[level] into // levelIndexToOffAddr converts an index into summary[level] into
// the corresponding address in the offset address space. // the corresponding address in the offset address space.
func levelIndexToOffAddr(level, idx int) offAddr { func levelIndexToOffAddr(level, idx int) offAddr {
return offAddr{(uintptr(idx) << levelShift[level]) - arenaBaseOffset} return offAddr{(uintptr(idx) << levelShift[level]) + arenaBaseOffset}
} }
// addrsToSummaryRange converts base and limit pointers into a range // addrsToSummaryRange converts base and limit pointers into a range
@ -159,8 +159,8 @@ func addrsToSummaryRange(level int, base, limit uintptr) (lo int, hi int) {
// of a summary's max page count boundary for this level // of a summary's max page count boundary for this level
// (1 << levelLogPages[level]). So, make limit an inclusive upper bound // (1 << levelLogPages[level]). So, make limit an inclusive upper bound
// then shift, then add 1, so we get an exclusive upper bound at the end. // then shift, then add 1, so we get an exclusive upper bound at the end.
lo = int((base + arenaBaseOffset) >> levelShift[level]) lo = int((base - arenaBaseOffset) >> levelShift[level])
hi = int(((limit-1)+arenaBaseOffset)>>levelShift[level]) + 1 hi = int(((limit-1)-arenaBaseOffset)>>levelShift[level]) + 1
return return
} }

View File

@ -31,7 +31,7 @@ type addrRange struct {
// Throws if the base and limit are not in the same memory segment. // Throws if the base and limit are not in the same memory segment.
func makeAddrRange(base, limit uintptr) addrRange { func makeAddrRange(base, limit uintptr) addrRange {
r := addrRange{offAddr{base}, offAddr{limit}} r := addrRange{offAddr{base}, offAddr{limit}}
if (base+arenaBaseOffset >= arenaBaseOffset) != (limit+arenaBaseOffset >= arenaBaseOffset) { if (base-arenaBaseOffset >= base) != (limit-arenaBaseOffset >= limit) {
throw("addr range base and limit are not in the same memory segment") throw("addr range base and limit are not in the same memory segment")
} }
return r return r
@ -71,33 +71,21 @@ func (a addrRange) subtract(b addrRange) addrRange {
var ( var (
// minOffAddr is the minimum address in the offset space, and // minOffAddr is the minimum address in the offset space, and
// it corresponds to the virtual address -arenaBaseOffset. // it corresponds to the virtual address arenaBaseOffset.
// minOffAddr = offAddr{arenaBaseOffset}
// We don't initialize this with offAddrFromRaw because allocation
// may happen during bootstrapping, and we rely on this value
// being initialized.
//
// As a result, creating this value in Go is tricky because of
// overflow not being allowed in constants. In order to get
// the value we want, we take arenaBaseOffset and do a manual
// two's complement negation, then mask that into what can fit
// into a uintptr.
minOffAddr = offAddr{((^arenaBaseOffset) + 1) & uintptrMask}
// maxOffAddr is the maximum address in the offset address // maxOffAddr is the maximum address in the offset address
// space, and it corresponds to the virtual address // space. It corresponds to the highest virtual address representable
// ^uintptr(0) - arenaBaseOffset. // by the page alloc chunk and heap arena maps.
// maxOffAddr = offAddr{(((1 << heapAddrBits) - 1) + arenaBaseOffset) & uintptrMask}
// We don't initialize this with offAddrFromRaw because allocation
// may happen during bootstrapping, and we rely on this value
// being initialized.
maxOffAddr = offAddr{^uintptr(0) - arenaBaseOffset}
) )
// offAddr represents an address in a contiguous view // offAddr represents an address in a contiguous view
// of the address space on systems where the address space is // of the address space on systems where the address space is
// segmented. On other systems, it's just a normal address. // segmented. On other systems, it's just a normal address.
type offAddr struct { type offAddr struct {
// a is just the virtual address, but should never be used
// directly. Call addr() to get this value instead.
a uintptr a uintptr
} }
@ -120,13 +108,13 @@ func (l1 offAddr) diff(l2 offAddr) uintptr {
// lessThan returns true if l1 is less than l2 in the offset // lessThan returns true if l1 is less than l2 in the offset
// address space. // address space.
func (l1 offAddr) lessThan(l2 offAddr) bool { func (l1 offAddr) lessThan(l2 offAddr) bool {
return (l1.a + arenaBaseOffset) < (l2.a + arenaBaseOffset) return (l1.a - arenaBaseOffset) < (l2.a - arenaBaseOffset)
} }
// lessEqual returns true if l1 is less than or equal to l2 in // lessEqual returns true if l1 is less than or equal to l2 in
// the offset address space. // the offset address space.
func (l1 offAddr) lessEqual(l2 offAddr) bool { func (l1 offAddr) lessEqual(l2 offAddr) bool {
return (l1.a + arenaBaseOffset) <= (l2.a + arenaBaseOffset) return (l1.a - arenaBaseOffset) <= (l2.a - arenaBaseOffset)
} }
// equal returns true if the two offAddr values are equal. // equal returns true if the two offAddr values are equal.