lkml.org 
[lkml]   [2016]   [Jun]   [23]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
From
Subject[PATCH 2/3] rt-tests: Add cyclicdeadline and deadline_test
Date
Add cyclicdeadline and deadline_test - these are two programs written by
Steven Rostedet to test the functionality and performance of
SCHED_DEADLINE

Signed-off-by: John Kacur <jkacur@redhat.com>
---
src/sched_deadline/cyclicdeadline.c | 1267 +++++++++++++++++++++
src/sched_deadline/deadline_test.c | 2097 +++++++++++++++++++++++++++++++++++
2 files changed, 3364 insertions(+)
create mode 100644 src/sched_deadline/cyclicdeadline.c
create mode 100644 src/sched_deadline/deadline_test.c

diff --git a/src/sched_deadline/cyclicdeadline.c b/src/sched_deadline/cyclicdeadline.c
new file mode 100644
index 000000000000..9c50456f7419
--- /dev/null
+++ b/src/sched_deadline/cyclicdeadline.c
@@ -0,0 +1,1267 @@
+#define _GNU_SOURCE
+#include <pthread.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <sched.h>
+#include <ctype.h>
+#include <errno.h>
+#include <signal.h>
+
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/mount.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/vfs.h>
+
+#include <linux/unistd.h>
+#include <linux/magic.h>
+
+#ifdef __i386__
+#ifndef __NR_sched_setattr
+#define __NR_sched_setattr 351
+#endif
+#ifndef __NR_sched_getattr
+#define __NR_sched_getattr 352
+#endif
+#ifndef __NR_getcpu
+#define __NR_getcpu 309
+#endif
+#else /* x86_64 */
+#ifndef __NR_sched_setattr
+#define __NR_sched_setattr 314
+#endif
+#ifndef __NR_sched_getattr
+#define __NR_sched_getattr 315
+#endif
+#ifndef __NR_getcpu
+#define __NR_getcpu 309
+#endif
+#endif /* i386 or x86_64 */
+#ifndef SCHED_DEADLINE
+#define SCHED_DEADLINE 6
+#endif
+
+#define _STR(x) #x
+#define STR(x) _STR(x)
+#ifndef MAXPATH
+#define MAXPATH 1024
+#endif
+
+#define CPUSET_ALL "my_cpuset_all"
+#define CPUSET_LOCAL "my_cpuset"
+
+#define gettid() syscall(__NR_gettid)
+#define sched_setattr(pid, attr, flags) syscall(__NR_sched_setattr, pid, attr, flags)
+#define sched_getattr(pid, attr, size, flags) syscall(__NR_sched_getattr, pid, attr, size, flags)
+#define getcpu(cpup, nodep, unused) syscall(__NR_getcpu, cpup, nodep, unused)
+
+typedef unsigned long long u64;
+typedef unsigned int u32;
+typedef int s32;
+
+/* Struct to transfer parameters to the thread */
+struct thread_param {
+ u64 runtime_us;
+ u64 deadline_us;
+
+ int mode;
+ int timermode;
+ int signal;
+ int clock;
+ unsigned long max_cycles;
+ struct thread_stat *stats;
+ unsigned long interval;
+ int cpu;
+ int node;
+ int tnum;
+};
+
+/* Struct for statistics */
+struct thread_stat {
+ unsigned long cycles;
+ unsigned long cyclesread;
+ long min;
+ long max;
+ long act;
+ double avg;
+ long *values;
+ long *hist_array;
+ long *outliers;
+ pthread_t thread;
+ int threadstarted;
+ int tid;
+ long reduce;
+ long redmax;
+ long cycleofmax;
+ long hist_overflow;
+ long num_outliers;
+};
+
+struct sched_data {
+ u64 runtime_us;
+ u64 deadline_us;
+
+ int bufmsk;
+
+ struct thread_stat stat;
+
+ char buff[BUFSIZ+1];
+};
+
+struct sched_attr {
+ u32 size;
+
+ u32 sched_policy;
+ u64 sched_flags;
+
+ /* SCHED_NORMAL, SCHED_BATCH */
+ s32 sched_nice;
+
+ /* SCHED_FIFO, SCHED_RR */
+ u32 sched_priority;
+
+ /* SCHED_DEADLINE */
+ u64 sched_runtime;
+ u64 sched_deadline;
+ u64 sched_period;
+};
+
+static int shutdown;
+
+static pthread_barrier_t barrier;
+
+static int cpu_count;
+static int all_cpus;
+
+static int nr_threads;
+static int use_nsecs;
+
+static int mark_fd;
+
+static int find_mount(const char *mount, char *debugfs)
+{
+ char type[100];
+ FILE *fp;
+
+ if ((fp = fopen("/proc/mounts","r")) == NULL)
+ return 0;
+
+ while (fscanf(fp, "%*s %"
+ STR(MAXPATH)
+ "s %99s %*s %*d %*d\n",
+ debugfs, type) == 2) {
+ if (strcmp(type, mount) == 0)
+ break;
+ }
+ fclose(fp);
+
+ if (strcmp(type, mount) != 0)
+ return 0;
+ return 1;
+}
+
+static const char *find_debugfs(void)
+{
+ static int debugfs_found;
+ static char debugfs[MAXPATH+1];
+
+ if (debugfs_found)
+ return debugfs;
+
+ if (!find_mount("debugfs", debugfs))
+ return "";
+
+ debugfs_found = 1;
+
+ return debugfs;
+}
+
+static int my_vsprintf(char *buf, int size, const char *fmt, va_list ap)
+{
+ const char *p;
+ char tmp[100];
+ char *s = buf;
+ char *end = buf + size;
+ char *str;
+ long long lng;
+ int l;
+ int i;
+
+ end[-1] = 0;
+
+ for (p = fmt; *p && s < end; p++) {
+ if (*p == '%') {
+ l = 0;
+ again:
+ p++;
+ switch (*p) {
+ case 's':
+ if (l) {
+ fprintf(stderr, "Illegal print format l used with %%s\n");
+ exit(-1);
+ }
+ str = va_arg(ap, char *);
+ l = strlen(str);
+ strncpy(s, str, end - s);
+ s += l;
+ break;
+ case 'l':
+ l++;
+ goto again;
+ case 'd':
+ if (l == 1) {
+ if (sizeof(long) == 8)
+ l = 2;
+ }
+ if (l == 2)
+ lng = va_arg(ap, long long);
+ else if (l > 2) {
+ fprintf(stderr, "Illegal print format l=%d\n", l);
+ exit(-1);
+ } else
+ lng = va_arg(ap, int);
+ i = 0;
+ while (lng > 0) {
+ tmp[i++] = (lng % 10) + '0';
+ lng /= 10;
+ }
+ tmp[i] = 0;
+ l = strlen(tmp);
+ if (!l) {
+ *s++ = '0';
+ } else {
+ while (l)
+ *s++ = tmp[--l];
+ }
+ break;
+ default:
+ fprintf(stderr, "Illegal print format '%c'\n", *p);
+ exit(-1);
+ }
+ continue;
+ }
+ *s++ = *p;
+ }
+
+ return s - buf;
+}
+
+#if 0
+static int my_sprintf(char *buf, int size, const char *fmt, ...)
+{
+ va_list ap;
+ int n;
+
+ va_start(ap, fmt);
+ n = vsnprintf(buf, size, fmt, ap);
+ va_end(ap);
+ return n;
+}
+#endif
+
+static void ftrace_write(char *buf, const char *fmt, ...)
+{
+ va_list ap;
+ int n;
+
+ if (mark_fd < 0)
+ return;
+
+ va_start(ap, fmt);
+ n = my_vsprintf(buf, BUFSIZ, fmt, ap);
+ va_end(ap);
+
+ write(mark_fd, buf, n);
+}
+
+static void setup_ftrace_marker(void)
+{
+ struct stat st;
+ const char *debugfs = find_debugfs();
+ char files[strlen(debugfs) + 14];
+ int ret;
+
+ if (strlen(debugfs) == 0)
+ return;
+
+ sprintf(files, "%s/tracing/trace_marker", debugfs);
+ ret = stat(files, &st);
+ if (ret >= 0)
+ goto found;
+ /* Do nothing if not mounted */
+ return;
+found:
+ mark_fd = open(files, O_WRONLY);
+}
+
+static int setup_hr_tick(void)
+{
+ const char *debugfs = find_debugfs();
+ char files[strlen(debugfs) + strlen("/sched_features") + 1];
+ char buf[500];
+ struct stat st;
+ static int set = 0;
+ char *p;
+ int ret;
+ int len;
+ int fd;
+
+ if (set)
+ return 1;
+
+ set = 1;
+
+ if (strlen(debugfs) == 0)
+ return 0;
+
+ sprintf(files, "%s/sched_features", debugfs);
+ ret = stat(files, &st);
+ if (ret < 0)
+ return 0;
+
+ fd = open(files, O_RDWR);
+ perror(files);
+ if (fd < 0)
+ return 0;
+
+ len = sizeof(buf);
+
+ ret = read(fd, buf, len);
+ if (ret < 0) {
+ perror(files);
+ close(fd);
+ return 0;
+ }
+ if (ret >= len)
+ ret = len - 1;
+ buf[ret] = 0;
+
+ ret = 1;
+
+ p = strstr(buf, "HRTICK");
+ if (p + 3 >= buf) {
+ p -= 3;
+ if (strncmp(p, "NO_HRTICK", 9) == 0) {
+ ret = write(fd, "HRTICK", 6);
+ if (ret != 6)
+ ret = 0;
+ else
+ ret = 1;
+ }
+ }
+
+ close(fd);
+ return ret;
+}
+
+static int mounted(const char *path, long magic)
+{
+ struct statfs st_fs;
+
+ if (statfs(path, &st_fs) < 0)
+ return -1;
+ if ((long)st_fs.f_type != magic)
+ return 0;
+ return 1;
+}
+
+#define CGROUP_PATH "/sys/fs/cgroup"
+#define CPUSET_PATH CGROUP_PATH "/cpuset"
+
+static int open_cpuset(const char *path, const char *name)
+{
+ char buf[MAXPATH];
+ struct stat st;
+ int ret;
+ int fd;
+
+ buf[MAXPATH - 1] = 0;
+ snprintf(buf, MAXPATH - 1, "%s/%s", path, name);
+
+ ret = stat(buf, &st);
+ if (ret < 0)
+ return ret;
+
+ fd = open(buf, O_WRONLY);
+ return fd;
+}
+
+static int mount_cpuset(void)
+{
+ struct stat st;
+ int ret;
+ int fd;
+
+ /* Check if cgroups is already mounted. */
+ ret = mounted(CGROUP_PATH, TMPFS_MAGIC);
+ if (ret < 0)
+ return ret;
+ if (!ret) {
+ ret = mount("cgroup_root", CGROUP_PATH, "tmpfs", 0, NULL);
+ if (ret < 0)
+ return ret;
+ }
+ ret = stat(CPUSET_PATH, &st);
+ if (ret < 0) {
+ ret = mkdir(CPUSET_PATH, 0755);
+ if (ret < 0)
+ return ret;
+ }
+ ret = mounted(CPUSET_PATH, CGROUP_SUPER_MAGIC);
+ if (ret < 0)
+ return ret;
+ if (!ret) {
+ ret = mount("cpuset", CPUSET_PATH, "cgroup", 0, "cpuset");
+ if (ret < 0)
+ return ret;
+ }
+
+ fd = open_cpuset(CPUSET_PATH, "cpuset.cpu_exclusive");
+ if (fd < 0)
+ return fd;
+ ret = write(fd, "1", 2);
+ close(fd);
+
+ fd = open_cpuset(CPUSET_PATH, "cpuset.sched_load_balance");
+ if (fd < 0)
+ return fd;
+ ret = write(fd, "0", 2);
+ close(fd);
+
+ return 0;
+}
+
+enum {
+ CPUSET_FL_CPU_EXCLUSIVE = (1 << 0),
+ CPUSET_FL_MEM_EXCLUSIVE = (1 << 1),
+ CPUSET_FL_ALL_TASKS = (1 << 2),
+ CPUSET_FL_TASKS = (1 << 3),
+ CPUSET_FL_CLEAR_LOADBALANCE = (1 << 4),
+ CPUSET_FL_SET_LOADBALANCE = (1 << 5),
+ CPUSET_FL_CLONE_CHILDREN = (1 << 6),
+};
+
+static const char *make_cpuset(const char *name, const char *cpus,
+ const char *mems, unsigned flags, ...)
+{
+ struct stat st;
+ char path[MAXPATH];
+ char buf[100];
+ va_list ap;
+ int ret;
+ int fd;
+
+ printf("Creating cpuset '%s'\n", name);
+ snprintf(path, MAXPATH - 1, "%s/%s", CPUSET_PATH, name);
+ path[MAXPATH - 1] = 0;
+
+ ret = mount_cpuset();
+ if (ret < 0)
+ return "mount_cpuset";
+
+ ret = stat(path, &st);
+ if (ret < 0) {
+ ret = mkdir(path, 0755);
+ if (ret < 0)
+ return "mkdir";
+ }
+
+ fd = open_cpuset(path, "cpuset.cpus");
+ if (fd < 0)
+ return "cset";
+ ret = write(fd, cpus, strlen(cpus));
+ close(fd);
+ if (ret < 0)
+ return "write cpus";
+
+ if (mems) {
+ fd = open_cpuset(path, "cpuset.mems");
+ if (fd < 0)
+ return "open mems";
+ ret = write(fd, mems, strlen(mems));
+ close(fd);
+ if (ret < 0)
+ return "write mems";
+ }
+
+ if (flags & CPUSET_FL_CPU_EXCLUSIVE) {
+ fd = open_cpuset(path, "cpuset.cpu_exclusive");
+ if (fd < 0)
+ return "open cpu_exclusive";
+ ret = write(fd, "1", 2);
+ close(fd);
+ if (ret < 0)
+ return "write cpu_exclusive";
+ }
+
+ if (flags & (CPUSET_FL_CLEAR_LOADBALANCE | CPUSET_FL_SET_LOADBALANCE)) {
+ fd = open_cpuset(path, "cpuset.sched_load_balance");
+ if (fd < 0)
+ return "open sched_load_balance";
+ if (flags & CPUSET_FL_SET_LOADBALANCE)
+ ret = write(fd, "1", 2);
+ else
+ ret = write(fd, "0", 2);
+ close(fd);
+ if (ret < 0)
+ return "write sched_load_balance";
+ }
+
+ if (flags & CPUSET_FL_CLONE_CHILDREN) {
+ fd = open_cpuset(path, "cgroup.clone_children");
+ if (fd < 0)
+ return "open clone_children";
+ ret = write(fd, "1", 2);
+ close(fd);
+ if (ret < 0)
+ return "write clone_children";
+ }
+
+
+ if (flags & CPUSET_FL_TASKS) {
+ int *pids;
+ int i;
+
+ va_start(ap, flags);
+
+ fd = open_cpuset(path, "tasks");
+ if (fd < 0)
+ return "open tasks";
+
+ ret = 0;
+ pids = va_arg(ap, int *);
+ for (i = 0; pids[i]; i++) {
+ sprintf(buf, "%d ", pids[i]);
+ ret = write(fd, buf, strlen(buf));
+ }
+ va_end(ap);
+ close(fd);
+ if (ret < 0) {
+ fprintf(stderr, "Failed on task %d\n", pids[i]);
+ return "write tasks";
+ }
+ }
+
+ if (flags & CPUSET_FL_ALL_TASKS) {
+ FILE *fp;
+ int pid;
+
+ fd = open_cpuset(path, "tasks");
+
+ snprintf(path, MAXPATH - 1, "%s/tasks", CPUSET_PATH);
+ if ((fp = fopen(path,"r")) == NULL) {
+ close (fd);
+ return "opening cpuset tasks";
+ }
+
+ while (fscanf(fp, "%d", &pid) == 1) {
+ sprintf(buf, "%d", pid);
+ ret = write(fd, buf, strlen(buf));
+ /*
+ * Tasks can come and go, the only error we care
+ * about is ENOSPC, as that means something went
+ * wrong that we did not expect.
+ */
+ if (ret < 0 && errno == ENOSPC) {
+ fclose(fp);
+ close(fd);
+ return "Can not move tasks";
+ }
+ }
+ fclose(fp);
+ close(fd);
+ }
+
+ return NULL;
+}
+
+static void destroy_cpuset(const char *name, int print)
+{
+ struct stat st;
+ char path[MAXPATH];
+ char buf[100];
+ FILE *fp;
+ int pid;
+ int ret;
+ int fd;
+ int retry = 0;
+
+ printf("Removing %s\n", name);
+ snprintf(path, MAXPATH - 1, "%s/%s", CPUSET_PATH, name);
+ path[MAXPATH - 1] = 0;
+
+ ret = stat(path, &st);
+ if (ret < 0)
+ return;
+
+ again:
+ strncat(path, "/tasks", MAXPATH - 1);
+ if ((fp = fopen(path,"r")) == NULL) {
+ fprintf(stderr, "Failed opening %s\n", path);
+ perror("fopen");
+ return;
+ }
+ snprintf(path, MAXPATH - 1, "%s/tasks", CPUSET_PATH);
+ path[MAXPATH - 1] = 0;
+
+ fd = open(path, O_WRONLY);
+ if (fd < 0) {
+ fclose(fp);
+ fprintf(stderr, "Failed opening %s\n", path);
+ perror("open");
+ return;
+ }
+
+ while (fscanf(fp, "%d", &pid) == 1) {
+ sprintf(buf, "%d", pid);
+ if (print)
+ printf("Moving %d out of %s\n", pid, name);
+ write(fd, buf, strlen(buf));
+ }
+ fclose(fp);
+ close(fd);
+
+ snprintf(path, MAXPATH - 1, "%s/%s", CPUSET_PATH, name);
+ path[MAXPATH - 1] = 0;
+
+// return;
+ sleep(1);
+ ret = rmdir(path);
+ if (ret < 0) {
+ if (retry++ < 5)
+ goto again;
+ fprintf(stderr, "Failed to remove %s\n", path);
+ perror("rmdir");
+ if (retry++ < 5) {
+ fprintf(stderr, "Trying again\n");
+ goto again;
+ }
+ }
+}
+
+static void teardown(void)
+{
+ int fd;
+
+ if (all_cpus)
+ return;
+
+ fd = open_cpuset(CPUSET_PATH, "cpuset.cpu_exclusive");
+ if (fd >= 0) {
+ write(fd, "0", 2);
+ close(fd);
+ }
+
+ fd = open_cpuset(CPUSET_PATH, "cpuset.sched_load_balance");
+ if (fd >= 0) {
+ write(fd, "1", 2);
+ close(fd);
+ }
+
+ destroy_cpuset(CPUSET_ALL, 0);
+ destroy_cpuset(CPUSET_LOCAL, 1);
+}
+
+static void usage(char **argv)
+{
+ char *arg = argv[0];
+ char *p = arg+strlen(arg);
+
+ while (p >= arg && *p != '/')
+ p--;
+ p++;
+
+ printf("usage: %s\n"
+ "\n",p);
+ exit(-1);
+}
+
+static int fail;
+
+static u64 get_time_us(void)
+{
+ struct timespec ts;
+ u64 time;
+
+ clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
+ time = ts.tv_sec * 1000000;
+ time += ts.tv_nsec / 1000;
+
+ return time;
+}
+
+static void print_stat(FILE *fp, struct sched_data *sd, int index, int verbose, int quiet)
+{
+ struct thread_stat *stat = &sd->stat;
+
+ if (!verbose) {
+ if (quiet != 1) {
+ char *fmt;
+ if (use_nsecs)
+ fmt = "T:%2d (%5d) I:%ld C:%7lu "
+ "Min:%7ld Act:%8ld Avg:%8ld Max:%8ld\n";
+ else
+ fmt = "T:%2d (%5d) I:%ld C:%7lu "
+ "Min:%7ld Act:%5ld Avg:%5ld Max:%8ld\n";
+ fprintf(fp, fmt, index, stat->tid,
+ sd->deadline_us, stat->cycles, stat->min, stat->act,
+ stat->cycles ?
+ (long)(stat->avg/stat->cycles) : 0, stat->max);
+ }
+ } else {
+ while (stat->cycles != stat->cyclesread) {
+ long diff = stat->values
+ [stat->cyclesread & sd->bufmsk];
+
+ if (diff > stat->redmax) {
+ stat->redmax = diff;
+ stat->cycleofmax = stat->cyclesread;
+ }
+ stat->cyclesread++;
+ }
+ }
+}
+
+static u64 do_runtime(long tid, struct sched_data *sd, u64 period)
+{
+ struct thread_stat *stat = &sd->stat;
+ u64 next_period = period + sd->deadline_us;
+ u64 now = get_time_us();
+ u64 diff;
+
+ if (now < period) {
+ u64 delta = period - now;
+ /*
+ * The period could be off due to other deadline tasks
+ * preempting us when we started. If that's the case then
+ * adjust the current period.
+ */
+ ftrace_write(sd->buff,
+ "Adjusting period: now: %lld period: %lld delta:%lld%s\n",
+ now, period, delta, delta > sd->deadline_us / 2 ?
+ " HUGE ADJUSTMENT" : "");
+ period = now;
+ next_period = period + sd->deadline_us;
+ }
+
+ ftrace_write(sd->buff, "start at %lld off=%lld (period=%lld next=%lld)\n",
+ now, now - period, period, next_period);
+
+
+ diff = now - period;
+ if (diff > stat->max)
+ stat->max = diff;
+ if (!stat->min || diff < stat->min)
+ stat->min = diff;
+ stat->act = diff;
+ stat->avg += (double) diff;
+
+ stat->cycles++;
+
+ return next_period;
+}
+
+void *run_deadline(void *data)
+{
+ struct sched_data *sd = data;
+ struct thread_stat *stat = &sd->stat;
+ struct sched_attr attr;
+ long tid = gettid();
+ u64 period;
+ int ret;
+
+ printf("deadline thread %ld\n", tid);
+
+ stat->tid = tid;
+
+ ret = sched_getattr(0, &attr, sizeof(attr), 0);
+ if (ret < 0) {
+ fprintf(stderr, "[%ld]", tid);
+ perror("sched_getattr");
+ fail = 1;
+ pthread_barrier_wait(&barrier);
+ pthread_exit("Failed sched_getattr");
+ return NULL;
+ }
+
+ pthread_barrier_wait(&barrier);
+
+ if (fail)
+ return NULL;
+
+ attr.sched_policy = SCHED_DEADLINE;
+ attr.sched_runtime = sd->runtime_us * 1000;
+ attr.sched_deadline = sd->deadline_us * 1000;
+
+ printf("thread[%ld] runtime=%lldus deadline=%lldus\n",
+ gettid(), sd->runtime_us, sd->deadline_us);
+
+ pthread_barrier_wait(&barrier);
+
+ ret = sched_setattr(0, &attr, 0);
+ if (ret < 0) {
+ fprintf(stderr, "[%ld]", tid);
+ perror("sched_setattr");
+ fail = 1;
+ pthread_barrier_wait(&barrier);
+ pthread_exit("Failed sched_setattr");
+ return NULL;
+ }
+
+ pthread_barrier_wait(&barrier);
+
+ if (fail)
+ return NULL;
+
+ sched_yield();
+ period = get_time_us();
+
+ while (!shutdown) {
+ period = do_runtime(tid, sd, period);
+ sched_yield();
+ }
+ ret = sched_getattr(0, &attr, sizeof(attr), 0);
+ if (ret < 0) {
+ perror("sched_getattr");
+ pthread_exit("Failed second sched_getattr");
+ }
+
+ return NULL;
+}
+
+struct cpu_list {
+ struct cpu_list *next;
+ int start_cpu;
+ int end_cpu;
+};
+
+static void add_cpus(struct cpu_list **cpu_list, int start_cpu, int end_cpu)
+{
+ struct cpu_list *list;
+
+ while (*cpu_list && (*cpu_list)->end_cpu + 1 < start_cpu)
+ cpu_list = &(*cpu_list)->next;
+
+ if (!*cpu_list) {
+ *cpu_list = malloc(sizeof(struct cpu_list));
+ (*cpu_list)->start_cpu = start_cpu;
+ (*cpu_list)->end_cpu = end_cpu;
+ (*cpu_list)->next = NULL;
+ return;
+ }
+
+ /* Look to concatinate */
+ if (end_cpu > (*cpu_list)->start_cpu &&
+ start_cpu <= (*cpu_list)->end_cpu + 1) {
+ if (start_cpu < (*cpu_list)->start_cpu)
+ (*cpu_list)->start_cpu = start_cpu;
+ list = (*cpu_list)->next;
+ while (list && list->start_cpu <= end_cpu + 1) {
+ (*cpu_list)->end_cpu = list->end_cpu;
+ (*cpu_list)->next = list->next;
+ free(list);
+ list = (*cpu_list)->next;
+ }
+ if ((*cpu_list)->end_cpu < end_cpu)
+ (*cpu_list)->end_cpu = end_cpu;
+ return;
+ }
+
+ /* Check for overlaps */
+ if (end_cpu >= (*cpu_list)->start_cpu - 1) {
+ (*cpu_list)->start_cpu = start_cpu;
+ return;
+ }
+
+ list = malloc(sizeof(struct cpu_list));
+ list->start_cpu = start_cpu;
+ list->end_cpu = end_cpu;
+ list->next = (*cpu_list)->next;
+ (*cpu_list)->next = list;
+}
+
+static int count_cpus(struct cpu_list *cpu_list)
+{
+ struct cpu_list *list;
+ int cpus = 0;
+ int fail = 0;
+
+ while (cpu_list) {
+ list = cpu_list;
+ cpus += (list->end_cpu - list->start_cpu) + 1;
+ if (list->end_cpu >= cpu_count)
+ fail = 1;
+ cpu_list = list->next;
+ free(list);
+ }
+ return fail ? -1 : cpus;
+}
+
+static char *append_cpus(char *buf, int start, int end,
+ const char *comma, int *total)
+{
+ int len;
+
+ if (start == end) {
+ len = snprintf(NULL, 0, "%s%d", comma, start);
+ buf = realloc(buf, *total + len + 1);
+ buf[*total] = 0;
+ snprintf(buf + *total, len + 1, "%s%d", comma, start);
+ } else {
+ len = snprintf(NULL, 0, "%s%d-%d", comma, start, end);
+ buf = realloc(buf, *total + len + 1);
+ buf[*total] = 0;
+ snprintf(buf + *total, len + 1, "%s%d-%d", comma,
+ start, end);
+ }
+ *total += len;
+ return buf;
+}
+
+static void make_new_list(struct cpu_list *cpu_list, char **buf)
+{
+ char *comma = "";
+ int total = 0;
+
+ while (cpu_list) {
+ *buf = append_cpus(*buf, cpu_list->start_cpu, cpu_list->end_cpu,
+ comma, &total);
+ comma = ",";
+ cpu_list = cpu_list->next;
+ }
+}
+
+static void make_other_cpu_list(const char *setcpu, char **cpus)
+{
+ const char *p = setcpu;
+ const char *comma = "";
+ int curr_cpu = 0;
+ int cpu;
+ int total = 0;
+
+ while (*p && curr_cpu < cpu_count) {
+ cpu = atoi(p);
+ if (cpu > curr_cpu) {
+ *cpus = append_cpus(*cpus, curr_cpu, cpu - 1,
+ comma, &total);
+ comma = ",";
+ }
+ while (isdigit(*p))
+ p++;
+ if (*p == '-') {
+ p++;
+ cpu = atoi(p);
+ while (isdigit(*p))
+ p++;
+ }
+ curr_cpu = cpu + 1;
+ if (*p)
+ p++;
+ }
+
+ if (curr_cpu < cpu_count) {
+ *cpus = append_cpus(*cpus, curr_cpu, cpu_count - 1,
+ comma, &total);
+ }
+}
+
+static int calc_nr_cpus(const char *setcpu, char **buf)
+{
+ struct cpu_list *cpu_list = NULL;
+ const char *p;
+ int end_cpu;
+ int cpu;
+
+ for (p = setcpu; *p; ) {
+ cpu = atoi(p);
+ if (cpu < 0 || (!cpu && *p != '0'))
+ goto err;
+
+ while (isdigit(*p))
+ p++;
+ if (*p == '-') {
+ p++;
+ end_cpu = atoi(p);
+ if (end_cpu < cpu || (!end_cpu && *p != '0'))
+ goto err;
+ while (isdigit(*p))
+ p++;
+ } else
+ end_cpu = cpu;
+
+ add_cpus(&cpu_list, cpu, end_cpu);
+ if (*p == ',')
+ p++;
+ }
+
+ make_new_list(cpu_list, buf);
+ return count_cpus(cpu_list);
+ err:
+ /* Frees the list */
+ count_cpus(cpu_list);
+ return -1;
+}
+
+static void sighand(int sig)
+{
+ shutdown = 1;
+}
+
+static const char *join_thread(pthread_t *thread)
+{
+ void *result;
+
+ pthread_join(*thread, &result);
+ return result;
+}
+
+static void loop(struct sched_data *sched_data, int nr_threads)
+{
+ int i;
+
+ while (!shutdown) {
+ for (i = 0; i < nr_threads; i++) {
+ print_stat(stdout, &sched_data[i], i, 0, 0);
+ }
+ usleep(10000);
+ printf("\033[%dA", nr_threads);
+ }
+ usleep(10000);
+ for (i = 0; i < nr_threads; i++) {
+ printf("\n");
+ }
+}
+
+int main (int argc, char **argv)
+{
+ struct sched_data *sched_data;
+ struct sched_data *sd;
+ const char *res;
+ const char *setcpu = NULL;
+ char *setcpu_buf = NULL;
+ char *allcpu_buf = NULL;
+ pthread_t *thread;
+ unsigned int interval = 1000;
+ unsigned int step = 500;
+ int percent = 60;
+ u64 runtime;
+ u64 start_period;
+ u64 end_period;
+ int nr_cpus;
+ int i;
+ int c;
+
+ cpu_count = sysconf(_SC_NPROCESSORS_CONF);
+ if (cpu_count < 1) {
+ fprintf(stderr, "Can not calculate number of CPUS\n");
+ exit(-1);
+ }
+
+ while ((c = getopt(argc, argv, "+hac:t:")) >= 0) {
+ switch (c) {
+ case 'a':
+ all_cpus = 1;
+ if (!nr_threads)
+ nr_threads = cpu_count;
+ break;
+ case 'c':
+ setcpu = optarg;
+ break;
+ case 'i':
+ interval = atoi(optarg);
+ break;
+ case 's':
+ step = atoi(optarg);
+ break;
+ case 't':
+ nr_threads = atoi(optarg);
+ break;
+ case 'h':
+ default:
+ usage(argv);
+ }
+ }
+
+ if (!nr_threads)
+ nr_threads = 1;
+
+ if (setcpu) {
+ nr_cpus = calc_nr_cpus(setcpu, &setcpu_buf);
+ if (nr_cpus < 0 || nr_cpus > cpu_count) {
+ fprintf(stderr, "Invalid cpu input '%s'\n", setcpu);
+ exit(-1);
+ }
+ } else
+ nr_cpus = cpu_count;
+
+ if (!all_cpus && cpu_count == nr_cpus) {
+ printf("Using all CPUS\n");
+ all_cpus = 1;
+ }
+
+ /* Default cpu to use is the last one */
+ if (!all_cpus && !setcpu) {
+ setcpu_buf = malloc(10);
+ if (!setcpu_buf) {
+ perror("malloc");
+ exit(-1);
+ }
+ sprintf(setcpu_buf, "%d", cpu_count - 1);
+ }
+
+ setcpu = setcpu_buf;
+
+ if (setcpu)
+ make_other_cpu_list(setcpu, &allcpu_buf);
+
+ if (mlockall(MCL_CURRENT|MCL_FUTURE) == -1) {
+ perror("mlockall");
+ }
+
+ setup_ftrace_marker();
+
+ thread = calloc(nr_threads, sizeof(*thread));
+ sched_data = calloc(nr_threads, sizeof(*sched_data));
+ if (!thread || !sched_data) {
+ perror("allocating threads");
+ exit(-1);
+ }
+
+ if (nr_threads > nr_cpus) {
+ /*
+ * More threads than CPUs, then have the total be
+ * no more than 80 percent.
+ */
+ percent = nr_cpus * 80 / nr_threads;
+ }
+
+ /* Set up the data while sill in SCHED_FIFO */
+ for (i = 0; i < nr_threads; i++) {
+ sd = &sched_data[i];
+ /*
+ * Interval is the deadline/period
+ * The runtime is the percentage of that period.
+ */
+ runtime = interval * percent / 100;
+
+ if (runtime < 2000) {
+ /*
+ * If the runtime is less than 2ms, then we better
+ * have HRTICK enabled.
+ */
+ if (!setup_hr_tick()) {
+ fprintf(stderr, "For less that 2ms run times, you need to\n"
+ "have HRTICK enabled in debugfs/sched_features\n");
+ exit(-1);
+ }
+ }
+ sd->runtime_us = runtime;
+ sd->deadline_us = interval;
+
+ printf("interval: %lld:%lld\n", sd->runtime_us, sd->deadline_us);
+
+ /* Make sure that we can make our deadlines */
+ start_period = get_time_us();
+ do_runtime(gettid(), sd, start_period);
+ end_period = get_time_us();
+ if (end_period - start_period > sd->runtime_us) {
+ fprintf(stderr, "Failed to perform task within runtime: Missed by %lld us\n",
+ end_period - start_period - sd->runtime_us);
+ exit(-1);
+ }
+
+ printf(" Tested at %lldus of %lldus\n",
+ end_period - start_period, sd->runtime_us);
+
+ interval += step;
+ }
+
+
+ pthread_barrier_init(&barrier, NULL, nr_threads + 1);
+
+ for (i = 0; i < nr_threads; i++) {
+ sd = &sched_data[i];
+ pthread_create(&thread[i], NULL, run_deadline, sd);
+ }
+
+ atexit(teardown);
+
+ pthread_barrier_wait(&barrier);
+
+ if (fail) {
+ printf("fail 1\n");
+ exit(-1);
+ }
+
+ all_cpus = 1;
+ if (!all_cpus) {
+ int *pids;
+
+ res = make_cpuset(CPUSET_ALL, allcpu_buf, "0",
+ CPUSET_FL_SET_LOADBALANCE |
+ CPUSET_FL_CLONE_CHILDREN |
+ CPUSET_FL_ALL_TASKS);
+ if (res) {
+ perror(res);
+ exit(-1);
+ }
+
+ pids = calloc(nr_threads + 1, sizeof(int));
+ if (!pids) {
+ perror("Allocating pids");
+ exit(-1);
+ }
+
+ for (i = 0; i < nr_threads; i++)
+ pids[i] = sched_data[i].stat.tid;
+
+ res = make_cpuset(CPUSET_LOCAL, setcpu, "0",
+ CPUSET_FL_CPU_EXCLUSIVE |
+ CPUSET_FL_SET_LOADBALANCE |
+ CPUSET_FL_CLONE_CHILDREN |
+ CPUSET_FL_TASKS, pids);
+ free(pids);
+ if (res) {
+ perror(res);
+ exit(-1);
+ }
+
+ system("cat /sys/fs/cgroup/cpuset/my_cpuset/tasks");
+ }
+
+ printf("main thread %ld\n", gettid());
+
+ pthread_barrier_wait(&barrier);
+ printf("fail 2 %d\n", fail);
+
+ if (fail)
+ exit(-1);
+
+ pthread_barrier_wait(&barrier);
+
+ signal(SIGINT, sighand);
+ signal(SIGTERM, sighand);
+
+ if (!fail)
+ loop(sched_data, nr_threads);
+
+ for (i = 0; i < nr_threads; i++) {
+
+ sd = &sched_data[i];
+
+ res = join_thread(&thread[i]);
+ if (res) {
+ printf("Thread %d failed: %s\n", i, res);
+ continue;
+ }
+ }
+
+ free(setcpu_buf);
+ return 0;
+}
diff --git a/src/sched_deadline/deadline_test.c b/src/sched_deadline/deadline_test.c
new file mode 100644
index 000000000000..8a813c494520
--- /dev/null
+++ b/src/sched_deadline/deadline_test.c
@@ -0,0 +1,2097 @@
+/*
+ * Copyright (C) 2016 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License (not later!)
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * deadline_test.c
+ *
+ * This program is used to test the deadline scheduler (SCHED_DEADLINE tasks).
+ * It is broken up into various degrees of complexity that can be set with
+ * options.
+ *
+ * Here are the test cases:
+ *
+ * 1) Simplest - create one deadline task that can migrate across all CPUS.
+ * Look for "simple_test"
+ *
+ */
+#define _GNU_SOURCE
+#include <pthread.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <sched.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/mount.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/vfs.h>
+
+#include <linux/unistd.h>
+#include <linux/magic.h>
+
+/**
+ * usage - show the usage of the program and exit.
+ * @argv: The program passed in args
+ *
+ * This is defined here to show people looking at this code how
+ * to use this program as well.
+ */
+static void usage(char **argv)
+{
+ char *arg = argv[0];
+ char *p = arg+strlen(arg);
+
+ while (p >= arg && *p != '/')
+ p--;
+ p++;
+
+ printf("usage: %s [options]\n"
+ " -h - Show this help menu\n"
+ " -b - Bind on the last cpu. (shortcut for -c <lastcpu>)\n"
+ " -r prio - Add an RT task with given prio to stress system\n"
+ " -c cpulist - Comma/hyphen separated list of CPUs to run deadline tasks on\n"
+ " -p percent - The percent of bandwidth to use (1-90%%)\n"
+ " -P percent - The percent of runtime for execution completion\n"
+ " (Default 100%%)\n"
+ " -t threads - The number of threads to run as deadline (default 1)\n"
+ " -s step(us) - The amount to increase the deadline for each task (default 500us)\n"
+ "\n", p);
+ exit(-1);
+}
+
+/*
+ * sched_setattr() and sched_getattr() are new system calls. We need to
+ * hardcode it here.
+ */
+#if defined(__i386__)
+
+#ifndef __NR_sched_setattr
+#define __NR_sched_setattr 351
+#endif
+#ifndef __NR_sched_getattr
+#define __NR_sched_getattr 352
+#endif
+
+#elif defined(__x86_64__)
+
+#ifndef __NR_sched_setattr
+#define __NR_sched_setattr 314
+#endif
+#ifndef __NR_sched_getattr
+#define __NR_sched_getattr 315
+#endif
+
+#endif /* i386 or x86_64 */
+
+#if !defined(__NR_sched_setattr)
+# error "Your arch does not support sched_setattr()"
+#endif
+
+#if !defined(__NR_sched_getattr)
+# error "Your arch does not support sched_getattr()"
+#endif
+
+/* If not included in the headers, define sched deadline policy numbe */
+#ifndef SCHED_DEADLINE
+#define SCHED_DEADLINE 6
+#endif
+
+#define _STR(x) #x
+#define STR(x) _STR(x)
+
+/* Max path for cpuset path names. 1K should be enough */
+#ifndef MAXPATH
+#define MAXPATH 1024
+#endif
+
+/*
+ * "my_cpuset" is the cpuset that will hold the SCHED_DEADLINE tasks that
+ * want to limit their affinity.
+ *
+ * "my_cpuset_all" is the cpuset that will have the affinity of all the
+ * other CPUs outside the ones for SCHED_DEADLINE threads. It will hold
+ * all other tasks.
+ */
+#define CPUSET_ALL "my_cpuset_all"
+#define CPUSET_LOCAL "my_cpuset"
+
+/* Define the system call interfaces */
+#define gettid() syscall(__NR_gettid)
+#define sched_setattr(pid, attr, flags) syscall(__NR_sched_setattr, pid, attr, flags)
+#define sched_getattr(pid, attr, size, flags) syscall(__NR_sched_getattr, pid, attr, size, flags)
+
+typedef unsigned long long u64;
+typedef unsigned int u32;
+typedef int s32;
+
+/**
+ * struct sched_attr - get/set attr system call descriptor.
+ *
+ * This is the descriptor defined for setting SCHED_DEADLINE tasks.
+ * It will someday be in a header file.
+ *
+ * The fields specific for deadline:
+ *
+ * @sched_policy: 6 is for deadline
+ * @sched_runtime: The runtime in nanoseconds
+ * @sched_deadline: The deadline in nanoseconds.
+ * @sched_period: The period, if different than deadline (not used here)
+ */
+struct sched_attr {
+ u32 size;
+
+ u32 sched_policy;
+ u64 sched_flags;
+
+ /* SCHED_NORMAL, SCHED_BATCH */
+ s32 sched_nice;
+
+ /* SCHED_FIFO, SCHED_RR */
+ u32 sched_priority;
+
+ /* SCHED_DEADLINE */
+ u64 sched_runtime;
+ u64 sched_deadline;
+ u64 sched_period;
+};
+
+/**
+ * struct sched_data - the descriptor for the threads.
+ *
+ * This is the descriptor that will be passed as the thread data.
+ * It is used as both input to the thread, as well as output to
+ * the main program.
+ *
+ * @runtime_us: The runtime for sched_deadline tasks in microseconds
+ * @deadline_us: The deadline for sched_deadline tasks in microseconds
+ * @loops_per_period: The amount of loops to run for the runtime
+ * @max_time: Recorded max time to complete loops
+ * @min_time: Recorded min time to complete loops
+ * @total_time: The total time of all periods to perform the loops
+ * @nr_periods: The number of periods executed
+ * @prime: Calculating a prime number.
+ * @missed_periods: The number of periods that were missed (started late)
+ * @missed_deadlines: The number of deadlines that were missed (ended late)
+ * @total_adjust: The time in microseconds adjusted for starting early
+ * @nr_adjust: The number of times adjusted for starting early
+ * @last_time: Last runtime of loops (used to calculate runtime to give)
+ * @prio: The priority for SCHED_FIFO threads (uses same descriptor)
+ * @tid: Stores the thread ID of the thread.
+ * @vol: The number of voluntary schedules the thread made
+ * @nonvol: The number of non-voluntary schedules the thread made (preempted)
+ * @migrate: The number of migrations the thread made.
+ * @buff: A string buffer to store data to write to ftrace
+ *
+ */
+struct sched_data {
+ u64 runtime_us;
+ u64 deadline_us;
+
+ u64 loops_per_period;
+
+ u64 max_time;
+ u64 min_time;
+ u64 total_time;
+ u64 nr_periods;
+
+ u64 prime;
+
+ int missed_periods;
+ int missed_deadlines;
+ u64 total_adjust;
+ u64 nr_adjust;
+
+ u64 last_time;
+
+ int prio;
+ int tid;
+
+ int vol;
+ int nonvol;
+ int migrate;
+
+ char buff[BUFSIZ+1];
+
+ /* Try to keep each sched_data out of cache lines */
+ char padding[256];
+};
+
+/* Barrier to synchronize the threads for initialization */
+static pthread_barrier_t barrier;
+
+/* cpu_count is the number of detected cpus on the running machine */
+static int cpu_count;
+
+/*
+ * cpusetp and cpuset_size is for cpumasks, in case we run on a machine with
+ * more than 64 CPUs.
+ */
+static cpu_set_t *cpusetp;
+static int cpuset_size;
+
+/* Number of threads to create to run deadline scheduler with (default two) */
+static int nr_threads = 2;
+
+/**
+ * find_mount - Find if a file system type is already mounted
+ * @mount: The type of files system to find
+ * @debugfs: Where to place the path to the found file system.
+ *
+ * Returns 1 if found and sets @debugfs.
+ * Returns 0 otherwise.
+ */
+static int find_mount(const char *mount, char *debugfs)
+{
+ char type[100];
+ FILE *fp;
+
+ if ((fp = fopen("/proc/mounts","r")) == NULL)
+ return 0;
+
+ while (fscanf(fp, "%*s %"
+ STR(MAXPATH)
+ "s %99s %*s %*d %*d\n",
+ debugfs, type) == 2) {
+ if (strcmp(type, mount) == 0)
+ break;
+ }
+ fclose(fp);
+
+ if (strcmp(type, mount) != 0)
+ return 0;
+ return 1;
+}
+
+/**
+ * find_debugfs - Search for where debugfs is found
+ *
+ * Finds where debugfs is mounted and returns the path.
+ * The returned string is static and should not be modified.
+ */
+static const char *find_debugfs(void)
+{
+ static int debugfs_found;
+ static char debugfs[MAXPATH+1];
+
+ if (debugfs_found)
+ return debugfs;
+
+ if (!find_mount("debugfs", debugfs))
+ return "";
+
+ debugfs_found = 1;
+
+ return debugfs;
+}
+
+/**
+ * my_vsprintf - simplified vsprintf()
+ * @buf: The buffer to write the string to
+ * @size: The allocated size of @buf
+ * @fmmt: The format to parse
+ * @ap: The variable arguments
+ *
+ * Because there's no real way to prevent glibc's vsprintf from
+ * allocating more memory, or doing any type of system call,
+ * This is a simple version of the function that is under
+ * our control, to make sure we stay in userspace when creating
+ * a ftrace_write string, and only do a system call for the
+ * actual ftrace_write.
+ */
+static int my_vsprintf(char *buf, int size, const char *fmt, va_list ap)
+{
+ const char *p;
+ char tmp[100];
+ char *s = buf;
+ char *end = buf + size;
+ char *str;
+ long long lng;
+ int l;
+ int i;
+
+ end[-1] = 0;
+
+ for (p = fmt; *p && s < end; p++) {
+ if (*p == '%') {
+ l = 0;
+ again:
+ p++;
+ switch (*p) {
+ case 's':
+ if (l) {
+ fprintf(stderr, "Illegal print format l used with %%s\n");
+ exit(-1);
+ }
+ str = va_arg(ap, char *);
+ l = strlen(str);
+ strncpy(s, str, end - s);
+ s += l;
+ break;
+ case 'l':
+ l++;
+ goto again;
+ case 'd':
+ if (l == 1) {
+ if (sizeof(long) == 8)
+ l = 2;
+ }
+ if (l == 2)
+ lng = va_arg(ap, long long);
+ else if (l > 2) {
+ fprintf(stderr, "Illegal print format l=%d\n", l);
+ exit(-1);
+ } else
+ lng = va_arg(ap, int);
+ i = 0;
+ while (lng > 0) {
+ tmp[i++] = (lng % 10) + '0';
+ lng /= 10;
+ }
+ tmp[i] = 0;
+ l = strlen(tmp);
+ if (!l) {
+ *s++ = '0';
+ } else {
+ while (l)
+ *s++ = tmp[--l];
+ }
+ break;
+ default:
+ fprintf(stderr, "Illegal print format '%c'\n", *p);
+ exit(-1);
+ }
+ continue;
+ }
+ *s++ = *p;
+ }
+
+ return s - buf;
+}
+
+/* The ftrace tracing_marker file descriptor to write to ftrace */
+static int mark_fd;
+
+/**
+ * ftrace_write - write a string to ftrace tracing_marker
+ * @buf: A BUFSIZ + 1 allocated scratch pad
+ * @fmt: The format of the sting to write
+ * @va_arg: The arguments for @fmt
+ *
+ * If mark_fd is not less than zero, format the input
+ * and write it out to trace_marker (where mark_fd is a file
+ * descriptor of).
+ */
+static void ftrace_write(char *buf, const char *fmt, ...)
+{
+ va_list ap;
+ int n;
+
+ if (mark_fd < 0)
+ return;
+
+ va_start(ap, fmt);
+ n = my_vsprintf(buf, BUFSIZ, fmt, ap);
+ va_end(ap);
+
+ write(mark_fd, buf, n);
+}
+
+/**
+ * setup_ftrace_marker - Check if trace_marker exists and open if it does
+ *
+ * Tests if debugfs is mounted, and if it is, it tests to see if the
+ * trace_marker exists. If it does, it opens trace_marker and sets
+ * mark_fd to the file descriptor. Then ftrace_write() will be able
+ * to write to the ftrace marker, otherwise ftrace_write() becomes
+ * a nop.
+ *
+ * Failure to open the trace_marker file will not stop this application
+ * from executing. Only ftrace writes will not be performed.
+ */
+static void setup_ftrace_marker(void)
+{
+ struct stat st;
+ const char *debugfs = find_debugfs();
+ char files[strlen(debugfs) + 14];
+ int ret;
+
+ if (strlen(debugfs) == 0)
+ return;
+
+ sprintf(files, "%s/tracing/trace_marker", debugfs);
+ ret = stat(files, &st);
+ if (ret >= 0)
+ goto found;
+ /* Do nothing if not mounted */
+ return;
+found:
+ mark_fd = open(files, O_WRONLY);
+}
+
+/**
+ * setup_hr_tick - Enable the HRTICK in sched_features (if available)
+ *
+ * SCHED_DEADLINE tasks are based on HZ, which could be as slow as
+ * 100 times a second (10ms). Which is incredibly slow for scheduling.
+ * For SCHED_DEADLINE to have finer resolution, HRTICK feature must be
+ * set. That's located in the debugfs/sched_features directory.
+ *
+ * This will not mount debugfs. If debugfs is not mounted, this simply
+ * will fail.
+ */
+static int setup_hr_tick(void)
+{
+ const char *debugfs = find_debugfs();
+ char files[strlen(debugfs) + strlen("/sched_features") + 1];
+ char buf[500];
+ struct stat st;
+ static int set = 0;
+ char *p;
+ int ret;
+ int len;
+ int fd;
+
+ if (set)
+ return 1;
+
+ set = 1;
+
+ if (strlen(debugfs) == 0)
+ return 0;
+
+ sprintf(files, "%s/sched_features", debugfs);
+ ret = stat(files, &st);
+ if (ret < 0)
+ return 0;
+
+ fd = open(files, O_RDWR);
+ if (fd < 0) {
+ perror(files);
+ return 0;
+ }
+
+ len = sizeof(buf);
+
+ ret = read(fd, buf, len);
+ if (ret < 0) {
+ perror(files);
+ close(fd);
+ return 0;
+ }
+ if (ret >= len)
+ ret = len - 1;
+ buf[ret] = 0;
+
+ ret = 1;
+
+ p = strstr(buf, "HRTICK");
+ if (p + 3 >= buf) {
+ p -= 3;
+ if (strncmp(p, "NO_HRTICK", 9) == 0) {
+ ret = write(fd, "HRTICK", 6);
+ if (ret != 6)
+ ret = 0;
+ else
+ ret = 1;
+ }
+ }
+
+ close(fd);
+ return ret;
+}
+
+/**
+ * mounted - test if a path is mounted via the given mount type
+ * @path: The path to check is mounted
+ * @magic: The magic number of the mount type.
+ *
+ * Returns -1 if the path does not exist.
+ * Returns 0 if it is mounted but not of the given @magic type.
+ * Returns 1 if mounted and the @magic type matches.
+ */
+static int mounted(const char *path, long magic)
+{
+ struct statfs st_fs;
+
+ if (statfs(path, &st_fs) < 0)
+ return -1;
+ if ((long)st_fs.f_type != magic)
+ return 0;
+ return 1;
+}
+
+#define CGROUP_PATH "/sys/fs/cgroup"
+#define CPUSET_PATH CGROUP_PATH "/cpuset"
+
+/**
+ * open_cpuset - open a file (usually a cpuset file)
+ * @path: The path of the directory the file is in
+ * @name: The name of the file in the path to open.
+ *
+ * Open a file, used to open cpuset files. This function simply is
+ * made to open many files in the same directory.
+ *
+ * Returns the file descriptor of the opened file or less than zero
+ * on error.
+ */
+static int open_cpuset(const char *path, const char *name)
+{
+ char buf[MAXPATH];
+ struct stat st;
+ int ret;
+ int fd;
+
+ buf[MAXPATH - 1] = 0;
+ snprintf(buf, MAXPATH - 1, "%s/%s", path, name);
+
+ ret = stat(buf, &st);
+ if (ret < 0)
+ return ret;
+
+ fd = open(buf, O_WRONLY);
+ return fd;
+}
+
+/**
+ * mount_cpuset - Inialize the cpuset system
+ *
+ * Looks to see if cgroups are mounted, if it is not, then it mounts
+ * the cgroup_root to /sys/fs/cgroup. Then the directory cpuset exists
+ * and is mounted in that directory. If it is not, it is created and
+ * mounted.
+ *
+ * The toplevel cpuset "cpu_exclusive" flag is set, this allows child
+ * cpusets to set the flag too.
+ *
+ * The toplevel cpuset "load_balance" flag is cleared, letting the
+ * child cpusets take over load balancing.
+ */
+static int mount_cpuset(void)
+{
+ struct stat st;
+ int ret;
+ int fd;
+
+ /* Check if cgroups is already mounted. */
+ ret = mounted(CGROUP_PATH, TMPFS_MAGIC);
+ if (ret < 0)
+ return ret;
+ if (!ret) {
+ ret = mount("cgroup_root", CGROUP_PATH, "tmpfs", 0, NULL);
+ if (ret < 0)
+ return ret;
+ }
+ ret = stat(CPUSET_PATH, &st);
+ if (ret < 0) {
+ ret = mkdir(CPUSET_PATH, 0755);
+ if (ret < 0)
+ return ret;
+ }
+ ret = mounted(CPUSET_PATH, CGROUP_SUPER_MAGIC);
+ if (ret < 0)
+ return ret;
+ if (!ret) {
+ ret = mount("cpuset", CPUSET_PATH, "cgroup", 0, "cpuset");
+ if (ret < 0)
+ return ret;
+ }
+
+ fd = open_cpuset(CPUSET_PATH, "cpuset.cpu_exclusive");
+ if (fd < 0)
+ return fd;
+ ret = write(fd, "1", 2);
+ close(fd);
+
+ fd = open_cpuset(CPUSET_PATH, "cpuset.sched_load_balance");
+ if (fd < 0)
+ return fd;
+ ret = write(fd, "0", 2);
+ close(fd);
+
+ return 0;
+}
+
+/*
+ * CPUSET flags: used for creating cpusets
+ *
+ * CPU_EXCLUSIVE - Set the cpu exclusive flag
+ * MEM_EXCLUSIVE - Set the mem exclusive flag
+ * ALL_TASKS - Move all tasks from the toplevel cpuset to this one
+ * TASKS - Supply a list of thread IDs to move to this cpuset
+ * CLEAR_LOADBALANCE - clear the loadbalance flag
+ * SET_LOADBALANCE - set the loadbalance flag
+ * CLONE_CHILDREN - set the clone_children flag
+ */
+enum {
+ CPUSET_FL_CPU_EXCLUSIVE = (1 << 0),
+ CPUSET_FL_MEM_EXCLUSIVE = (1 << 1),
+ CPUSET_FL_ALL_TASKS = (1 << 2),
+ CPUSET_FL_TASKS = (1 << 3),
+ CPUSET_FL_CLEAR_LOADBALANCE = (1 << 4),
+ CPUSET_FL_SET_LOADBALANCE = (1 << 5),
+ CPUSET_FL_CLONE_CHILDREN = (1 << 6),
+};
+
+/**
+ * make_cpuset - create a cpuset
+ * @name: The name of the cpuset
+ * @cpus: A string list of cpus this set is for e.g. "1,3,4-7"
+ * @mems: The memory nodes to use (usually just "0") (set to NULL to ignore)
+ * @flags: See the CPUSET_FL_* flags above for information
+ * @va_args: An array of tasks to move if TASKS flag is set.
+ *
+ * Creates a cpuset.
+ *
+ * If TASKS is set, then @va_args will be an array of PIDs to move from
+ * the main cpuset, to this cpuset. The last element of the array must
+ * be a zero, to stop the processing of arrays.
+ *
+ * Returns NULL on success, and a string to describe what went wrong on error.
+ */
+static const char *make_cpuset(const char *name, const char *cpus,
+ const char *mems, unsigned flags, ...)
+{
+ struct stat st;
+ char path[MAXPATH];
+ char buf[100];
+ va_list ap;
+ int ret;
+ int fd;
+
+ printf("Creating cpuset '%s'\n", name);
+ snprintf(path, MAXPATH - 1, "%s/%s", CPUSET_PATH, name);
+ path[MAXPATH - 1] = 0;
+
+ ret = mount_cpuset();
+ if (ret < 0)
+ return "mount_cpuset";
+
+ /* Only create the new cpuset directory if it does not yet exist */
+ ret = stat(path, &st);
+ if (ret < 0) {
+ ret = mkdir(path, 0755);
+ if (ret < 0)
+ return "mkdir";
+ }
+
+ /* Assign the CPUs */
+ fd = open_cpuset(path, "cpuset.cpus");
+ if (fd < 0)
+ return "cset";
+ ret = write(fd, cpus, strlen(cpus));
+ close(fd);
+ if (ret < 0)
+ return "write cpus";
+
+ /* Assign the "mems" if it exists */
+ if (mems) {
+ fd = open_cpuset(path, "cpuset.mems");
+ if (fd < 0)
+ return "open mems";
+ ret = write(fd, mems, strlen(mems));
+ close(fd);
+ if (ret < 0)
+ return "write mems";
+ }
+
+ if (flags & CPUSET_FL_CPU_EXCLUSIVE) {
+ fd = open_cpuset(path, "cpuset.cpu_exclusive");
+ if (fd < 0)
+ return "open cpu_exclusive";
+ ret = write(fd, "1", 2);
+ close(fd);
+ if (ret < 0)
+ return "write cpu_exclusive";
+ }
+
+ if (flags & (CPUSET_FL_CLEAR_LOADBALANCE | CPUSET_FL_SET_LOADBALANCE)) {
+ fd = open_cpuset(path, "cpuset.sched_load_balance");
+ if (fd < 0)
+ return "open sched_load_balance";
+ if (flags & CPUSET_FL_SET_LOADBALANCE)
+ ret = write(fd, "1", 2);
+ else
+ ret = write(fd, "0", 2);
+ close(fd);
+ if (ret < 0)
+ return "write sched_load_balance";
+ }
+
+ if (flags & CPUSET_FL_CLONE_CHILDREN) {
+ fd = open_cpuset(path, "cgroup.clone_children");
+ if (fd < 0)
+ return "open clone_children";
+ ret = write(fd, "1", 2);
+ close(fd);
+ if (ret < 0)
+ return "write clone_children";
+ }
+
+
+ /* If TASKS flag is set, then an array of tasks is passed it */
+ if (flags & CPUSET_FL_TASKS) {
+ int *pids;
+ int i;
+
+ va_start(ap, flags);
+
+ fd = open_cpuset(path, "tasks");
+ if (fd < 0)
+ return "open tasks";
+
+ ret = 0;
+ pids = va_arg(ap, int *);
+
+ /* The array ends with pids[i] == 0 */
+ for (i = 0; pids[i]; i++) {
+ sprintf(buf, "%d ", pids[i]);
+ ret = write(fd, buf, strlen(buf));
+ if (ret < 0)
+ break;
+ }
+ va_end(ap);
+ close(fd);
+ if (ret < 0) {
+ fprintf(stderr, "Failed on task %d\n", pids[i]);
+ return "write tasks";
+ }
+ }
+
+ /* If ALL_TASKS flag is set, move all tasks from the top level cpuset */
+ if (flags & CPUSET_FL_ALL_TASKS) {
+ FILE *fp;
+ int pid;
+
+ fd = open_cpuset(path, "tasks");
+
+ snprintf(path, MAXPATH - 1, "%s/tasks", CPUSET_PATH);
+ if ((fp = fopen(path,"r")) == NULL) {
+ close (fd);
+ return "opening cpuset tasks";
+ }
+
+ while (fscanf(fp, "%d", &pid) == 1) {
+ sprintf(buf, "%d", pid);
+ ret = write(fd, buf, strlen(buf));
+ /*
+ * Tasks can come and go, and some tasks are kernel
+ * threads that cannot be moved. The only error we care
+ * about is ENOSPC, as that means something went
+ * wrong that we did not expect.
+ */
+ if (ret < 0 && errno == ENOSPC) {
+ fclose(fp);
+ close(fd);
+ return "Can not move tasks";
+ }
+ }
+ fclose(fp);
+ close(fd);
+ }
+
+ return NULL;
+}
+
+/**
+ * destroy_cpuset - tear down a cpuset that was created
+ * @name: The name of the cpuset to destroy
+ * @print: If the tasks being moved should be displayed
+ *
+ * Reads the tasks in the cpuset and moves them to the top level cpuset
+ * then destroys the @name cpuset.
+ */
+static void destroy_cpuset(const char *name, int print)
+{
+ struct stat st;
+ char path[MAXPATH];
+ char buf[100];
+ FILE *fp;
+ int pid;
+ int ret;
+ int fd;
+ int retry = 0;
+
+ printf("Removing %s\n", name);
+
+ /* Set path to the cpuset name that we will destroy */
+ snprintf(path, MAXPATH - 1, "%s/%s", CPUSET_PATH, name);
+ path[MAXPATH - 1] = 0;
+
+ /* Make sure it exists! */
+ ret = stat(path, &st);
+ if (ret < 0)
+ return;
+
+ again:
+ /*
+ * Append "/tasks" to the cpuset name, to read the tasks that are
+ * in this cpuset, that must be moved before destroying the cpuset.
+ */
+ strncat(path, "/tasks", MAXPATH - 1);
+ if ((fp = fopen(path,"r")) == NULL) {
+ fprintf(stderr, "Failed opening %s\n", path);
+ perror("fopen");
+ return;
+ }
+ /* Set path to the toplevel cpuset tasks file */
+ snprintf(path, MAXPATH - 1, "%s/tasks", CPUSET_PATH);
+ path[MAXPATH - 1] = 0;
+
+ fd = open(path, O_WRONLY);
+ if (fd < 0) {
+ fclose(fp);
+ fprintf(stderr, "Failed opening %s\n", path);
+ perror("open");
+ return;
+ }
+
+ /*
+ * Now fp points to the destroying cpuset tasks file, and
+ * fd is the toplevel cpuset file descriptor. Scan in the
+ * tasks that are in the cpuset that is being destroyed and
+ * write their pids into the toplevel cpuset.
+ */
+ while (fscanf(fp, "%d", &pid) == 1) {
+ sprintf(buf, "%d", pid);
+ if (print)
+ printf("Moving %d out of %s\n", pid, name);
+ write(fd, buf, strlen(buf));
+ }
+ fclose(fp);
+ close(fd);
+
+ /* Reset the path name back to the cpuset to destroy */
+ snprintf(path, MAXPATH - 1, "%s/%s", CPUSET_PATH, name);
+ path[MAXPATH - 1] = 0;
+
+ /* Sleep a bit to let all tasks migrate out of this cpuset. */
+ sleep(1);
+
+ ret = rmdir(path);
+ if (ret < 0) {
+ /*
+ * Sometimes there appears to be a delay, and tasks don't
+ * always move when you expect them to. Try 5 times, and
+ * give up after that.
+ */
+ if (retry++ < 5)
+ goto again;
+ fprintf(stderr, "Failed to remove %s\n", path);
+ perror("rmdir");
+ if (retry++ < 5) {
+ fprintf(stderr, "Trying again\n");
+ goto again;
+ }
+ }
+}
+
+/**
+ * teardown - Called atexit() to reset the system back to normal
+ *
+ * If cpusets were created, this destroys them and puts all tasks
+ * back to the main cgroup.
+ */
+static void teardown(void)
+{
+ int fd;
+
+ fd = open_cpuset(CPUSET_PATH, "cpuset.cpu_exclusive");
+ if (fd >= 0) {
+ write(fd, "0", 2);
+ close(fd);
+ }
+
+ fd = open_cpuset(CPUSET_PATH, "cpuset.sched_load_balance");
+ if (fd >= 0) {
+ write(fd, "1", 2);
+ close(fd);
+ }
+
+ destroy_cpuset(CPUSET_ALL, 0);
+ destroy_cpuset(CPUSET_LOCAL, 1);
+}
+
+/**
+ * bind_cpu - Set the affinity of a thread to a specific CPU.
+ * @cpu: The CPU to bind to.
+ *
+ * Sets the current thread to have an affinity of a sigle CPU.
+ * Does not work on SCHED_DEADLINE tasks.
+ */
+static void bind_cpu(int cpu)
+{
+ int ret;
+
+ CPU_ZERO_S(cpuset_size, cpusetp);
+ CPU_SET_S(cpu, cpuset_size, cpusetp);
+
+ ret = sched_setaffinity(0, cpuset_size, cpusetp);
+ if (ret < 0)
+ perror("sched_setaffinity bind");
+}
+
+/**
+ * unbind_cpu - Set the affinity of a task to all CPUs
+ *
+ * Sets the current thread to have an affinity for all CPUs.
+ * Does not work on SCHED_DEADLINE tasks.
+ */
+static void unbind_cpu(void)
+{
+ int cpu;
+ int ret;
+
+ for (cpu = 0; cpu < cpu_count; cpu++)
+ CPU_SET_S(cpu, cpuset_size, cpusetp);
+
+ ret = sched_setaffinity(0, cpuset_size, cpusetp);
+ if (ret < 0)
+ perror("sched_setaffinity unbind");
+}
+
+/*
+ * Used by set_prio, but can be used for any task not just current.
+ */
+static int set_thread_prio(pid_t pid, int prio)
+{
+ struct sched_param sp = { .sched_priority = prio };
+ int policy = SCHED_FIFO;
+
+ if (!prio)
+ policy = SCHED_OTHER;
+
+ /* set up our priority */
+ return sched_setscheduler(pid, policy, &sp);
+}
+
+/**
+ * set_prio - Set the SCHED_FIFO priority of a thread
+ * @prio: The priority to set a thread to
+ *
+ * Converts a SCHED_OTHER task into a SCHED_FIFO task and sets
+ * its priority to @prio. If @prio is zero, then it converts
+ * a SCHED_FIFO task back to a SCHED_OTHER task.
+ *
+ * Returns 0 on success, otherwise it failed.
+ */
+static int set_prio(int prio)
+{
+ return set_thread_prio(0, prio);
+}
+
+/* done - set when the test is complete to have all threads stop */
+static int done;
+
+/* fail - set during setup if any thread fails to initialize. */
+static int fail;
+
+/**
+ * get_time_us - Git the current clock time in microseconds
+ *
+ * Returns the current clock time in microseconds.
+ */
+static u64 get_time_us(void)
+{
+ struct timespec ts;
+ u64 time;
+
+ clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
+ time = ts.tv_sec * 1000000;
+ time += ts.tv_nsec / 1000;
+
+ return time;
+}
+
+/**
+ * run_loops - execute a number of loops to perform
+ * @loops: The number of loops to execute.
+ *
+ * Calculates prime numbers, because what else should we do?
+ */
+static u64 run_loops(struct sched_data *data, u64 loops)
+{
+ u64 start = get_time_us();
+ u64 end;
+ u64 i;
+ u64 prime;
+ u64 cnt = 2;
+ u64 result;
+
+ prime = data->prime;
+
+ for (i = 0; i < loops; i++) {
+ if (cnt > prime / 2) {
+ data->prime = prime;
+ prime++;
+ cnt = 2;
+ }
+ result = prime / cnt;
+ if (result * cnt == prime) {
+ prime++;
+ cnt = 2;
+ } else
+ cnt++;
+ }
+
+ /* Memory barrier */
+ asm("":::"memory");
+
+ end = get_time_us();
+ return end - start;
+}
+
+/* Helper function for read_ctx_switchs */
+static int get_value(const char *line)
+{
+ const char *p;
+
+ for (p = line; isspace(*p); p++)
+ ;
+ if (*p != ':')
+ return -1;
+ p++;
+ for (; isspace(*p); p++)
+ ;
+ return atoi(p);
+}
+
+/* Helper function for read_ctx_switchs */
+static int update_value(const char *line, int *val, const char *name)
+{
+ int ret;
+
+ if (strncmp(line, name, strlen(name)) == 0) {
+ ret = get_value(line + strlen(name));
+ if (ret < 0)
+ return 0;
+ *val = ret;
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * read_ctx_switches - read the scheduling information of a task
+ * @vol: Output to place number of voluntary schedules
+ * @nonvol: Output to place number of non-voluntary schedules (preemption)
+ * @migrate: Output to place the number of times the task migrated
+ *
+ * Reads /proc/<pid>/sched to get the statistics of the thread.
+ *
+ * For info only.
+ */
+static int read_ctx_switches(int *vol, int *nonvol, int *migrate)
+{
+ static int vol_once, nonvol_once;
+ const char *vol_name = "nr_voluntary_switches";
+ const char *nonvol_name = "nr_involuntary_switches";
+ const char *migrate_name = "se.nr_migrations";
+ char file[1024];
+ char buf[1024];
+ char *pbuf;
+ int pid;
+ size_t *pn;
+ size_t n;
+ FILE *fp;
+ int r;
+
+ pid = gettid();
+ snprintf(file, 1024, "/proc/%d/sched", pid);
+ fp = fopen(file, "r");
+ if (!fp) {
+ snprintf(file, 1024, "/proc/%d/status", pid);
+ fp = fopen(file, "r");
+ if (!fp) {
+ fprintf(stderr, "could not open %s", file);
+ return -1;
+ }
+ vol_name = "voluntary_ctxt_switches";
+ nonvol_name = "nonvoluntary_ctxt_switches";
+ }
+
+ *vol = *nonvol = *migrate = -1;
+
+ n = 1024;
+ pn = &n;
+ pbuf = buf;
+
+ while ((r = getline(&pbuf, pn, fp)) >= 0) {
+
+ if (update_value(buf, vol, vol_name))
+ continue;
+
+ if (update_value(buf, nonvol, nonvol_name))
+ continue;
+
+ if (update_value(buf, migrate, migrate_name))
+ continue;
+ }
+ fclose(fp);
+
+ if (!vol_once && *vol == -1) {
+ vol_once++;
+ fprintf(stderr, "Warning, could not find voluntary ctx switch count\n");
+ }
+ if (!nonvol_once && *nonvol == -1) {
+ nonvol_once++;
+ fprintf(stderr, "Warning, could not find nonvoluntary ctx switch count\n");
+ }
+
+ return 0;
+}
+
+/**
+ * do_runtime - Run a loop to simulate a specific task
+ * @tid: The thread ID
+ * @data: The sched_data descriptor
+ * @period: The time of the last period.
+ *
+ * Returns the expected next period.
+ *
+ * This simulates some task that needs to be completed within the deadline.
+ *
+ * Input:
+ * @data->deadline_us - to calculate next peroid
+ * @data->loops_per_peroid - to loop this amount of time
+ *
+ * Output:
+ * @data->total_adjust - Time adjusted for starting a period early
+ * @data->nr_adjusted - Number of times adjusted
+ * @data->missed_deadlines - Counter of missed deadlines
+ * @data->missed_periods - Counter of missed periods (started late)
+ * @data->max_time - Maximum time it took to complete the loops
+ * @data->min_time - Minimum time it took to complete the loops
+ * @data->last_time - How much time it took to complete loops this time
+ * @data->total_time - Total time it took to complete all loops
+ * @data->nr_periods - Number of periods that were executed.
+ */
+static u64 do_runtime(long tid, struct sched_data *data, u64 period)
+{
+ u64 next_period = period + data->deadline_us;
+ u64 now = get_time_us();
+ u64 end;
+ u64 diff;
+ u64 time;
+
+ /*
+ * next_period is our new deadline. If now is passed that point
+ * we missed a period.
+ */
+ if (now > next_period) {
+ ftrace_write(data->buff,
+ "Missed a period start: %lld next: %lld now: %lld\n",
+ period, next_period, now);
+ /* See how many periods were missed. */
+ while (next_period < now) {
+ next_period += data->deadline_us;
+ data->missed_periods++;
+ }
+ } else if (now < period) {
+ u64 delta = period - now;
+ /*
+ * Currently, there's no way to find when the period actually
+ * does begin. If the first runtime starts late, due to another
+ * deadline task with a shorter deadline running, then it is
+ * possible that the next period comes in quicker than we
+ * expect it to.
+ *
+ * Adjust the period to start at now, and record the shift.
+ */
+ ftrace_write(data->buff,
+ "Adjusting period: now: %lld period: %lld delta:%lld%s\n",
+ now, period, delta, delta > data->deadline_us / 2 ?
+ " HUGE ADJUSTMENT" : "");
+ data->total_adjust += delta;
+ data->nr_adjust++;
+ period = now;
+ next_period = period + data->deadline_us;
+ }
+
+ ftrace_write(data->buff, "start at %lld off=%lld (period=%lld next=%lld)\n",
+ now, now - period, period, next_period);
+
+ /* Run the simulate task (loops) */
+ time = run_loops(data, data->loops_per_period);
+
+ end = get_time_us();
+
+ /* Did we make our deadline? */
+ if (end > next_period) {
+ ftrace_write(data->buff,
+ "Failed runtime by %lld\n", end - next_period);
+ data->missed_deadlines++;
+ /*
+ * We missed our deadline, which means we entered the
+ * next period. Move it forward one, if we moved it too
+ * much, then the next interation will adjust.
+ */
+ next_period += data->deadline_us;
+ }
+
+
+ diff = end - now;
+ if (diff > data->max_time)
+ data->max_time = diff;
+ if (!data->min_time || diff < data->min_time)
+ data->min_time = diff;
+
+ data->last_time = time;
+ data->total_time += diff;
+ data->nr_periods++;
+ ftrace_write(data->buff,
+ "end at %lld diff: %lld run loops: %lld us\n", end, diff, time);
+
+ return next_period;
+}
+
+/**
+ * run_deadline - Run deadline thread
+ * @data: sched_data descriptor
+ *
+ * This is called by pthread_create() and executes the sched deadline
+ * task. @data has the following:
+ *
+ * Input:
+ * @data->runtime_us: The amount of requested runtime in microseconds
+ * @data->deadline_us: The requested deadline in microseconds
+ * @data->loops_per_period: The number of loops to make during its runtime
+ *
+ * Output:
+ * @data->tid: The thread ID
+ * @data->vol: The number of times the thread voluntarily scheduled out
+ * @data->nonvol: The number of times the thread non-voluntarily scheduled out
+ * @data->migrate: The number of times the thread migrated across CPUs.
+ */
+void *run_deadline(void *data)
+{
+ struct sched_data *sched_data = data;
+ struct sched_attr attr;
+ int vol, nonvol, migrate;
+ long tid = gettid();
+ void *heap;
+ u64 period;
+ int ret;
+
+ /*
+ * The internal glibc vsnprintf() used by ftrace_write()
+ * may alloc more space to do conversions. Alloc a bunch of
+ * memory and free it, and hopefully glibc doesn't return that
+ * back to the system (we did do an mlockall after all).
+ */
+ heap = malloc(1000000);
+ if (!heap) {
+ perror("malloc");
+ fail = 1;
+ pthread_barrier_wait(&barrier);
+ pthread_exit("Failed to alloc heap");
+ return NULL;
+ }
+ free(heap);
+
+ printf("deadline thread %ld\n", tid);
+
+ sched_data->tid = tid;
+ sched_data->prime = 2;
+
+ ret = sched_getattr(0, &attr, sizeof(attr), 0);
+ if (ret < 0) {
+ fprintf(stderr, "[%ld]", tid);
+ perror("sched_getattr");
+ fail = 1;
+ pthread_barrier_wait(&barrier);
+ pthread_exit("Failed sched_getattr");
+ return NULL;
+ }
+
+ pthread_barrier_wait(&barrier);
+
+ if (fail)
+ return NULL;
+
+ attr.sched_policy = SCHED_DEADLINE;
+ attr.sched_runtime = sched_data->runtime_us * 1000;
+ attr.sched_deadline = sched_data->deadline_us * 1000;
+
+ printf("thread[%ld] runtime=%lldus deadline=%lldus loops=%lld\n",
+ gettid(), sched_data->runtime_us,
+ sched_data->deadline_us, sched_data->loops_per_period);
+
+ pthread_barrier_wait(&barrier);
+
+ ret = sched_setattr(0, &attr, 0);
+ if (ret < 0) {
+ done = 0;
+ fprintf(stderr, "[%ld]", tid);
+ perror("sched_setattr");
+ fail = 1;
+ pthread_barrier_wait(&barrier);
+ pthread_exit("Failed sched_setattr");
+ return NULL;
+ }
+
+ pthread_barrier_wait(&barrier);
+
+ if (fail)
+ return NULL;
+
+ sched_yield();
+ period = get_time_us();
+
+ while (!done) {
+ period = do_runtime(tid, sched_data, period);
+ sched_yield();
+ }
+ ret = sched_getattr(0, &attr, sizeof(attr), 0);
+ if (ret < 0) {
+ perror("sched_getattr");
+ pthread_exit("Failed second sched_getattr");
+ }
+
+ read_ctx_switches(&vol, &nonvol, &migrate);
+
+ sched_data->vol = vol;
+ sched_data->nonvol = nonvol;
+ sched_data->migrate = migrate;
+
+ return NULL;
+}
+
+/**
+ * run_rt_spin - the Real-Time task spinner
+ * @data: The sched_data descriptor
+ *
+ * This function is called as a thread function. It will read @data->prio
+ * and set its priority base on that parameter. It sets @data->tid to the
+ * thread ID. Then after waiting through pthread barriers to sync with
+ * the main thread as well as with sched deadline threads, it will
+ * run in a tight loop until the global variable "done" is set.
+ */
+void *run_rt_spin(void *data)
+{
+ struct sched_data *sched_data = data;
+ long tid = gettid();
+
+ sched_data->tid = tid;
+
+ if (set_prio(sched_data->prio) < 0) {
+ fail = 1;
+ pthread_barrier_wait(&barrier);
+ pthread_exit("Failed setting prio");
+ return NULL;
+ }
+
+ pthread_barrier_wait(&barrier);
+
+ if (fail)
+ return NULL;
+
+ pthread_barrier_wait(&barrier);
+
+ if (fail)
+ return NULL;
+
+ pthread_barrier_wait(&barrier);
+
+ if (fail)
+ return NULL;
+
+ while (!done) {
+ get_time_us();
+ }
+
+ return NULL;
+}
+
+struct cpu_list {
+ struct cpu_list *next;
+ int start_cpu;
+ int end_cpu;
+};
+
+/**
+ * add_cpus - Add cpus to cpu_list based on the passed in range
+ * @cpu_list: The cpu list to add to
+ * @start_cpu: The start of the range to add
+ * @end_cpu: The end of the range to add.
+ *
+ * Adds a sorted unique item into @cpu_list based on @start_cpu and @end_cpu.
+ * It removes duplicates in @cpu_list, and will even merge lists if a
+ * new range is entered that will fill a gap. That is, if @cpu_list has
+ * "1-3" and "6-7", and @start_cpu is 4 and @end_cpu is 5, it will combined
+ * the two elements into a single list item of "1-7".
+ */
+static void add_cpus(struct cpu_list **cpu_list, int start_cpu, int end_cpu)
+{
+ struct cpu_list *list;
+
+ while (*cpu_list && (*cpu_list)->end_cpu + 1 < start_cpu)
+ cpu_list = &(*cpu_list)->next;
+
+ if (!*cpu_list) {
+ *cpu_list = malloc(sizeof(struct cpu_list));
+ (*cpu_list)->start_cpu = start_cpu;
+ (*cpu_list)->end_cpu = end_cpu;
+ (*cpu_list)->next = NULL;
+ return;
+ }
+
+ /* Look to concatinate */
+ if (end_cpu > (*cpu_list)->start_cpu &&
+ start_cpu <= (*cpu_list)->end_cpu + 1) {
+ if (start_cpu < (*cpu_list)->start_cpu)
+ (*cpu_list)->start_cpu = start_cpu;
+ list = (*cpu_list)->next;
+ while (list && list->start_cpu <= end_cpu + 1) {
+ (*cpu_list)->end_cpu = list->end_cpu;
+ (*cpu_list)->next = list->next;
+ free(list);
+ list = (*cpu_list)->next;
+ }
+ if ((*cpu_list)->end_cpu < end_cpu)
+ (*cpu_list)->end_cpu = end_cpu;
+ return;
+ }
+
+ /* Check for overlaps */
+ if (end_cpu >= (*cpu_list)->start_cpu - 1) {
+ (*cpu_list)->start_cpu = start_cpu;
+ return;
+ }
+
+ list = malloc(sizeof(struct cpu_list));
+ list->start_cpu = start_cpu;
+ list->end_cpu = end_cpu;
+ list->next = (*cpu_list)->next;
+ (*cpu_list)->next = list;
+}
+
+/**
+ * count_cpus - Return the number of CPUs in a list
+ * @cpu_list: The list of CPUs to count
+ *
+ * Reads the list of CPUs in @cpu_list. It als will free the
+ * list as it reads it, so this can only be called once on @cpu_list.
+ * It also checks if the CPUs in @cpu_list are less than cpu_count
+ * (the number of discovered CPUs).
+ *
+ * Returns the number of CPUs in @cpu_list, or -1 if any CPU in
+ * @cpu_list is greater or equal to cpu_count.
+ */
+static int count_cpus(struct cpu_list *cpu_list)
+{
+ struct cpu_list *list;
+ int cpus = 0;
+ int fail = 0;
+
+ while (cpu_list) {
+ list = cpu_list;
+ cpus += (list->end_cpu - list->start_cpu) + 1;
+ if (list->end_cpu >= cpu_count)
+ fail = 1;
+ cpu_list = list->next;
+ free(list);
+ }
+ return fail ? -1 : cpus;
+}
+
+/**
+ * append_cpus - Append a set of consecutive cpus to a string
+ * @buf: The string to append to
+ * @start: The cpu to start at.
+ * @end: The cpu to end at.
+ * @comma: The "," or "" to append before the cpu list.
+ * @total: The total length of buf.
+ *
+ * Realloc @buf to include @comma@start-@end.
+ * Updates @total to the new length of @buf.
+ */
+static char *append_cpus(char *buf, int start, int end,
+ const char *comma, int *total)
+{
+ int len;
+
+ if (start == end) {
+ len = snprintf(NULL, 0, "%s%d", comma, start);
+ buf = realloc(buf, *total + len + 1);
+ buf[*total] = 0;
+ snprintf(buf + *total, len + 1, "%s%d", comma, start);
+ } else {
+ len = snprintf(NULL, 0, "%s%d-%d", comma, start, end);
+ buf = realloc(buf, *total + len + 1);
+ buf[*total] = 0;
+ snprintf(buf + *total, len + 1, "%s%d-%d", comma,
+ start, end);
+ }
+ *total += len;
+ return buf;
+}
+
+/**
+ * make_new_list - convert cpu_list into a string
+ * @cpu_list: The list of CPUs to include
+ * @buf: The pointer to the allocated string to return
+ *
+ * Reads @cpu_list which contains a link list of consecutive
+ * CPUs, and returns the combined list in @buf.
+ * If cpu_list has "1", "3" and "6-8", buf would return
+ * "1,3,6-8"
+ */
+static void make_new_list(struct cpu_list *cpu_list, char **buf)
+{
+ char *comma = "";
+ int total = 0;
+
+ while (cpu_list) {
+ *buf = append_cpus(*buf, cpu_list->start_cpu, cpu_list->end_cpu,
+ comma, &total);
+ comma = ",";
+ cpu_list = cpu_list->next;
+ }
+}
+
+/**
+ * make_other_cpu_list - parse cpu list and return all other CPUs
+ * @setcpu: string listing the CPUs to exclude
+ * @cpus: The buffer to return the list of CPUs not in setcpu.
+ *
+ * @setcpu is expected to be compressed by calc_nr_cpus().
+ *
+ * Reads @setcpu and uses cpu_count (number of all CPUs), to return
+ * a list of CPUs not included in @setcpu. For example, if
+ * @setcpu is "1-5" and cpu_count is 8, then @cpus would contain
+ * "0,6-7".
+ */
+static void make_other_cpu_list(const char *setcpu, char **cpus)
+{
+ const char *p = setcpu;
+ const char *comma = "";
+ int curr_cpu = 0;
+ int cpu;
+ int total = 0;
+
+ while (*p && curr_cpu < cpu_count) {
+ cpu = atoi(p);
+ if (cpu > curr_cpu) {
+ *cpus = append_cpus(*cpus, curr_cpu, cpu - 1,
+ comma, &total);
+ comma = ",";
+ }
+ while (isdigit(*p))
+ p++;
+ if (*p == '-') {
+ p++;
+ cpu = atoi(p);
+ while (isdigit(*p))
+ p++;
+ }
+ curr_cpu = cpu + 1;
+ if (*p)
+ p++;
+ }
+
+ if (curr_cpu < cpu_count) {
+ *cpus = append_cpus(*cpus, curr_cpu, cpu_count - 1,
+ comma, &total);
+ }
+}
+
+/**
+ * calc_nr_cpus - parse cpu list for list of cpus.
+ * @setcpu: string listing the CPUs to include
+ * @buf: The buffer to return as a compressed list.
+ *
+ * Reads @setcpu and removes duplicates, it also sets @buf to be
+ * a consolidated list. For example, if @setcpu is "1,2,4,3-5"
+ * @buf would become "1-5" and 5 would be returned.
+ *
+ * Returns the number of cpus listed in @setcpu.
+ */
+static int calc_nr_cpus(const char *setcpu, char **buf)
+{
+ struct cpu_list *cpu_list = NULL;
+ const char *p;
+ int end_cpu;
+ int cpu;
+
+ for (p = setcpu; *p; ) {
+ cpu = atoi(p);
+ if (cpu < 0 || (!cpu && *p != '0'))
+ goto err;
+
+ while (isdigit(*p))
+ p++;
+ if (*p == '-') {
+ p++;
+ end_cpu = atoi(p);
+ if (end_cpu < cpu || (!end_cpu && *p != '0'))
+ goto err;
+ while (isdigit(*p))
+ p++;
+ } else
+ end_cpu = cpu;
+
+ add_cpus(&cpu_list, cpu, end_cpu);
+ if (*p == ',')
+ p++;
+ }
+
+ make_new_list(cpu_list, buf);
+ return count_cpus(cpu_list);
+ err:
+ /* Frees the list */
+ count_cpus(cpu_list);
+ return -1;
+}
+
+static const char *join_thread(pthread_t *thread)
+{
+ void *result;
+
+ pthread_join(*thread, &result);
+ return result;
+}
+
+static void do_sleep(u64 next)
+{
+ struct timespec req;
+
+ req.tv_nsec = next * 1000;
+ req.tv_sec = 0;
+ while (req.tv_nsec > 1000000000UL) {
+ req.tv_nsec -= 1000000000UL;
+ req.tv_sec++;
+ }
+ nanosleep(&req, NULL);
+}
+
+/**
+ * calculate_loops_per_ms - calculate the number of loops per ms
+ * @overhead: Return the overhead of the call around run_loops()
+ *
+ * Runs the do_runtime() to see how long it takes. That returns
+ * how long the loops took (data->last_time), and the overhead can
+ * be calculated by that diff.
+ *
+ * Returns the length of time it took to run for 1000 loops.
+ */
+static u64 calculate_loops_per_ms(u64 *overhead)
+{
+ struct sched_data sd = { };
+ u64 test_loops = 100000;
+ u64 loops;
+ u64 diff;
+ u64 odiff;
+ u64 start;
+ u64 end;
+
+ sd.prime = 2;
+
+ /* Sleep 1ms to help flush a bit of cache */
+ do_sleep(1000);
+
+ start = run_loops(&sd, test_loops);
+
+ sd.deadline_us = start * 2;
+ sd.runtime_us = start;
+ sd.loops_per_period = test_loops;
+
+ /* Again try to dirty some cache */
+ do_sleep(1000);
+
+ start = get_time_us();
+ do_runtime(0, &sd, start + sd.deadline_us);
+ end = get_time_us();
+
+ diff = end - start;
+
+ /*
+ * Based on the time it took to run test_loops, figure
+ * out how many loops it may take to run for 1000us.
+ *
+ * last_time / test_loops = 1000us / loops
+ * or
+ * loops = test_loops * 1000us / last_time
+ */
+
+ loops = 1000ULL * test_loops / sd.last_time;
+
+ printf("%lld test loops took %lldus total (%lld internal)\n"
+ "calculated loops for 1000us=%lld\n",
+ test_loops, diff, sd.last_time, loops);
+
+ sd.deadline_us = 2000;
+ sd.runtime_us = 1000;
+ sd.loops_per_period = loops;
+
+ test_loops = loops;
+
+ do_sleep(1000);
+
+ start = get_time_us();
+ do_runtime(0, &sd, start + sd.deadline_us);
+ end = get_time_us();
+
+ odiff = end - start;
+
+ /*
+ * Use this new calcualtion to recalculate the number of loops
+ * for 1000us
+ */
+ loops = 1000ULL * loops / sd.last_time;
+
+ *overhead = odiff - sd.last_time;
+
+ printf("%lld test loops took %lldus total (%lld internal)\n"
+ "New calculated loops for 1000us=%lld\n"
+ "Diff from last calculation: %lld loops\n",
+ test_loops, odiff, sd.last_time, loops, loops - test_loops);
+
+ return loops;
+}
+
+int main (int argc, char **argv)
+{
+ struct sched_data *sched_data;
+ struct sched_data *sd;
+ struct sched_data rt_sched_data;
+ const char *res;
+ const char *setcpu = NULL;
+ char *setcpu_buf = NULL;
+ char *allcpu_buf = NULL;
+ pthread_t *thread;
+ pthread_t rt_thread;
+ unsigned int interval = 1000;
+ unsigned int step = 500;
+ u64 loop_time;
+ u64 loops;
+ u64 runtime;
+ u64 overhead;
+ u64 start_period;
+ u64 end_period;
+ int nr_cpus;
+ int all_cpus = 1;
+ int run_percent = 100;
+ int percent = 80;
+ int rt_task = 0;
+ int i;
+ int c;
+
+ cpu_count = sysconf(_SC_NPROCESSORS_CONF);
+ if (cpu_count < 1) {
+ fprintf(stderr, "Can not calculate number of CPUS\n");
+ exit(-1);
+ }
+
+ while ((c = getopt(argc, argv, "+hbr:c:p:P:t:s:")) >= 0) {
+ switch (c) {
+ case 'b':
+ all_cpus = 0;
+ break;
+ case 'c':
+ all_cpus = 0;
+ setcpu = optarg;
+ break;
+ case 'i':
+ interval = atoi(optarg);
+ break;
+ case 'p':
+ percent = atoi(optarg);
+ break;
+ case 'P':
+ run_percent = atoi(optarg);
+ break;
+ case 's':
+ step = atoi(optarg);
+ break;
+ case 't':
+ nr_threads = atoi(optarg);
+ break;
+ case 'r':
+ rt_task = atoi(optarg);
+ break;
+ case 'h':
+ default:
+ usage(argv);
+ }
+ }
+
+ if (rt_task < 0 || rt_task > 98) {
+ fprintf(stderr, "RT task can only be from 1 to 98\n");
+ exit(-1);
+ }
+
+ if (percent < 1 || percent > 100 || run_percent < 1 || run_percent > 100) {
+ fprintf(stderr, "Percent must be between 1 and 100\n");
+ exit(-1);
+ }
+
+ if (setcpu) {
+ nr_cpus = calc_nr_cpus(setcpu, &setcpu_buf);
+ if (nr_cpus < 0) {
+ fprintf(stderr, "Invalid cpu input '%s'\n", setcpu);
+ exit(-1);
+ }
+ } else
+ nr_cpus = 1;
+
+ if (all_cpus)
+ nr_cpus = cpu_count;
+
+ if (cpu_count == nr_cpus)
+ all_cpus = 1;
+
+ /* -b has us bind to the last CPU. */
+ if (!all_cpus && !setcpu) {
+ setcpu_buf = malloc(10);
+ if (!setcpu_buf) {
+ perror("malloc");
+ exit(-1);
+ }
+ sprintf(setcpu_buf, "%d", cpu_count - 1);
+ setcpu = setcpu_buf;
+ }
+
+ /*
+ * Now the amount of bandwidth each tasks takes will be
+ * percent * nr_cpus / nr_threads. Now if nr_threads is
+ * But the amount of any one thread can not be more than
+ * 90 of the CPUs.
+ */
+ percent = (percent * nr_cpus) / nr_threads;
+ if (percent > 90)
+ percent = 90;
+
+ cpusetp = CPU_ALLOC(cpu_count);
+ cpuset_size = CPU_ALLOC_SIZE(cpu_count);
+ if (!cpusetp) {
+ perror("allocating cpuset");
+ exit(-1);
+ }
+
+ setup_ftrace_marker();
+
+ thread = calloc(nr_threads, sizeof(*thread));
+ sched_data = calloc(nr_threads, sizeof(*sched_data));
+ if (!thread || !sched_data) {
+ perror("allocating threads");
+ exit(-1);
+ }
+
+ if (mlockall(MCL_CURRENT|MCL_FUTURE) == -1) {
+ perror("mlockall");
+ }
+
+ /*
+ * Run at prio 99 bound to the last CPU, and try to calculate
+ * the time it takes to run the loops.
+ */
+ set_prio(99);
+ bind_cpu(cpu_count - 1);
+
+ loops = calculate_loops_per_ms(&overhead);
+
+ printf("Setup:\n");
+ printf(" percent per task:%d", percent);
+ if (run_percent < 100)
+ printf(" run-percent:%d", run_percent);
+ printf(" nr_cpus:%d", nr_cpus);
+ if (setcpu)
+ printf(" (%s)", setcpu);
+ printf(" loops:%lld overhead:%lldus\n", loops, overhead);
+
+ again:
+ /* Set up the data while sill in SCHED_FIFO */
+ for (i = 0; i < nr_threads; i++) {
+ sd = &sched_data[i];
+ /*
+ * Interval is the deadline/period
+ * The runtime is the percentage of that period.
+ */
+ runtime = interval * percent / 100;
+ if (runtime < overhead) {
+ fprintf(stderr, "Run time too short: %lld us\n",
+ runtime);
+ fprintf(stderr, "Read context takes %lld us\n",
+ overhead);
+ exit(-1);
+ }
+ if (runtime < 2000) {
+ /*
+ * If the runtime is less than 2ms, then we better
+ * have HRTICK enabled.
+ */
+ if (!setup_hr_tick()) {
+ fprintf(stderr, "For less that 2ms run times, you need to\n"
+ "have HRTICK enabled in debugfs/sched_features\n");
+ exit(-1);
+ }
+ }
+ sd->runtime_us = runtime;
+ /* Account for the reading of context switches */
+ runtime -= overhead;
+ /*
+ * loops is # of loops per ms, convert to us and
+ * take 5% off of it.
+ * loops * %run_percent / 1000
+ */
+ loop_time = runtime * run_percent / 100;
+ sd->loops_per_period = loop_time * loops / 1000;
+
+ sd->deadline_us = interval;
+
+ /* Make sure that we can make our deadlines */
+ start_period = get_time_us();
+ do_runtime(gettid(), sd, start_period);
+ end_period = get_time_us();
+ if (end_period - start_period > sd->runtime_us) {
+ printf("Failed to perform task within runtime: Missed by %lld us\n",
+ end_period - start_period - sd->runtime_us);
+ overhead += end_period - start_period - sd->runtime_us;
+ printf("New overhead=%lldus\n", overhead);
+ goto again;
+ }
+
+ printf(" Tested at %lldus of %lldus\n",
+ end_period - start_period, sd->runtime_us);
+
+ interval += step;
+ }
+
+ set_prio(0);
+
+ unbind_cpu();
+
+ pthread_barrier_init(&barrier, NULL, nr_threads + 1 + !!rt_task);
+
+ for (i = 0; i < nr_threads; i++) {
+ sd = &sched_data[i];
+ pthread_create(&thread[i], NULL, run_deadline, sd);
+ }
+
+ if (rt_task) {
+ /* Make sure we are a higher priority than the spinner */
+ set_prio(rt_task + 1);
+
+ rt_sched_data.prio = rt_task;
+ pthread_create(&rt_thread, NULL, run_rt_spin, &rt_sched_data);
+ }
+
+ pthread_barrier_wait(&barrier);
+
+ if (fail) {
+ exit(-1);
+ }
+
+ if (!all_cpus) {
+ int *pids;
+
+ atexit(teardown);
+
+ make_other_cpu_list(setcpu, &allcpu_buf);
+
+ res = make_cpuset(CPUSET_ALL, allcpu_buf, "0",
+ CPUSET_FL_SET_LOADBALANCE |
+ CPUSET_FL_CLONE_CHILDREN |
+ CPUSET_FL_ALL_TASKS);
+ if (res) {
+ perror(res);
+ exit(-1);
+ }
+
+ pids = calloc(nr_threads + !!rt_task + 1, sizeof(int));
+ if (!pids) {
+ perror("Allocating pids");
+ exit(-1);
+ }
+
+ for (i = 0; i < nr_threads; i++)
+ pids[i] = sched_data[i].tid;
+ if (rt_task)
+ pids[i++] = rt_sched_data.tid;
+
+ res = make_cpuset(CPUSET_LOCAL, setcpu, "0",
+ CPUSET_FL_CPU_EXCLUSIVE |
+ CPUSET_FL_SET_LOADBALANCE |
+ CPUSET_FL_CLONE_CHILDREN |
+ CPUSET_FL_TASKS, pids);
+ free(pids);
+ if (res) {
+ perror(res);
+ fprintf(stderr, "Check if other cpusets exist that conflict\n");
+ exit(-1);
+ }
+
+ system("cat /sys/fs/cgroup/cpuset/my_cpuset/tasks");
+ }
+
+ pthread_barrier_wait(&barrier);
+
+ if (fail)
+ exit(-1);
+
+ pthread_barrier_wait(&barrier);
+
+ if (!fail)
+ sleep(10);
+
+ done = 1;
+ if (rt_task) {
+ res = join_thread(&rt_thread);
+ if (res)
+ printf("RT Thread failed: %s\n", res);
+ }
+
+ for (i = 0; i < nr_threads; i++) {
+
+ sd = &sched_data[i];
+
+ res = join_thread(&thread[i]);
+ if (res) {
+ printf("Thread %d failed: %s\n", i, res);
+ continue;
+ }
+
+ printf("\n[%d]\n", sd->tid);
+ printf("missed deadlines = %d\n", sd->missed_deadlines);
+ printf("missed periods = %d\n", sd->missed_periods);
+ printf("Total adjustments = %lld us\n", sd->total_adjust);
+ printf("# adjustments = %lld avg: %lld us\n",
+ sd->nr_adjust, sd->total_adjust / sd->nr_adjust);
+ printf("deadline : %lld us\n", sd->deadline_us);
+ printf("runtime : %lld us\n", sd->runtime_us);
+ printf("nr_periods : %lld\n", sd->nr_periods);
+ printf("max_time: %lldus", sd->max_time);
+ printf("\tmin_time: %lldus", sd->min_time);
+ printf("\tavg_time: %lldus\n", sd->total_time / sd->nr_periods);
+ printf("ctx switches vol:%d nonvol:%d migration:%d\n",
+ sd->vol, sd->nonvol, sd->migrate);
+ printf("highes prime: %lld\n", sd->prime);
+ printf("\n");
+ }
+
+ free(setcpu_buf);
+ return 0;
+}
--
2.4.11
\
 
 \ /
  Last update: 2016-06-23 14:41    [W:0.130 / U:0.308 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site