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.

at master 219 lines 4.9 kB view raw
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 47struct noise_params { 48 int64_t noise_nsecs; 49 int64_t sleep_nsecs; 50 int64_t run; 51}; 52 53FIXTURE(slice_ext) 54{ 55 pthread_t noise_thread; 56 struct noise_params noise_params; 57}; 58 59FIXTURE_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 68FIXTURE_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 76FIXTURE_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 84FIXTURE_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 94static 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 104static 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 123FIXTURE_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 152FIXTURE_TEARDOWN(slice_ext) 153{ 154 self->noise_params.run = 0; 155 pthread_join(self->noise_thread, NULL); 156} 157 158TEST_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 219TEST_HARNESS_MAIN