mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/chenhuacai/linux-loongson
synced 2025-09-03 17:51:23 +00:00

Add a stress test which runs one more process than we have CPUs spinning through a very recursive function with frequent syscalls immediately prior to return and signals being injected every 100ms. The goal is to flag up any scheduling related issues, for example failure to ensure that barriers are inserted when moving a GCS using task to another CPU. The test runs for a configurable amount of time, defaulting to 10 seconds. Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org> Tested-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org> Signed-off-by: Mark Brown <broonie@kernel.org> Link: https://lore.kernel.org/r/20241001-arm64-gcs-v13-38-222b78d87eee@kernel.org Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
312 lines
5.0 KiB
ArmAsm
312 lines
5.0 KiB
ArmAsm
// Program that loops for ever doing lots of recursions and system calls,
|
|
// intended to be used as part of a stress test for GCS context switching.
|
|
//
|
|
// Copyright 2015-2023 Arm Ltd
|
|
|
|
#include <asm/unistd.h>
|
|
|
|
#define sa_sz 32
|
|
#define sa_flags 8
|
|
#define sa_handler 0
|
|
#define sa_mask_sz 8
|
|
|
|
#define si_code 8
|
|
|
|
#define SIGINT 2
|
|
#define SIGABRT 6
|
|
#define SIGUSR1 10
|
|
#define SIGSEGV 11
|
|
#define SIGUSR2 12
|
|
#define SIGTERM 15
|
|
#define SEGV_CPERR 10
|
|
|
|
#define SA_NODEFER 1073741824
|
|
#define SA_SIGINFO 4
|
|
#define ucontext_regs 184
|
|
|
|
#define PR_SET_SHADOW_STACK_STATUS 75
|
|
# define PR_SHADOW_STACK_ENABLE (1UL << 0)
|
|
|
|
#define GCSPR_EL0 S3_3_C2_C5_1
|
|
|
|
.macro function name
|
|
.macro endfunction
|
|
.type \name, @function
|
|
.purgem endfunction
|
|
.endm
|
|
\name:
|
|
.endm
|
|
|
|
// Print a single character x0 to stdout
|
|
// Clobbers x0-x2,x8
|
|
function putc
|
|
str x0, [sp, #-16]!
|
|
|
|
mov x0, #1 // STDOUT_FILENO
|
|
mov x1, sp
|
|
mov x2, #1
|
|
mov x8, #__NR_write
|
|
svc #0
|
|
|
|
add sp, sp, #16
|
|
ret
|
|
endfunction
|
|
.globl putc
|
|
|
|
// Print a NUL-terminated string starting at address x0 to stdout
|
|
// Clobbers x0-x3,x8
|
|
function puts
|
|
mov x1, x0
|
|
|
|
mov x2, #0
|
|
0: ldrb w3, [x0], #1
|
|
cbz w3, 1f
|
|
add x2, x2, #1
|
|
b 0b
|
|
|
|
1: mov w0, #1 // STDOUT_FILENO
|
|
mov x8, #__NR_write
|
|
svc #0
|
|
|
|
ret
|
|
endfunction
|
|
.globl puts
|
|
|
|
// Utility macro to print a literal string
|
|
// Clobbers x0-x4,x8
|
|
.macro puts string
|
|
.pushsection .rodata.str1.1, "aMS", @progbits, 1
|
|
.L__puts_literal\@: .string "\string"
|
|
.popsection
|
|
|
|
ldr x0, =.L__puts_literal\@
|
|
bl puts
|
|
.endm
|
|
|
|
// Print an unsigned decimal number x0 to stdout
|
|
// Clobbers x0-x4,x8
|
|
function putdec
|
|
mov x1, sp
|
|
str x30, [sp, #-32]! // Result can't be > 20 digits
|
|
|
|
mov x2, #0
|
|
strb w2, [x1, #-1]! // Write the NUL terminator
|
|
|
|
mov x2, #10
|
|
0: udiv x3, x0, x2 // div-mod loop to generate the digits
|
|
msub x0, x3, x2, x0
|
|
add w0, w0, #'0'
|
|
strb w0, [x1, #-1]!
|
|
mov x0, x3
|
|
cbnz x3, 0b
|
|
|
|
ldrb w0, [x1]
|
|
cbnz w0, 1f
|
|
mov w0, #'0' // Print "0" for 0, not ""
|
|
strb w0, [x1, #-1]!
|
|
|
|
1: mov x0, x1
|
|
bl puts
|
|
|
|
ldr x30, [sp], #32
|
|
ret
|
|
endfunction
|
|
.globl putdec
|
|
|
|
// Print an unsigned decimal number x0 to stdout, followed by a newline
|
|
// Clobbers x0-x5,x8
|
|
function putdecn
|
|
mov x5, x30
|
|
|
|
bl putdec
|
|
mov x0, #'\n'
|
|
bl putc
|
|
|
|
ret x5
|
|
endfunction
|
|
.globl putdecn
|
|
|
|
// Fill x1 bytes starting at x0 with 0.
|
|
// Clobbers x1, x2.
|
|
function memclr
|
|
mov w2, #0
|
|
endfunction
|
|
.globl memclr
|
|
// fall through to memfill
|
|
|
|
// Trivial memory fill: fill x1 bytes starting at address x0 with byte w2
|
|
// Clobbers x1
|
|
function memfill
|
|
cmp x1, #0
|
|
b.eq 1f
|
|
|
|
0: strb w2, [x0], #1
|
|
subs x1, x1, #1
|
|
b.ne 0b
|
|
|
|
1: ret
|
|
endfunction
|
|
.globl memfill
|
|
|
|
// w0: signal number
|
|
// x1: sa_action
|
|
// w2: sa_flags
|
|
// Clobbers x0-x6,x8
|
|
function setsignal
|
|
str x30, [sp, #-((sa_sz + 15) / 16 * 16 + 16)]!
|
|
|
|
mov w4, w0
|
|
mov x5, x1
|
|
mov w6, w2
|
|
|
|
add x0, sp, #16
|
|
mov x1, #sa_sz
|
|
bl memclr
|
|
|
|
mov w0, w4
|
|
add x1, sp, #16
|
|
str w6, [x1, #sa_flags]
|
|
str x5, [x1, #sa_handler]
|
|
mov x2, #0
|
|
mov x3, #sa_mask_sz
|
|
mov x8, #__NR_rt_sigaction
|
|
svc #0
|
|
|
|
cbz w0, 1f
|
|
|
|
puts "sigaction failure\n"
|
|
b abort
|
|
|
|
1: ldr x30, [sp], #((sa_sz + 15) / 16 * 16 + 16)
|
|
ret
|
|
endfunction
|
|
|
|
|
|
function tickle_handler
|
|
// Perhaps collect GCSPR_EL0 here in future?
|
|
ret
|
|
endfunction
|
|
|
|
function terminate_handler
|
|
mov w21, w0
|
|
mov x20, x2
|
|
|
|
puts "Terminated by signal "
|
|
mov w0, w21
|
|
bl putdec
|
|
puts ", no error\n"
|
|
|
|
mov x0, #0
|
|
mov x8, #__NR_exit
|
|
svc #0
|
|
endfunction
|
|
|
|
function segv_handler
|
|
// stash the siginfo_t *
|
|
mov x20, x1
|
|
|
|
// Disable GCS, we don't want additional faults logging things
|
|
mov x0, PR_SET_SHADOW_STACK_STATUS
|
|
mov x1, xzr
|
|
mov x2, xzr
|
|
mov x3, xzr
|
|
mov x4, xzr
|
|
mov x5, xzr
|
|
mov x8, #__NR_prctl
|
|
svc #0
|
|
|
|
puts "Got SIGSEGV code "
|
|
|
|
ldr x21, [x20, #si_code]
|
|
mov x0, x21
|
|
bl putdec
|
|
|
|
// GCS faults should have si_code SEGV_CPERR
|
|
cmp x21, #SEGV_CPERR
|
|
bne 1f
|
|
|
|
puts " (GCS violation)"
|
|
1:
|
|
mov x0, '\n'
|
|
bl putc
|
|
b abort
|
|
endfunction
|
|
|
|
// Recurse x20 times
|
|
.macro recurse id
|
|
function recurse\id
|
|
stp x29, x30, [sp, #-16]!
|
|
mov x29, sp
|
|
|
|
cmp x20, 0
|
|
beq 1f
|
|
sub x20, x20, 1
|
|
bl recurse\id
|
|
|
|
1:
|
|
ldp x29, x30, [sp], #16
|
|
|
|
// Do a syscall immediately prior to returning to try to provoke
|
|
// scheduling and migration at a point where coherency issues
|
|
// might trigger.
|
|
mov x8, #__NR_getpid
|
|
svc #0
|
|
|
|
ret
|
|
endfunction
|
|
.endm
|
|
|
|
// Generate and use two copies so we're changing the GCS contents
|
|
recurse 1
|
|
recurse 2
|
|
|
|
.globl _start
|
|
function _start
|
|
// Run with GCS
|
|
mov x0, PR_SET_SHADOW_STACK_STATUS
|
|
mov x1, PR_SHADOW_STACK_ENABLE
|
|
mov x2, xzr
|
|
mov x3, xzr
|
|
mov x4, xzr
|
|
mov x5, xzr
|
|
mov x8, #__NR_prctl
|
|
svc #0
|
|
cbz x0, 1f
|
|
puts "Failed to enable GCS\n"
|
|
b abort
|
|
1:
|
|
|
|
mov w0, #SIGTERM
|
|
adr x1, terminate_handler
|
|
mov w2, #SA_SIGINFO
|
|
bl setsignal
|
|
|
|
mov w0, #SIGUSR1
|
|
adr x1, tickle_handler
|
|
mov w2, #SA_SIGINFO
|
|
orr w2, w2, #SA_NODEFER
|
|
bl setsignal
|
|
|
|
mov w0, #SIGSEGV
|
|
adr x1, segv_handler
|
|
mov w2, #SA_SIGINFO
|
|
orr w2, w2, #SA_NODEFER
|
|
bl setsignal
|
|
|
|
puts "Running\n"
|
|
|
|
loop:
|
|
// Small recursion depth so we're frequently flipping between
|
|
// the two recursors and changing what's on the stack
|
|
mov x20, #5
|
|
bl recurse1
|
|
mov x20, #5
|
|
bl recurse2
|
|
b loop
|
|
endfunction
|
|
|
|
abort:
|
|
mov x0, #255
|
|
mov x8, #__NR_exit
|
|
svc #0
|