[release-branch.go1.24] runtime: cleanup M vgetrandom state before dropping P

When an M is destroyed, we put its vgetrandom state back on the shared
list for another M to reuse. This list is simply a slice, so appending
to the slice may allocate. Currently this operation is performed in
mdestroy, after the P is released, meaning allocation is not allowed.

More the cleanup earlier in mdestroy when allocation is still OK.

Also add //go:nowritebarrierrec to mdestroy since it runs without a P,
which would have caught this bug.

Fixes #73144.
For #73141.

Change-Id: I6a6a636c3fbf5c6eec09d07a260e39dbb4d2db12
Reviewed-on: https://go-review.googlesource.com/c/go/+/662455
Reviewed-by: Jason Donenfeld <Jason@zx2c4.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Keith Randall <khr@golang.org>
Reviewed-by: Keith Randall <khr@google.com>
(cherry picked from commit 0b31e6d4cc804ab76ae8ced151ee2f50657aec14)
Reviewed-on: https://go-review.googlesource.com/c/go/+/662496
This commit is contained in:
Michael Pratt 2025-04-03 03:26:25 +00:00 committed by Carlos Amedee
parent 56eb99859d
commit 0ab64e2caa
12 changed files with 48 additions and 16 deletions

View File

@ -234,8 +234,11 @@ func unminit() {
getg().m.procid = 0 getg().m.procid = 0
} }
// Called from exitm, but not from drop, to undo the effect of thread-owned // Called from mexit, but not from dropm, to undo the effect of thread-owned
// resources in minit, semacreate, or elsewhere. Do not take locks after calling this. // resources in minit, semacreate, or elsewhere. Do not take locks after calling this.
//
// This always runs without a P, so //go:nowritebarrierrec is required.
//go:nowritebarrierrec
func mdestroy(mp *m) { func mdestroy(mp *m) {
} }

View File

@ -186,8 +186,11 @@ func unminit() {
getg().m.procid = 0 getg().m.procid = 0
} }
// Called from exitm, but not from drop, to undo the effect of thread-owned // Called from mexit, but not from dropm, to undo the effect of thread-owned
// resources in minit, semacreate, or elsewhere. Do not take locks after calling this. // resources in minit, semacreate, or elsewhere. Do not take locks after calling this.
//
// This always runs without a P, so //go:nowritebarrierrec is required.
//go:nowritebarrierrec
func mdestroy(mp *m) { func mdestroy(mp *m) {
} }

View File

@ -344,8 +344,11 @@ func unminit() {
getg().m.procid = 0 getg().m.procid = 0
} }
// Called from exitm, but not from drop, to undo the effect of thread-owned // Called from mexit, but not from dropm, to undo the effect of thread-owned
// resources in minit, semacreate, or elsewhere. Do not take locks after calling this. // resources in minit, semacreate, or elsewhere. Do not take locks after calling this.
//
// This always runs without a P, so //go:nowritebarrierrec is required.
//go:nowritebarrierrec
func mdestroy(mp *m) { func mdestroy(mp *m) {
} }

View File

@ -216,8 +216,11 @@ func unminit() {
getg().m.procid = 0 getg().m.procid = 0
} }
// Called from exitm, but not from drop, to undo the effect of thread-owned // Called from mexit, but not from dropm, to undo the effect of thread-owned
// resources in minit, semacreate, or elsewhere. Do not take locks after calling this. // resources in minit, semacreate, or elsewhere. Do not take locks after calling this.
//
// This always runs without a P, so //go:nowritebarrierrec is required.
//go:nowritebarrierrec
func mdestroy(mp *m) { func mdestroy(mp *m) {
} }

View File

@ -412,13 +412,12 @@ func unminit() {
getg().m.procid = 0 getg().m.procid = 0
} }
// Called from exitm, but not from drop, to undo the effect of thread-owned // Called from mexit, but not from dropm, to undo the effect of thread-owned
// resources in minit, semacreate, or elsewhere. Do not take locks after calling this. // resources in minit, semacreate, or elsewhere. Do not take locks after calling this.
//
// This always runs without a P, so //go:nowritebarrierrec is required.
//go:nowritebarrierrec
func mdestroy(mp *m) { func mdestroy(mp *m) {
if mp.vgetrandomState != 0 {
vgetrandomPutState(mp.vgetrandomState)
mp.vgetrandomState = 0
}
} }
// #ifdef GOARCH_386 // #ifdef GOARCH_386

View File

@ -320,8 +320,11 @@ func unminit() {
// must continue working after unminit. // must continue working after unminit.
} }
// Called from exitm, but not from drop, to undo the effect of thread-owned // Called from mexit, but not from dropm, to undo the effect of thread-owned
// resources in minit, semacreate, or elsewhere. Do not take locks after calling this. // resources in minit, semacreate, or elsewhere. Do not take locks after calling this.
//
// This always runs without a P, so //go:nowritebarrierrec is required.
//go:nowritebarrierrec
func mdestroy(mp *m) { func mdestroy(mp *m) {
} }

View File

@ -182,8 +182,11 @@ func unminit() {
getg().m.procid = 0 getg().m.procid = 0
} }
// Called from exitm, but not from drop, to undo the effect of thread-owned // Called from mexit, but not from dropm, to undo the effect of thread-owned
// resources in minit, semacreate, or elsewhere. Do not take locks after calling this. // resources in minit, semacreate, or elsewhere. Do not take locks after calling this.
//
// This always runs without a P, so //go:nowritebarrierrec is required.
//go:nowritebarrierrec
func mdestroy(mp *m) { func mdestroy(mp *m) {
} }

View File

@ -217,8 +217,11 @@ func minit() {
func unminit() { func unminit() {
} }
// Called from exitm, but not from drop, to undo the effect of thread-owned // Called from mexit, but not from dropm, to undo the effect of thread-owned
// resources in minit, semacreate, or elsewhere. Do not take locks after calling this. // resources in minit, semacreate, or elsewhere. Do not take locks after calling this.
//
// This always runs without a P, so //go:nowritebarrierrec is required.
//go:nowritebarrierrec
func mdestroy(mp *m) { func mdestroy(mp *m) {
} }

View File

@ -906,9 +906,11 @@ func unminit() {
mp.procid = 0 mp.procid = 0
} }
// Called from exitm, but not from drop, to undo the effect of thread-owned // Called from mexit, but not from dropm, to undo the effect of thread-owned
// resources in minit, semacreate, or elsewhere. Do not take locks after calling this. // resources in minit, semacreate, or elsewhere. Do not take locks after calling this.
// //
// This always runs without a P, so //go:nowritebarrierrec is required.
//go:nowritebarrierrec
//go:nosplit //go:nosplit
func mdestroy(mp *m) { func mdestroy(mp *m) {
if mp.highResTimer != 0 { if mp.highResTimer != 0 {

View File

@ -1935,6 +1935,9 @@ func mexit(osStack bool) {
mp.gsignal = nil mp.gsignal = nil
} }
// Free vgetrandom state.
vgetrandomDestroy(mp)
// Remove m from allm. // Remove m from allm.
lock(&sched.lock) lock(&sched.lock)
for pprev := &allm; *pprev != nil; pprev = &(*pprev).alllink { for pprev := &allm; *pprev != nil; pprev = &(*pprev).alllink {

View File

@ -73,9 +73,16 @@ func vgetrandomGetState() uintptr {
return state return state
} }
func vgetrandomPutState(state uintptr) { // Free vgetrandom state from the M (if any) prior to destroying the M.
//
// This may allocate, so it must have a P.
func vgetrandomDestroy(mp *m) {
if mp.vgetrandomState == 0 {
return
}
lock(&vgetrandomAlloc.statesLock) lock(&vgetrandomAlloc.statesLock)
vgetrandomAlloc.states = append(vgetrandomAlloc.states, state) vgetrandomAlloc.states = append(vgetrandomAlloc.states, mp.vgetrandomState)
unlock(&vgetrandomAlloc.statesLock) unlock(&vgetrandomAlloc.statesLock)
} }

View File

@ -13,6 +13,6 @@ func vgetrandom(p []byte, flags uint32) (ret int, supported bool) {
return -1, false return -1, false
} }
func vgetrandomPutState(state uintptr) {} func vgetrandomDestroy(mp *m) {}
func vgetrandomInit() {} func vgetrandomInit() {}