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