Commit e5ce13c1 authored by Michael Anthony Knyszek's avatar Michael Anthony Knyszek Committed by Michael Knyszek

runtime: add option to scavenge with lock held throughout

This change adds a "locked" parameter to scavenge() and scavengeone()
which allows these methods to be run with the heap lock acquired, and
synchronously with respect to others which acquire the heap lock.

This mode is necessary for both heap-growth scavenging (multiple
asynchronous scavengers here could be problematic) and
debug.FreeOSMemory.

Updates #35112.

Change-Id: I24eea8e40f971760999c980981893676b4c9b666
Reviewed-on: https://go-review.googlesource.com/c/go/+/195699Reviewed-by: default avatarAustin Clements <austin@google.com>
Reviewed-by: default avatarKeith Randall <khr@golang.org>
parent e1ddf050
...@@ -866,9 +866,9 @@ func (p *PageAlloc) Bounds() (ChunkIdx, ChunkIdx) { ...@@ -866,9 +866,9 @@ func (p *PageAlloc) Bounds() (ChunkIdx, ChunkIdx) {
func (p *PageAlloc) PallocData(i ChunkIdx) *PallocData { func (p *PageAlloc) PallocData(i ChunkIdx) *PallocData {
return (*PallocData)(&((*pageAlloc)(p).chunks[i])) return (*PallocData)(&((*pageAlloc)(p).chunks[i]))
} }
func (p *PageAlloc) Scavenge(nbytes uintptr) (r uintptr) { func (p *PageAlloc) Scavenge(nbytes uintptr, locked bool) (r uintptr) {
systemstack(func() { systemstack(func() {
r = (*pageAlloc)(p).scavenge(nbytes) r = (*pageAlloc)(p).scavenge(nbytes, locked)
}) })
return return
} }
......
...@@ -421,16 +421,17 @@ func bgscavenge(c chan int) { ...@@ -421,16 +421,17 @@ func bgscavenge(c chan int) {
// //
// Returns the amount of memory scavenged in bytes. // Returns the amount of memory scavenged in bytes.
// //
// s.mheapLock must not be locked. // If locked == false, s.mheapLock must not be locked. If locked == true,
// s.mheapLock must be locked.
// //
// Must run on the system stack because scavengeOne must run on the // Must run on the system stack because scavengeOne must run on the
// system stack. // system stack.
// //
//go:systemstack //go:systemstack
func (s *pageAlloc) scavenge(nbytes uintptr) uintptr { func (s *pageAlloc) scavenge(nbytes uintptr, locked bool) uintptr {
released := uintptr(0) released := uintptr(0)
for released < nbytes { for released < nbytes {
r := s.scavengeOne(nbytes - released) r := s.scavengeOne(nbytes-released, locked)
if r == 0 { if r == 0 {
// Nothing left to scavenge! Give up. // Nothing left to scavenge! Give up.
break break
...@@ -457,11 +458,14 @@ func (s *pageAlloc) resetScavengeAddr() { ...@@ -457,11 +458,14 @@ func (s *pageAlloc) resetScavengeAddr() {
// //
// Should it exhaust the heap, it will return 0 and set s.scavAddr to minScavAddr. // Should it exhaust the heap, it will return 0 and set s.scavAddr to minScavAddr.
// //
// s.mheapLock must not be locked. Must be run on the system stack because it // If locked == false, s.mheapLock must not be locked.
// acquires the heap lock. // If locked == true, s.mheapLock must be locked.
//
// Must be run on the system stack because it either acquires the heap lock
// or executes with the heap lock acquired.
// //
//go:systemstack //go:systemstack
func (s *pageAlloc) scavengeOne(max uintptr) uintptr { func (s *pageAlloc) scavengeOne(max uintptr, locked bool) uintptr {
// Calculate the maximum number of pages to scavenge. // Calculate the maximum number of pages to scavenge.
// //
// This should be alignUp(max, pageSize) / pageSize but max can and will // This should be alignUp(max, pageSize) / pageSize but max can and will
...@@ -483,10 +487,22 @@ func (s *pageAlloc) scavengeOne(max uintptr) uintptr { ...@@ -483,10 +487,22 @@ func (s *pageAlloc) scavengeOne(max uintptr) uintptr {
minPages = 1 minPages = 1
} }
lock(s.mheapLock) // Helpers for locking and unlocking only if locked == false.
lockHeap := func() {
if !locked {
lock(s.mheapLock)
}
}
unlockHeap := func() {
if !locked {
unlock(s.mheapLock)
}
}
lockHeap()
top := chunkIndex(s.scavAddr) top := chunkIndex(s.scavAddr)
if top < s.start { if top < s.start {
unlock(s.mheapLock) unlockHeap()
return 0 return 0
} }
...@@ -498,10 +514,10 @@ func (s *pageAlloc) scavengeOne(max uintptr) uintptr { ...@@ -498,10 +514,10 @@ func (s *pageAlloc) scavengeOne(max uintptr) uintptr {
// If we found something, scavenge it and return! // If we found something, scavenge it and return!
if npages != 0 { if npages != 0 {
s.scavengeRangeLocked(ci, base, npages) s.scavengeRangeLocked(ci, base, npages)
unlock(s.mheapLock) unlockHeap()
return uintptr(npages) * pageSize return uintptr(npages) * pageSize
} }
unlock(s.mheapLock) unlockHeap()
// Slow path: iterate optimistically looking for any free and unscavenged page. // Slow path: iterate optimistically looking for any free and unscavenged page.
// If we think we see something, stop and verify it! // If we think we see something, stop and verify it!
...@@ -528,7 +544,7 @@ func (s *pageAlloc) scavengeOne(max uintptr) uintptr { ...@@ -528,7 +544,7 @@ func (s *pageAlloc) scavengeOne(max uintptr) uintptr {
} }
// We found a candidate, so let's lock and verify it. // We found a candidate, so let's lock and verify it.
lock(s.mheapLock) lockHeap()
// Find, verify, and scavenge if we can. // Find, verify, and scavenge if we can.
chunk := &s.chunks[i] chunk := &s.chunks[i]
...@@ -536,7 +552,7 @@ func (s *pageAlloc) scavengeOne(max uintptr) uintptr { ...@@ -536,7 +552,7 @@ func (s *pageAlloc) scavengeOne(max uintptr) uintptr {
if npages > 0 { if npages > 0 {
// We found memory to scavenge! Mark the bits and report that up. // We found memory to scavenge! Mark the bits and report that up.
s.scavengeRangeLocked(i, base, npages) s.scavengeRangeLocked(i, base, npages)
unlock(s.mheapLock) unlockHeap()
return uintptr(npages) * pageSize return uintptr(npages) * pageSize
} }
...@@ -544,14 +560,14 @@ func (s *pageAlloc) scavengeOne(max uintptr) uintptr { ...@@ -544,14 +560,14 @@ func (s *pageAlloc) scavengeOne(max uintptr) uintptr {
// all the way down to where we searched as scavenged for future calls // all the way down to where we searched as scavenged for future calls
// and keep iterating. // and keep iterating.
s.scavAddr = chunkBase(i-1) + pallocChunkPages*pageSize - 1 s.scavAddr = chunkBase(i-1) + pallocChunkPages*pageSize - 1
unlock(s.mheapLock) unlockHeap()
} }
lock(s.mheapLock) lockHeap()
// We couldn't find anything, so signal that there's nothing left // We couldn't find anything, so signal that there's nothing left
// to scavenge. // to scavenge.
s.scavAddr = minScavAddr s.scavAddr = minScavAddr
unlock(s.mheapLock) unlockHeap()
return 0 return 0
} }
......
...@@ -364,12 +364,12 @@ func TestPageAllocScavenge(t *testing.T) { ...@@ -364,12 +364,12 @@ func TestPageAllocScavenge(t *testing.T) {
} }
for name, v := range tests { for name, v := range tests {
v := v v := v
t.Run(name, func(t *testing.T) { runTest := func(t *testing.T, locked bool) {
b := NewPageAlloc(v.beforeAlloc, v.beforeScav) b := NewPageAlloc(v.beforeAlloc, v.beforeScav)
defer FreePageAlloc(b) defer FreePageAlloc(b)
for iter, h := range v.expect { for iter, h := range v.expect {
if got := b.Scavenge(h.request); got != h.expect { if got := b.Scavenge(h.request, locked); got != h.expect {
t.Fatalf("bad scavenge #%d: want %d, got %d", iter+1, h.expect, got) t.Fatalf("bad scavenge #%d: want %d, got %d", iter+1, h.expect, got)
} }
} }
...@@ -377,6 +377,12 @@ func TestPageAllocScavenge(t *testing.T) { ...@@ -377,6 +377,12 @@ func TestPageAllocScavenge(t *testing.T) {
defer FreePageAlloc(want) defer FreePageAlloc(want)
checkPageAlloc(t, want, b) checkPageAlloc(t, want, b)
}
t.Run(name, func(t *testing.T) {
runTest(t, false)
})
t.Run(name+"Locked", func(t *testing.T) {
runTest(t, true)
}) })
} }
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment