| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219 |
- // SPDX-License-Identifier: LGPL-2.1
- #define _GNU_SOURCE
- #include <assert.h>
- #include <pthread.h>
- #include <sched.h>
- #include <signal.h>
- #include <stdbool.h>
- #include <stdio.h>
- #include <string.h>
- #include <syscall.h>
- #include <unistd.h>
- #include <linux/prctl.h>
- #include <sys/prctl.h>
- #include <sys/time.h>
- #include "rseq.h"
- #include "../kselftest_harness.h"
- #ifndef __NR_rseq_slice_yield
- # define __NR_rseq_slice_yield 471
- #endif
- #define BITS_PER_INT 32
- #define BITS_PER_BYTE 8
- #ifndef PR_RSEQ_SLICE_EXTENSION
- # define PR_RSEQ_SLICE_EXTENSION 79
- # define PR_RSEQ_SLICE_EXTENSION_GET 1
- # define PR_RSEQ_SLICE_EXTENSION_SET 2
- # define PR_RSEQ_SLICE_EXT_ENABLE 0x01
- #endif
- #ifndef RSEQ_SLICE_EXT_REQUEST_BIT
- # define RSEQ_SLICE_EXT_REQUEST_BIT 0
- # define RSEQ_SLICE_EXT_GRANTED_BIT 1
- #endif
- #ifndef asm_inline
- # define asm_inline asm __inline
- #endif
- #define NSEC_PER_SEC 1000000000L
- #define NSEC_PER_USEC 1000L
- struct noise_params {
- int64_t noise_nsecs;
- int64_t sleep_nsecs;
- int64_t run;
- };
- FIXTURE(slice_ext)
- {
- pthread_t noise_thread;
- struct noise_params noise_params;
- };
- FIXTURE_VARIANT(slice_ext)
- {
- int64_t total_nsecs;
- int64_t slice_nsecs;
- int64_t noise_nsecs;
- int64_t sleep_nsecs;
- bool no_yield;
- };
- FIXTURE_VARIANT_ADD(slice_ext, n2_2_50)
- {
- .total_nsecs = 5LL * NSEC_PER_SEC,
- .slice_nsecs = 2LL * NSEC_PER_USEC,
- .noise_nsecs = 2LL * NSEC_PER_USEC,
- .sleep_nsecs = 50LL * NSEC_PER_USEC,
- };
- FIXTURE_VARIANT_ADD(slice_ext, n50_2_50)
- {
- .total_nsecs = 5LL * NSEC_PER_SEC,
- .slice_nsecs = 50LL * NSEC_PER_USEC,
- .noise_nsecs = 2LL * NSEC_PER_USEC,
- .sleep_nsecs = 50LL * NSEC_PER_USEC,
- };
- FIXTURE_VARIANT_ADD(slice_ext, n2_2_50_no_yield)
- {
- .total_nsecs = 5LL * NSEC_PER_SEC,
- .slice_nsecs = 2LL * NSEC_PER_USEC,
- .noise_nsecs = 2LL * NSEC_PER_USEC,
- .sleep_nsecs = 50LL * NSEC_PER_USEC,
- .no_yield = true,
- };
- static inline bool elapsed(struct timespec *start, struct timespec *now,
- int64_t span)
- {
- int64_t delta = now->tv_sec - start->tv_sec;
- delta *= NSEC_PER_SEC;
- delta += now->tv_nsec - start->tv_nsec;
- return delta >= span;
- }
- static void *noise_thread(void *arg)
- {
- struct noise_params *p = arg;
- while (RSEQ_READ_ONCE(p->run)) {
- struct timespec ts_start, ts_now;
- clock_gettime(CLOCK_MONOTONIC, &ts_start);
- do {
- clock_gettime(CLOCK_MONOTONIC, &ts_now);
- } while (!elapsed(&ts_start, &ts_now, p->noise_nsecs));
- ts_start.tv_sec = 0;
- ts_start.tv_nsec = p->sleep_nsecs;
- clock_nanosleep(CLOCK_MONOTONIC, 0, &ts_start, NULL);
- }
- return NULL;
- }
- FIXTURE_SETUP(slice_ext)
- {
- cpu_set_t affinity;
- ASSERT_EQ(sched_getaffinity(0, sizeof(affinity), &affinity), 0);
- /* Pin it on a single CPU. Avoid CPU 0 */
- for (int i = 1; i < CPU_SETSIZE; i++) {
- if (!CPU_ISSET(i, &affinity))
- continue;
- CPU_ZERO(&affinity);
- CPU_SET(i, &affinity);
- ASSERT_EQ(sched_setaffinity(0, sizeof(affinity), &affinity), 0);
- break;
- }
- ASSERT_EQ(rseq_register_current_thread(), 0);
- ASSERT_EQ(prctl(PR_RSEQ_SLICE_EXTENSION, PR_RSEQ_SLICE_EXTENSION_SET,
- PR_RSEQ_SLICE_EXT_ENABLE, 0, 0), 0);
- self->noise_params.noise_nsecs = variant->noise_nsecs;
- self->noise_params.sleep_nsecs = variant->sleep_nsecs;
- self->noise_params.run = 1;
- ASSERT_EQ(pthread_create(&self->noise_thread, NULL, noise_thread, &self->noise_params), 0);
- }
- FIXTURE_TEARDOWN(slice_ext)
- {
- self->noise_params.run = 0;
- pthread_join(self->noise_thread, NULL);
- }
- TEST_F(slice_ext, slice_test)
- {
- unsigned long success = 0, yielded = 0, scheduled = 0, raced = 0;
- unsigned long total = 0, aborted = 0;
- struct rseq_abi *rs = rseq_get_abi();
- struct timespec ts_start, ts_now;
- ASSERT_NE(rs, NULL);
- clock_gettime(CLOCK_MONOTONIC, &ts_start);
- do {
- struct timespec ts_cs;
- bool req = false;
- clock_gettime(CLOCK_MONOTONIC, &ts_cs);
- total++;
- RSEQ_WRITE_ONCE(rs->slice_ctrl.request, 1);
- do {
- clock_gettime(CLOCK_MONOTONIC, &ts_now);
- } while (!elapsed(&ts_cs, &ts_now, variant->slice_nsecs));
- /*
- * request can be cleared unconditionally, but for making
- * the stats work this is actually checking it first
- */
- if (RSEQ_READ_ONCE(rs->slice_ctrl.request)) {
- RSEQ_WRITE_ONCE(rs->slice_ctrl.request, 0);
- /* Race between check and clear! */
- req = true;
- success++;
- }
- if (RSEQ_READ_ONCE(rs->slice_ctrl.granted)) {
- /* The above raced against a late grant */
- if (req)
- success--;
- if (variant->no_yield) {
- syscall(__NR_getpid);
- aborted++;
- } else {
- yielded++;
- if (!syscall(__NR_rseq_slice_yield))
- raced++;
- }
- } else {
- if (!req)
- scheduled++;
- }
- clock_gettime(CLOCK_MONOTONIC, &ts_now);
- } while (!elapsed(&ts_start, &ts_now, variant->total_nsecs));
- printf("# Total %12ld\n", total);
- printf("# Success %12ld\n", success);
- printf("# Yielded %12ld\n", yielded);
- printf("# Aborted %12ld\n", aborted);
- printf("# Scheduled %12ld\n", scheduled);
- printf("# Raced %12ld\n", raced);
- }
- TEST_HARNESS_MAIN
|