Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

selftests/rseq: Implement time slice extension test

Provide an initial test case to evaluate the functionality. This needs to be
extended to cover the ABI violations and expose the race condition between
observing granted and arriving in rseq_slice_yield().

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://patch.msgid.link/20251215155709.320325431@linutronix.de

authored by

Thomas Gleixner and committed by
Peter Zijlstra
830969e7 3c78aaec

+251 -1
+1
tools/testing/selftests/rseq/.gitignore
··· 10 10 param_test_mm_cid_benchmark 11 11 param_test_mm_cid_compare_twice 12 12 syscall_errors_test 13 + slice_test
+4 -1
tools/testing/selftests/rseq/Makefile
··· 17 17 TEST_GEN_PROGS = basic_test basic_percpu_ops_test basic_percpu_ops_mm_cid_test param_test \ 18 18 param_test_benchmark param_test_compare_twice param_test_mm_cid \ 19 19 param_test_mm_cid_benchmark param_test_mm_cid_compare_twice \ 20 - syscall_errors_test 20 + syscall_errors_test slice_test 21 21 22 22 TEST_GEN_PROGS_EXTENDED = librseq.so 23 23 ··· 58 58 59 59 $(OUTPUT)/syscall_errors_test: syscall_errors_test.c $(TEST_GEN_PROGS_EXTENDED) \ 60 60 rseq.h rseq-*.h 61 + $(CC) $(CFLAGS) $< $(LDLIBS) -lrseq -o $@ 62 + 63 + $(OUTPUT)/slice_test: slice_test.c $(TEST_GEN_PROGS_EXTENDED) rseq.h rseq-*.h 61 64 $(CC) $(CFLAGS) $< $(LDLIBS) -lrseq -o $@
+27
tools/testing/selftests/rseq/rseq-abi.h
··· 53 53 __u64 abort_ip; 54 54 } __attribute__((aligned(4 * sizeof(__u64)))); 55 55 56 + /** 57 + * rseq_abi_slice_ctrl - Time slice extension control structure 58 + * @all: Compound value 59 + * @request: Request for a time slice extension 60 + * @granted: Granted time slice extension 61 + * 62 + * @request is set by user space and can be cleared by user space or kernel 63 + * space. @granted is set and cleared by the kernel and must only be read 64 + * by user space. 65 + */ 66 + struct rseq_abi_slice_ctrl { 67 + union { 68 + __u32 all; 69 + struct { 70 + __u8 request; 71 + __u8 granted; 72 + __u16 __reserved; 73 + }; 74 + }; 75 + }; 76 + 56 77 /* 57 78 * struct rseq_abi is aligned on 4 * 8 bytes to ensure it is always 58 79 * contained within a single cache-line. ··· 184 163 * (allocated uniquely within a memory map). 185 164 */ 186 165 __u32 mm_cid; 166 + 167 + /* 168 + * Time slice extension control structure. CPU local updates from 169 + * kernel and user space. 170 + */ 171 + struct rseq_abi_slice_ctrl slice_ctrl; 187 172 188 173 /* 189 174 * Flexible array member at end of structure, after last feature field.
+219
tools/testing/selftests/rseq/slice_test.c
··· 1 + // SPDX-License-Identifier: LGPL-2.1 2 + #define _GNU_SOURCE 3 + #include <assert.h> 4 + #include <pthread.h> 5 + #include <sched.h> 6 + #include <signal.h> 7 + #include <stdbool.h> 8 + #include <stdio.h> 9 + #include <string.h> 10 + #include <syscall.h> 11 + #include <unistd.h> 12 + 13 + #include <linux/prctl.h> 14 + #include <sys/prctl.h> 15 + #include <sys/time.h> 16 + 17 + #include "rseq.h" 18 + 19 + #include "../kselftest_harness.h" 20 + 21 + #ifndef __NR_rseq_slice_yield 22 + # define __NR_rseq_slice_yield 471 23 + #endif 24 + 25 + #define BITS_PER_INT 32 26 + #define BITS_PER_BYTE 8 27 + 28 + #ifndef PR_RSEQ_SLICE_EXTENSION 29 + # define PR_RSEQ_SLICE_EXTENSION 79 30 + # define PR_RSEQ_SLICE_EXTENSION_GET 1 31 + # define PR_RSEQ_SLICE_EXTENSION_SET 2 32 + # define PR_RSEQ_SLICE_EXT_ENABLE 0x01 33 + #endif 34 + 35 + #ifndef RSEQ_SLICE_EXT_REQUEST_BIT 36 + # define RSEQ_SLICE_EXT_REQUEST_BIT 0 37 + # define RSEQ_SLICE_EXT_GRANTED_BIT 1 38 + #endif 39 + 40 + #ifndef asm_inline 41 + # define asm_inline asm __inline 42 + #endif 43 + 44 + #define NSEC_PER_SEC 1000000000L 45 + #define NSEC_PER_USEC 1000L 46 + 47 + struct noise_params { 48 + int64_t noise_nsecs; 49 + int64_t sleep_nsecs; 50 + int64_t run; 51 + }; 52 + 53 + FIXTURE(slice_ext) 54 + { 55 + pthread_t noise_thread; 56 + struct noise_params noise_params; 57 + }; 58 + 59 + FIXTURE_VARIANT(slice_ext) 60 + { 61 + int64_t total_nsecs; 62 + int64_t slice_nsecs; 63 + int64_t noise_nsecs; 64 + int64_t sleep_nsecs; 65 + bool no_yield; 66 + }; 67 + 68 + FIXTURE_VARIANT_ADD(slice_ext, n2_2_50) 69 + { 70 + .total_nsecs = 5LL * NSEC_PER_SEC, 71 + .slice_nsecs = 2LL * NSEC_PER_USEC, 72 + .noise_nsecs = 2LL * NSEC_PER_USEC, 73 + .sleep_nsecs = 50LL * NSEC_PER_USEC, 74 + }; 75 + 76 + FIXTURE_VARIANT_ADD(slice_ext, n50_2_50) 77 + { 78 + .total_nsecs = 5LL * NSEC_PER_SEC, 79 + .slice_nsecs = 50LL * NSEC_PER_USEC, 80 + .noise_nsecs = 2LL * NSEC_PER_USEC, 81 + .sleep_nsecs = 50LL * NSEC_PER_USEC, 82 + }; 83 + 84 + FIXTURE_VARIANT_ADD(slice_ext, n2_2_50_no_yield) 85 + { 86 + .total_nsecs = 5LL * NSEC_PER_SEC, 87 + .slice_nsecs = 2LL * NSEC_PER_USEC, 88 + .noise_nsecs = 2LL * NSEC_PER_USEC, 89 + .sleep_nsecs = 50LL * NSEC_PER_USEC, 90 + .no_yield = true, 91 + }; 92 + 93 + 94 + static inline bool elapsed(struct timespec *start, struct timespec *now, 95 + int64_t span) 96 + { 97 + int64_t delta = now->tv_sec - start->tv_sec; 98 + 99 + delta *= NSEC_PER_SEC; 100 + delta += now->tv_nsec - start->tv_nsec; 101 + return delta >= span; 102 + } 103 + 104 + static void *noise_thread(void *arg) 105 + { 106 + struct noise_params *p = arg; 107 + 108 + while (RSEQ_READ_ONCE(p->run)) { 109 + struct timespec ts_start, ts_now; 110 + 111 + clock_gettime(CLOCK_MONOTONIC, &ts_start); 112 + do { 113 + clock_gettime(CLOCK_MONOTONIC, &ts_now); 114 + } while (!elapsed(&ts_start, &ts_now, p->noise_nsecs)); 115 + 116 + ts_start.tv_sec = 0; 117 + ts_start.tv_nsec = p->sleep_nsecs; 118 + clock_nanosleep(CLOCK_MONOTONIC, 0, &ts_start, NULL); 119 + } 120 + return NULL; 121 + } 122 + 123 + FIXTURE_SETUP(slice_ext) 124 + { 125 + cpu_set_t affinity; 126 + 127 + ASSERT_EQ(sched_getaffinity(0, sizeof(affinity), &affinity), 0); 128 + 129 + /* Pin it on a single CPU. Avoid CPU 0 */ 130 + for (int i = 1; i < CPU_SETSIZE; i++) { 131 + if (!CPU_ISSET(i, &affinity)) 132 + continue; 133 + 134 + CPU_ZERO(&affinity); 135 + CPU_SET(i, &affinity); 136 + ASSERT_EQ(sched_setaffinity(0, sizeof(affinity), &affinity), 0); 137 + break; 138 + } 139 + 140 + ASSERT_EQ(rseq_register_current_thread(), 0); 141 + 142 + ASSERT_EQ(prctl(PR_RSEQ_SLICE_EXTENSION, PR_RSEQ_SLICE_EXTENSION_SET, 143 + PR_RSEQ_SLICE_EXT_ENABLE, 0, 0), 0); 144 + 145 + self->noise_params.noise_nsecs = variant->noise_nsecs; 146 + self->noise_params.sleep_nsecs = variant->sleep_nsecs; 147 + self->noise_params.run = 1; 148 + 149 + ASSERT_EQ(pthread_create(&self->noise_thread, NULL, noise_thread, &self->noise_params), 0); 150 + } 151 + 152 + FIXTURE_TEARDOWN(slice_ext) 153 + { 154 + self->noise_params.run = 0; 155 + pthread_join(self->noise_thread, NULL); 156 + } 157 + 158 + TEST_F(slice_ext, slice_test) 159 + { 160 + unsigned long success = 0, yielded = 0, scheduled = 0, raced = 0; 161 + unsigned long total = 0, aborted = 0; 162 + struct rseq_abi *rs = rseq_get_abi(); 163 + struct timespec ts_start, ts_now; 164 + 165 + ASSERT_NE(rs, NULL); 166 + 167 + clock_gettime(CLOCK_MONOTONIC, &ts_start); 168 + do { 169 + struct timespec ts_cs; 170 + bool req = false; 171 + 172 + clock_gettime(CLOCK_MONOTONIC, &ts_cs); 173 + 174 + total++; 175 + RSEQ_WRITE_ONCE(rs->slice_ctrl.request, 1); 176 + do { 177 + clock_gettime(CLOCK_MONOTONIC, &ts_now); 178 + } while (!elapsed(&ts_cs, &ts_now, variant->slice_nsecs)); 179 + 180 + /* 181 + * request can be cleared unconditionally, but for making 182 + * the stats work this is actually checking it first 183 + */ 184 + if (RSEQ_READ_ONCE(rs->slice_ctrl.request)) { 185 + RSEQ_WRITE_ONCE(rs->slice_ctrl.request, 0); 186 + /* Race between check and clear! */ 187 + req = true; 188 + success++; 189 + } 190 + 191 + if (RSEQ_READ_ONCE(rs->slice_ctrl.granted)) { 192 + /* The above raced against a late grant */ 193 + if (req) 194 + success--; 195 + if (variant->no_yield) { 196 + syscall(__NR_getpid); 197 + aborted++; 198 + } else { 199 + yielded++; 200 + if (!syscall(__NR_rseq_slice_yield)) 201 + raced++; 202 + } 203 + } else { 204 + if (!req) 205 + scheduled++; 206 + } 207 + 208 + clock_gettime(CLOCK_MONOTONIC, &ts_now); 209 + } while (!elapsed(&ts_start, &ts_now, variant->total_nsecs)); 210 + 211 + printf("# Total %12ld\n", total); 212 + printf("# Success %12ld\n", success); 213 + printf("# Yielded %12ld\n", yielded); 214 + printf("# Aborted %12ld\n", aborted); 215 + printf("# Scheduled %12ld\n", scheduled); 216 + printf("# Raced %12ld\n", raced); 217 + } 218 + 219 + TEST_HARNESS_MAIN