Skip to content

Commit eb6f2c2

Browse files
zx2c4gopherbot
authored andcommitted
runtime: use vDSO for getrandom() on linux
Linux 6.11 supports calling getrandom() from the vDSO. It operates on a thread-local opaque state allocated with mmap using flags specified by the vDSO. Opaque states are allocated in chunks, ideally ncpu at a time as a hint, rounding up to as many fit in a complete page. On first use, a state is assigned to an m, which owns that state, until the m exits, at which point it is given back to the pool. Performance appears to be quite good: │ sec/op │ sec/op vs base │ Read/4-16 222.45n ± 3% 27.13n ± 6% -87.80% (p=0.000 n=10) │ B/s │ B/s vs base │ Read/4-16 17.15Mi ± 3% 140.61Mi ± 6% +719.82% (p=0.000 n=10) Fixes #69577. Change-Id: Ib6f44e8f2f3940c94d970eaada0eb566ec297dc7 Reviewed-on: https://go-review.googlesource.com/c/go/+/614835 Reviewed-by: Filippo Valsorda <[email protected]> Reviewed-by: Cuong Manh Le <[email protected]> Auto-Submit: Jason Donenfeld <[email protected]> Reviewed-by: Paul Murphy <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: David Chase <[email protected]> Reviewed-by: Michael Pratt <[email protected]>
1 parent 677b6cc commit eb6f2c2

15 files changed

+377
-8
lines changed

src/crypto/rand/rand_test.go

+3
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ func TestReadEmpty(t *testing.T) {
4343
}
4444

4545
func BenchmarkRead(b *testing.B) {
46+
b.Run("4", func(b *testing.B) {
47+
benchmarkRead(b, 4)
48+
})
4649
b.Run("32", func(b *testing.B) {
4750
benchmarkRead(b, 32)
4851
})

src/internal/syscall/unix/getrandom.go

+10
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,23 @@ import (
1212
"unsafe"
1313
)
1414

15+
//go:linkname vgetrandom runtime.vgetrandom
16+
func vgetrandom(p []byte, flags uint32) (ret int, supported bool)
17+
1518
var getrandomUnsupported atomic.Bool
1619

1720
// GetRandomFlag is a flag supported by the getrandom system call.
1821
type GetRandomFlag uintptr
1922

2023
// GetRandom calls the getrandom system call.
2124
func GetRandom(p []byte, flags GetRandomFlag) (n int, err error) {
25+
ret, supported := vgetrandom(p, uint32(flags))
26+
if supported {
27+
if ret < 0 {
28+
return 0, syscall.Errno(-ret)
29+
}
30+
return ret, nil
31+
}
2232
if getrandomUnsupported.Load() {
2333
return 0, syscall.ENOSYS
2434
}

src/runtime/os_linux.go

+9
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ type mOS struct {
3131
// needPerThreadSyscall indicates that a per-thread syscall is required
3232
// for doAllThreadsSyscall.
3333
needPerThreadSyscall atomic.Uint8
34+
35+
// This is a pointer to a chunk of memory allocated with a special
36+
// mmap invocation in vgetrandomGetState().
37+
vgetrandomState uintptr
3438
}
3539

3640
//go:noescape
@@ -344,6 +348,7 @@ func osinit() {
344348
ncpu = getproccount()
345349
physHugePageSize = getHugePageSize()
346350
osArchInit()
351+
vgetrandomInit()
347352
}
348353

349354
var urandom_dev = []byte("/dev/urandom\x00")
@@ -400,6 +405,10 @@ func unminit() {
400405
// Called from exitm, but not from drop, to undo the effect of thread-owned
401406
// resources in minit, semacreate, or elsewhere. Do not take locks after calling this.
402407
func mdestroy(mp *m) {
408+
if mp.vgetrandomState != 0 {
409+
vgetrandomPutState(mp.vgetrandomState)
410+
mp.vgetrandomState = 0
411+
}
403412
}
404413

405414
// #ifdef GOARCH_386

src/runtime/sys_linux_amd64.s

+33
Original file line numberDiff line numberDiff line change
@@ -704,3 +704,36 @@ TEXT runtime·sbrk0(SB),NOSPLIT,$0-8
704704
SYSCALL
705705
MOVQ AX, ret+0(FP)
706706
RET
707+
708+
// func vgetrandom1(buf *byte, length uintptr, flags uint32, state uintptr, stateSize uintptr) int
709+
TEXT runtime·vgetrandom1<ABIInternal>(SB),NOSPLIT,$16-48
710+
MOVQ SI, R8 // stateSize
711+
MOVL CX, DX // flags
712+
MOVQ DI, CX // state
713+
MOVQ BX, SI // length
714+
MOVQ AX, DI // buf
715+
716+
MOVQ SP, R12
717+
718+
MOVQ runtime·vdsoGetrandomSym(SB), AX
719+
MOVQ g_m(R14), BX
720+
721+
MOVQ m_vdsoPC(BX), R9
722+
MOVQ R9, 0(SP)
723+
MOVQ m_vdsoSP(BX), R9
724+
MOVQ R9, 8(SP)
725+
LEAQ buf+0(FP), R9
726+
MOVQ R9, m_vdsoSP(BX)
727+
MOVQ -8(R9), R9
728+
MOVQ R9, m_vdsoPC(BX)
729+
730+
ANDQ $~15, SP
731+
732+
CALL AX
733+
734+
MOVQ R12, SP
735+
MOVQ 8(SP), R9
736+
MOVQ R9, m_vdsoSP(BX)
737+
MOVQ 0(SP), R9
738+
MOVQ R9, m_vdsoPC(BX)
739+
RET

src/runtime/sys_linux_arm64.s

+45
Original file line numberDiff line numberDiff line change
@@ -785,3 +785,48 @@ TEXT runtime·sbrk0(SB),NOSPLIT,$0-8
785785
SVC
786786
MOVD R0, ret+0(FP)
787787
RET
788+
789+
// func vgetrandom1(buf *byte, length uintptr, flags uint32, state uintptr, stateSize uintptr) int
790+
TEXT runtime·vgetrandom1<ABIInternal>(SB),NOSPLIT,$16-48
791+
MOVD RSP, R20
792+
793+
MOVD runtime·vdsoGetrandomSym(SB), R8
794+
MOVD g_m(g), R21
795+
796+
MOVD m_vdsoPC(R21), R9
797+
MOVD R9, 8(RSP)
798+
MOVD m_vdsoSP(R21), R9
799+
MOVD R9, 16(RSP)
800+
MOVD LR, m_vdsoPC(R21)
801+
MOVD $buf-8(FP), R9
802+
MOVD R9, m_vdsoSP(R21)
803+
804+
MOVD RSP, R9
805+
BIC $15, R9
806+
MOVD R9, RSP
807+
808+
MOVBU runtime·iscgo(SB), R9
809+
CBNZ R9, nosaveg
810+
MOVD m_gsignal(R21), R9
811+
CBZ R9, nosaveg
812+
CMP g, R9
813+
BEQ nosaveg
814+
MOVD (g_stack+stack_lo)(R9), R22
815+
MOVD g, (R22)
816+
817+
BL (R8)
818+
819+
MOVD ZR, (R22)
820+
B restore
821+
822+
nosaveg:
823+
BL (R8)
824+
825+
restore:
826+
MOVD R20, RSP
827+
MOVD 16(RSP), R1
828+
MOVD R1, m_vdsoSP(R21)
829+
MOVD 8(RSP), R1
830+
MOVD R1, m_vdsoPC(R21)
831+
NOP R0 // Satisfy go vet, since the return value comes from the vDSO function.
832+
RET

src/runtime/sys_linux_loong64.s

+43
Original file line numberDiff line numberDiff line change
@@ -657,3 +657,46 @@ TEXT runtime·socket(SB),$0-20
657657
MOVV R0, 2(R0) // unimplemented, only needed for android; declared in stubs_linux.go
658658
MOVW R0, ret+16(FP) // for vet
659659
RET
660+
661+
// func vgetrandom1(buf *byte, length uintptr, flags uint32, state uintptr, stateSize uintptr) int
662+
TEXT runtime·vgetrandom1<ABIInternal>(SB),NOSPLIT,$16-48
663+
MOVV R3, R23
664+
665+
MOVV runtime·vdsoGetrandomSym(SB), R12
666+
667+
MOVV g_m(g), R24
668+
669+
MOVV m_vdsoPC(R24), R13
670+
MOVV R13, 8(R3)
671+
MOVV m_vdsoSP(R24), R13
672+
MOVV R13, 16(R3)
673+
MOVV R1, m_vdsoPC(R24)
674+
MOVV $buf-8(FP), R13
675+
MOVV R13, m_vdsoSP(R24)
676+
677+
AND $~15, R3
678+
679+
MOVBU runtime·iscgo(SB), R13
680+
BNE R13, nosaveg
681+
MOVV m_gsignal(R24), R13
682+
BEQ R13, nosaveg
683+
BEQ g, R13, nosaveg
684+
MOVV (g_stack+stack_lo)(R13), R25
685+
MOVV g, (R25)
686+
687+
JAL (R12)
688+
689+
MOVV R0, (R25)
690+
JMP restore
691+
692+
nosaveg:
693+
JAL (R12)
694+
695+
restore:
696+
MOVV R23, R3
697+
MOVV 16(R3), R25
698+
MOVV R25, m_vdsoSP(R24)
699+
MOVV 8(R3), R25
700+
MOVV R25, m_vdsoPC(R24)
701+
NOP R4 // Satisfy go vet, since the return value comes from the vDSO function.
702+
RET

src/runtime/sys_linux_ppc64x.s

+50
Original file line numberDiff line numberDiff line change
@@ -757,3 +757,53 @@ TEXT runtime·socket(SB),$0-20
757757
MOVD R0, 0(R0) // unimplemented, only needed for android; declared in stubs_linux.go
758758
MOVW R0, ret+16(FP) // for vet
759759
RET
760+
761+
// func vgetrandom1(buf *byte, length uintptr, flags uint32, state uintptr, stateSize uintptr) int
762+
TEXT runtime·vgetrandom1<ABIInternal>(SB),NOSPLIT,$16-48
763+
MOVD R1, R15
764+
765+
MOVD runtime·vdsoGetrandomSym(SB), R12
766+
MOVD R12, CTR
767+
MOVD g_m(g), R21
768+
769+
MOVD m_vdsoPC(R21), R22
770+
MOVD R22, 32(R1)
771+
MOVD m_vdsoSP(R21), R22
772+
MOVD R22, 40(R1)
773+
MOVD LR, m_vdsoPC(R21)
774+
MOVD $buf-FIXED_FRAME(FP), R22
775+
MOVD R22, m_vdsoSP(R21)
776+
777+
RLDICR $0, R1, $59, R1
778+
779+
MOVBZ runtime·iscgo(SB), R22
780+
CMP R22, $0
781+
BNE nosaveg
782+
MOVD m_gsignal(R21), R22
783+
CMP R22, $0
784+
BEQ nosaveg
785+
CMP R22, g
786+
BEQ nosaveg
787+
MOVD (g_stack+stack_lo)(R22), R22
788+
MOVD g, (R22)
789+
790+
BL (CTR)
791+
792+
MOVD $0, (R22)
793+
JMP restore
794+
795+
nosaveg:
796+
BL (CTR)
797+
798+
restore:
799+
MOVD $0, R0
800+
MOVD R15, R1
801+
MOVD 40(R1), R22
802+
MOVD R22, m_vdsoSP(R21)
803+
MOVD 32(R1), R22
804+
MOVD R22, m_vdsoPC(R21)
805+
806+
BVC out
807+
NEG R3, R3
808+
out:
809+
RET

src/runtime/sys_linux_s390x.s

+50
Original file line numberDiff line numberDiff line change
@@ -604,3 +604,53 @@ TEXT runtime·socket(SB),$0-20
604604
MOVD $0, 2(R0) // unimplemented, only needed for android; declared in stubs_linux.go
605605
MOVW R0, ret+16(FP)
606606
RET
607+
608+
// func vgetrandom1(buf *byte, length uintptr, flags uint32, state uintptr, stateSize uintptr) int
609+
TEXT runtime·vgetrandom1(SB),NOSPLIT,$16-48
610+
MOVD buf+0(FP), R2
611+
MOVD length+8(FP), R3
612+
MOVW flags+16(FP), R4
613+
MOVD state+24(FP), R5
614+
MOVD stateSize+32(FP), R6
615+
616+
MOVD R15, R7
617+
618+
MOVD runtime·vdsoGetrandomSym(SB), R1
619+
MOVD g_m(g), R9
620+
621+
MOVD m_vdsoPC(R9), R12
622+
MOVD R12, 8(R15)
623+
MOVD m_vdsoSP(R9), R12
624+
MOVD R12, 16(R15)
625+
MOVD R14, m_vdsoPC(R9)
626+
MOVD $buf+0(FP), R12
627+
MOVD R12, m_vdsoSP(R9)
628+
629+
SUB $160, R15
630+
MOVD $~7, R12
631+
AND R12, R15
632+
633+
MOVB runtime·iscgo(SB), R12
634+
CMPBNE R12, $0, nosaveg
635+
MOVD m_gsignal(R9), R12
636+
CMPBEQ R12, $0, nosaveg
637+
CMPBEQ g, R12, nosaveg
638+
MOVD (g_stack+stack_lo)(R12), R12
639+
MOVD g, (R12)
640+
641+
BL R1
642+
643+
MOVD $0, (R12)
644+
JMP restore
645+
646+
nosaveg:
647+
BL R1
648+
649+
restore:
650+
MOVD R7, R15
651+
MOVD 16(R15), R12
652+
MOVD R12, m_vdsoSP(R9)
653+
MOVD 8(R15), R12
654+
MOVD R12, m_vdsoPC(R9)
655+
MOVD R2, ret+40(FP)
656+
RET

src/runtime/vdso_linux_amd64.go

+2
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,13 @@ var vdsoLinuxVersion = vdsoVersionKey{"LINUX_2.6", 0x3ae75f6}
1717
var vdsoSymbolKeys = []vdsoSymbolKey{
1818
{"__vdso_gettimeofday", 0x315ca59, 0xb01bca00, &vdsoGettimeofdaySym},
1919
{"__vdso_clock_gettime", 0xd35ec75, 0x6e43a318, &vdsoClockgettimeSym},
20+
{"__vdso_getrandom", 0x25425d, 0x84a559bf, &vdsoGetrandomSym},
2021
}
2122

2223
var (
2324
vdsoGettimeofdaySym uintptr
2425
vdsoClockgettimeSym uintptr
26+
vdsoGetrandomSym uintptr
2527
)
2628

2729
// vdsoGettimeofdaySym is accessed from the syscall package.

src/runtime/vdso_linux_arm64.go

+5-2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ var vdsoLinuxVersion = vdsoVersionKey{"LINUX_2.6.39", 0x75fcb89}
1515

1616
var vdsoSymbolKeys = []vdsoSymbolKey{
1717
{"__kernel_clock_gettime", 0xb0cd725, 0xdfa941fd, &vdsoClockgettimeSym},
18+
{"__kernel_getrandom", 0x9800c0d, 0x540d4e24, &vdsoGetrandomSym},
1819
}
1920

20-
// initialize to fall back to syscall
21-
var vdsoClockgettimeSym uintptr = 0
21+
var (
22+
vdsoClockgettimeSym uintptr
23+
vdsoGetrandomSym uintptr
24+
)

src/runtime/vdso_linux_loong64.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,10 @@ var vdsoLinuxVersion = vdsoVersionKey{"LINUX_5.10", 0xae78f70}
1919

2020
var vdsoSymbolKeys = []vdsoSymbolKey{
2121
{"__vdso_clock_gettime", 0xd35ec75, 0x6e43a318, &vdsoClockgettimeSym},
22+
{"__vdso_getrandom", 0x25425d, 0x84a559bf, &vdsoGetrandomSym},
2223
}
2324

24-
// initialize to fall back to syscall
2525
var (
26-
vdsoClockgettimeSym uintptr = 0
26+
vdsoClockgettimeSym uintptr
27+
vdsoGetrandomSym uintptr
2728
)

src/runtime/vdso_linux_ppc64x.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@ var vdsoLinuxVersion = vdsoVersionKey{"LINUX_2.6.15", 0x75fcba5}
1616

1717
var vdsoSymbolKeys = []vdsoSymbolKey{
1818
{"__kernel_clock_gettime", 0xb0cd725, 0xdfa941fd, &vdsoClockgettimeSym},
19+
{"__kernel_getrandom", 0x9800c0d, 0x540d4e24, &vdsoGetrandomSym},
1920
}
2021

21-
// initialize with vsyscall fallbacks
2222
var (
23-
vdsoClockgettimeSym uintptr = 0
23+
vdsoClockgettimeSym uintptr
24+
vdsoGetrandomSym uintptr
2425
)

src/runtime/vdso_linux_s390x.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@ var vdsoLinuxVersion = vdsoVersionKey{"LINUX_2.6.29", 0x75fcbb9}
1616

1717
var vdsoSymbolKeys = []vdsoSymbolKey{
1818
{"__kernel_clock_gettime", 0xb0cd725, 0xdfa941fd, &vdsoClockgettimeSym},
19+
{"__kernel_getrandom", 0x9800c0d, 0x540d4e24, &vdsoGetrandomSym},
1920
}
2021

21-
// initialize with vsyscall fallbacks
2222
var (
23-
vdsoClockgettimeSym uintptr = 0
23+
vdsoClockgettimeSym uintptr
24+
vdsoGetrandomSym uintptr
2425
)

0 commit comments

Comments
 (0)