Created
January 26, 2023 18:03
-
-
Save joelagnel/a830380f532c1d21262914a06adc3718 to your computer and use it in GitHub Desktop.
A test to check RT throttling
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Author: Steven Rostedt | |
// | |
#define _GNU_SOURCE | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <stdbool.h> | |
#include <string.h> | |
#include <stdarg.h> | |
#include <errno.h> | |
#include <time.h> | |
#include <sched.h> | |
#include <unistd.h> | |
#include <pthread.h> | |
#include <sys/time.h> | |
static char *argv0; | |
static char *get_this_name(void) | |
{ | |
static char *this_name; | |
char *arg; | |
char *p; | |
if (this_name) | |
return this_name; | |
arg = argv0; | |
p = arg+strlen(arg); | |
while (p >= arg && *p != '/') | |
p--; | |
p++; | |
this_name = p; | |
return p; | |
} | |
static void usage(void) | |
{ | |
char *p = get_this_name(); | |
printf("usage: %s timeout (in seconds)\n" | |
"\n",p); | |
exit(-1); | |
} | |
static void __vdie(const char *fmt, va_list ap, int err) | |
{ | |
int ret = errno; | |
char *p = get_this_name(); | |
if (err && errno) | |
perror(p); | |
else | |
ret = -1; | |
fprintf(stderr, " "); | |
vfprintf(stderr, fmt, ap); | |
fprintf(stderr, "\n"); | |
exit(ret); | |
} | |
void die(const char *fmt, ...) | |
{ | |
va_list ap; | |
va_start(ap, fmt); | |
__vdie(fmt, ap, 0); | |
va_end(ap); | |
} | |
void pdie(const char *fmt, ...) | |
{ | |
va_list ap; | |
va_start(ap, fmt); | |
__vdie(fmt, ap, 1); | |
va_end(ap); | |
} | |
static void set_prio(int prio) | |
{ | |
struct sched_param param = { | |
.sched_priority = prio, | |
}; | |
sched_setscheduler(0, SCHED_FIFO, ¶m); | |
} | |
#define barrier() asm volatile ("" ::: "memory") | |
static int done; | |
void *start_task(void *d) | |
{ | |
unsigned long long *slice = d; | |
struct timespec tv; | |
set_prio(5); | |
tv.tv_sec = *slice / 1000000000ULL; | |
tv.tv_nsec = *slice - tv.tv_sec * 1000000000ULL; | |
nanosleep(&tv, NULL); | |
while (!done) | |
barrier(); | |
return NULL; | |
} | |
#define sec2usec(sec) (sec * 1000000ULL) | |
#define sec2nano(sec) (sec * 1000000000ULL) | |
#define inc_cpu(cpu, cpus) ((cpu) < ((cpus) - 1) ? (cpu) + 1 : 0) | |
static unsigned long long get_time(void) | |
{ | |
struct timeval tv; | |
unsigned long long time; | |
gettimeofday(&tv, NULL); | |
time = sec2usec(tv.tv_sec); | |
time += tv.tv_usec; | |
return time; | |
} | |
void *start_single_task(void *data) | |
{ | |
unsigned long long now; | |
cpu_set_t *start_cpu_set; | |
cpu_set_t *cpu_set; | |
size_t cpu_size; | |
int next_cpu = -1; | |
int cpus; | |
int cpu; | |
cpus = sysconf(_SC_NPROCESSORS_CONF); | |
cpu_size = CPU_ALLOC_SIZE(cpus); | |
start_cpu_set = CPU_ALLOC(cpus); | |
cpu_set = CPU_ALLOC(cpus); | |
CPU_ZERO_S(cpu_size, cpu_set); | |
sched_getaffinity(0, cpu_size, start_cpu_set); | |
while (!done) { | |
for (cpu = inc_cpu(next_cpu, cpus); cpu != next_cpu; | |
cpu = inc_cpu(cpu, cpus)) { | |
if (CPU_SET_S(cpu, cpu_size, start_cpu_set)) { | |
CPU_CLR_S(next_cpu, cpu_size, cpu_set); | |
next_cpu = cpu; | |
CPU_SET_S(next_cpu, cpu_size, cpu_set); | |
printf("Pin to %d\n", cpu); | |
sched_setaffinity(0, cpu_size, cpu_set); | |
break; | |
} | |
} | |
now = get_time(); | |
now += sec2usec(1) / 2; | |
while (get_time() < now) | |
barrier(); | |
} | |
return NULL; | |
} | |
static void run_single_thread(int secs) | |
{ | |
pthread_t threads; | |
if (pthread_create(&threads, NULL, start_single_task, NULL)) | |
pdie("pthread_create"); | |
sleep(secs); | |
done = 1; | |
pthread_join(threads, NULL); | |
} | |
static void __create_threads(int cpus, int secs) | |
{ | |
unsigned long long slice[cpus]; | |
unsigned long long time; | |
pthread_t threads[cpus]; | |
int i; | |
time = sec2nano(1) / cpus; | |
if (!time) | |
time = 1; | |
for (i=0; i < cpus; i++) { | |
slice[i] = time * i; | |
if (pthread_create(&threads[i], NULL, start_task, | |
(void *)&slice[i])) | |
pdie("pthread_create"); | |
} | |
sleep(secs); | |
done = 1; | |
for (i = 0; i < cpus; i++) | |
pthread_join(threads[i], NULL); | |
} | |
static void create_threads(int secs) | |
{ | |
int cpus; | |
cpus = sysconf(_SC_NPROCESSORS_ONLN); | |
if (cpus <= 0) | |
pdie("Can't determine the number of CPUs"); | |
__create_threads(cpus, secs); | |
} | |
int main (int argc, char **argv) | |
{ | |
bool single; | |
int secs; | |
int c; | |
argv0 = argv[0]; | |
while ((c = getopt(argc, argv, "sh")) >= 0) { | |
switch (c) { | |
case 's': | |
single = true; | |
break; | |
case 'h': | |
default: | |
usage(); | |
} | |
} | |
argc -= optind; | |
argv += optind; | |
if (argc < 1) | |
usage(); | |
secs = atoi(argv[0]); | |
if (secs <= 0) | |
die("Invalid timeout '%d'\n", secs); | |
set_prio(10); | |
if (single) | |
run_single_thread(secs); | |
else | |
create_threads(secs); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment