Skip to content

Commit ffb5646

Browse files
committed
runtime: define maximum supported physical page and huge page sizes
This change defines a maximum supported physical and huge page size in the runtime based on the new page allocator's implementation, and uses them where appropriate. Furthemore, if the system exceeds the maximum supported huge page size, we simply ignore it silently. It also fixes a huge-page-related test which is only triggered by a condition which is definitely wrong. Finally, it adds a few TODOs related to code clean-up and supporting larger huge page sizes. Updates #35112. Fixes #35431. Change-Id: Ie4348afb6bf047cce2c1433576d1514720d8230f Reviewed-on: https://go-review.googlesource.com/c/go/+/205937 Run-TryBot: Michael Knyszek <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Keith Randall <[email protected]> Reviewed-by: Cherry Zhang <[email protected]>
1 parent a782472 commit ffb5646

File tree

4 files changed

+45
-14
lines changed

4 files changed

+45
-14
lines changed

src/runtime/malloc.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,10 @@ func mallocinit() {
436436
// The OS init code failed to fetch the physical page size.
437437
throw("failed to get system page size")
438438
}
439+
if physPageSize > maxPhysPageSize {
440+
print("system page size (", physPageSize, ") is larger than maximum page size (", maxPhysPageSize, ")\n")
441+
throw("bad system page size")
442+
}
439443
if physPageSize < minPhysPageSize {
440444
print("system page size (", physPageSize, ") is smaller than minimum page size (", minPhysPageSize, ")\n")
441445
throw("bad system page size")
@@ -448,6 +452,13 @@ func mallocinit() {
448452
print("system huge page size (", physHugePageSize, ") must be a power of 2\n")
449453
throw("bad system huge page size")
450454
}
455+
if physHugePageSize > maxPhysHugePageSize {
456+
// physHugePageSize is greater than the maximum supported huge page size.
457+
// Don't throw here, like in the other cases, since a system configured
458+
// in this way isn't wrong, we just don't have the code to support them.
459+
// Instead, silently set the huge page size to zero.
460+
physHugePageSize = 0
461+
}
451462
if physHugePageSize != 0 {
452463
// Since physHugePageSize is a power of 2, it suffices to increase
453464
// physHugePageShift until 1<<physHugePageShift == physHugePageSize.

src/runtime/mgcscavenge.go

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,10 @@ const (
7676
// incurs an additional cost), to account for heap fragmentation and
7777
// the ever-changing layout of the heap.
7878
retainExtraPercent = 10
79+
80+
// maxPagesPerPhysPage is the maximum number of supported runtime pages per
81+
// physical page, based on maxPhysPageSize.
82+
maxPagesPerPhysPage = maxPhysPageSize / pageSize
7983
)
8084

8185
// heapRetained returns an estimate of the current heap RSS.
@@ -498,7 +502,7 @@ func (s *pageAlloc) scavengeRangeLocked(ci chunkIdx, base, npages uint) {
498502
//
499503
// Note that if m == 1, this is a no-op.
500504
//
501-
// m must be a power of 2 <= 64.
505+
// m must be a power of 2 <= maxPagesPerPhysPage.
502506
func fillAligned(x uint64, m uint) uint64 {
503507
apply := func(x uint64, c uint64) uint64 {
504508
// The technique used it here is derived from
@@ -533,8 +537,10 @@ func fillAligned(x uint64, m uint) uint64 {
533537
x = apply(x, 0x7fff7fff7fff7fff)
534538
case 32:
535539
x = apply(x, 0x7fffffff7fffffff)
536-
case 64:
540+
case 64: // == maxPagesPerPhysPage
537541
x = apply(x, 0x7fffffffffffffff)
542+
default:
543+
throw("bad m value")
538544
}
539545
// Now, the top bit of each m-aligned group in x is set
540546
// that group was all zero in the original x.
@@ -552,14 +558,14 @@ func fillAligned(x uint64, m uint) uint64 {
552558
// min pages of free-and-unscavenged memory in the region represented by this
553559
// pallocData.
554560
//
555-
// min must be a non-zero power of 2 <= 64.
561+
// min must be a non-zero power of 2 <= maxPagesPerPhysPage.
556562
func (m *pallocData) hasScavengeCandidate(min uintptr) bool {
557563
if min&(min-1) != 0 || min == 0 {
558564
print("runtime: min = ", min, "\n")
559565
throw("min must be a non-zero power of 2")
560-
} else if min > 64 {
566+
} else if min > maxPagesPerPhysPage {
561567
print("runtime: min = ", min, "\n")
562-
throw("physical page sizes > 512 KiB are not supported")
568+
throw("min too large")
563569
}
564570

565571
// The goal of this search is to see if the chunk contains any free and unscavenged memory.
@@ -590,7 +596,7 @@ func (m *pallocData) hasScavengeCandidate(min uintptr) bool {
590596
// min indicates a hard minimum size and alignment for runs of pages. That is,
591597
// findScavengeCandidate will not return a region smaller than min pages in size,
592598
// or that is min pages or greater in size but not aligned to min. min must be
593-
// a non-zero power of 2 <= 64.
599+
// a non-zero power of 2 <= maxPagesPerPhysPage.
594600
//
595601
// max is a hint for how big of a region is desired. If max >= pallocChunkPages, then
596602
// findScavengeCandidate effectively returns entire free and unscavenged regions.
@@ -603,9 +609,9 @@ func (m *pallocData) findScavengeCandidate(searchIdx uint, min, max uintptr) (ui
603609
if min&(min-1) != 0 || min == 0 {
604610
print("runtime: min = ", min, "\n")
605611
throw("min must be a non-zero power of 2")
606-
} else if min > 64 {
612+
} else if min > maxPagesPerPhysPage {
607613
print("runtime: min = ", min, "\n")
608-
throw("physical page sizes > 512 KiB are not supported")
614+
throw("min too large")
609615
}
610616
// max is allowed to be less than min, but we need to ensure
611617
// we never truncate further than min.
@@ -660,6 +666,11 @@ func (m *pallocData) findScavengeCandidate(searchIdx uint, min, max uintptr) (ui
660666
}
661667
start := end - size
662668

669+
// Each huge page is guaranteed to fit in a single palloc chunk.
670+
//
671+
// TODO(mknyszek): Support larger huge page sizes.
672+
// TODO(mknyszek): Consider taking pages-per-huge-page as a parameter
673+
// so we can write tests for this.
663674
if physHugePageSize > pageSize && physHugePageSize > physPageSize {
664675
// We have huge pages, so let's ensure we don't break one by scavenging
665676
// over a huge page boundary. If the range [start, start+size) overlaps with

src/runtime/mgcscavenge_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -229,8 +229,8 @@ func TestPallocDataFindScavengeCandidate(t *testing.T) {
229229
max: 3, // Make it so that max would have us try to break the huge page.
230230
want: BitRange{0, bits + 2},
231231
}
232-
if bits >= 3*PallocChunkPages {
233-
// We need at least 3 huge pages in an arena for this test to make sense.
232+
if 3*bits < PallocChunkPages {
233+
// We need at least 3 huge pages in a chunk for this test to make sense.
234234
tests["PreserveHugePageMiddle"] = test{
235235
alloc: []BitRange{{0, bits - 10}, {2*bits + 10, PallocChunkPages - (2*bits + 10)}},
236236
min: 1,

src/runtime/mheap.go

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,19 @@ import (
1515
"unsafe"
1616
)
1717

18-
// minPhysPageSize is a lower-bound on the physical page size. The
19-
// true physical page size may be larger than this. In contrast,
20-
// sys.PhysPageSize is an upper-bound on the physical page size.
21-
const minPhysPageSize = 4096
18+
const (
19+
// minPhysPageSize is a lower-bound on the physical page size. The
20+
// true physical page size may be larger than this. In contrast,
21+
// sys.PhysPageSize is an upper-bound on the physical page size.
22+
minPhysPageSize = 4096
23+
24+
// maxPhysPageSize is the maximum page size the runtime supports.
25+
maxPhysPageSize = 512 << 10
26+
27+
// maxPhysHugePageSize sets an upper-bound on the maximum huge page size
28+
// that the runtime supports.
29+
maxPhysHugePageSize = pallocChunkBytes
30+
)
2231

2332
// Main malloc heap.
2433
// The heap itself is the "free" and "scav" treaps,

0 commit comments

Comments
 (0)