On Mon, 2009-07-06 at 10:26 -0400, Steven Rostedt wrote: > > On Tue, 23 Jun 2009, Subrata Modak wrote: > > > Steven, > > > > Do you remember this ? > > Yes, and I finally got time to fix it up a little ;-) Great. Forwarding to the ltp mailing list as well. Thanks steven. Regards-- Subrata > > OK, here it is. To run it, just do: > > ./rt-migrate-test -c > > You need to be root since it changes the priorities. It will check the > number of processors and create N+1 threads (where N is the number of > processors). Each thread will wake up at the same time and try to do a > busy loop for 20 millisecs. If a lower prio thread runs for 1ms before a > higher prio thread, it will fail. This should test the migration of the > threads to make sure that the higher prio threads run first. > > Sorry for taking so long. > > -- Steve > > > > > > > Regards-- > > Subrata > > > > On Mon, 2008-12-15 at 08:52 -0500, Steven Rostedt wrote: > > > On Mon, 15 Dec 2008, Subrata Modak wrote: > > > > > > > > > > Lets see, you wanted my rt migration test. I'll clean that up now. Is > > > > > > > > Oh yes. > > > > > > OK, I'm cleaning it up to be more for production use. > > > > > > > > > > > > there something else I needed to send you? If you remember. > > > > > > > > I am not quite sure now. Will let you know in future. > > > > > > OK, thanks, > > > > > > -- Steve > > > > > > >
/* * rt-migrate-test.c * * Copyright (C) 2007-2009 Steven Rostedt <srostedt@xxxxxxxxxx> * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #define _GNU_SOURCE #include <stdio.h> #ifndef __USE_XOPEN2K # define __USE_XOPEN2K #endif #include <stdlib.h> #include <string.h> #include <getopt.h> #include <stdarg.h> #include <unistd.h> #include <ctype.h> #include <time.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <signal.h> #include <sys/time.h> #include <linux/unistd.h> #include <sys/syscall.h> #include <errno.h> #include <sched.h> #include <pthread.h> #ifdef LOGDEV # include <dump_log.h> # define do_logdev_open() open("/debug/logdev/write", O_WRONLY) # define do_logdev_close(ld) close(ld) __thread char buff[BUFSIZ]; static lgprint(int fd, const char *fmt, ...) { va_list ap; int n; va_start(ap, fmt); n = vsnprintf(buff, BUFSIZ, fmt, ap); va_end(ap); write(fd, buff, n); } #else # define do_logdev_open() (0) # define do_logdev_close(lfd) do { } while(0) # define logdev_print_set(x) do { } while(0) # define logdev_switch_set(x) do { } while(0) # define lgprint(x...) do { } while(0) #endif #define gettid() syscall(__NR_gettid) #define VERSION_STRING "V 0.3" int nr_tasks; int lfd; #define nano2sec(nan) (nan / 1000000000ULL) #define nano2ms(nan) (nan / 1000000ULL) #define nano2usec(nan) (nan / 1000ULL) #define usec2nano(sec) (sec * 1000ULL) #define ms2nano(ms) (ms * 1000000ULL) #define sec2nano(sec) (sec * 1000000000ULL) #define INTERVAL ms2nano(100ULL) #define RUN_INTERVAL ms2nano(20ULL) #define NR_RUNS 50 #define PRIO_START 2 /* 1 millisec off */ #define MAX_ERR usec2nano(1000) #define PROGRESS_CHARS 70 static unsigned long long interval = INTERVAL; static unsigned long long run_interval = RUN_INTERVAL; static unsigned long long max_err = MAX_ERR; static int nr_runs = NR_RUNS; static int prio_start = PRIO_START; static int check; static int stop; static unsigned long long now; static int done; static int loop; static pthread_barrier_t start_barrier; static pthread_barrier_t end_barrier; static unsigned long long **intervals; static unsigned long long **intervals_length; static unsigned long **intervals_loops; static long *thread_pids; static char buffer[BUFSIZ]; static void perr(char *fmt, ...) { va_list ap; va_start(ap, fmt); vsnprintf(buffer, BUFSIZ, fmt, ap); va_end(ap); perror(buffer); fflush(stderr); exit(-1); } static void print_progress_bar(int percent) { int i; int p; if (percent > 100) percent = 100; /* Use stderr, so we don't capture it */ putc('\r', stderr); putc('|', stderr); for (i=0; i < PROGRESS_CHARS; i++) putc(' ', stderr); putc('|', stderr); putc('\r', stderr); putc('|', stderr); p = PROGRESS_CHARS * percent / 100; for (i=0; i < p; i++) putc('-', stderr); fflush(stderr); } static void usage(char **argv) { char *arg = argv[0]; char *p = arg+strlen(arg); while (p >= arg && *p != '/') p--; p++; printf("%s %s\n", p, VERSION_STRING); printf("Usage:\n" "%s <options> nr_tasks\n\n" "-p prio --prio prio base priority to start RT tasks with (2) \n" "-r time --run-time time Run time (ms) to busy loop the threads (20)\n" "-s time --sleep-time time Sleep time (ms) between intervals (100)\n" "-m time --maxerr time Max allowed error (microsecs)\n" "-l loops --loops loops Number of iterations to run (50)\n" "-c --check Stop if lower prio task is quick than higher (off)\n" " () above are defaults \n", p); exit(0); } static void parse_options (int argc, char *argv[]) { for (;;) { int option_index = 0; /** Options for getopt */ static struct option long_options[] = { {"prio", required_argument, NULL, 'p'}, {"run-time", required_argument, NULL, 'r'}, {"sleep-time", required_argument, NULL, 's'}, {"maxerr", required_argument, NULL, 'm'}, {"loops", required_argument, NULL, 'l'}, {"check", no_argument, NULL, 'c'}, {"help", no_argument, NULL, '?'}, {NULL, 0, NULL, 0} }; int c = getopt_long (argc, argv, "p:r:s:m:l:ch", long_options, &option_index); if (c == -1) break; switch (c) { case 'p': prio_start = atoi(optarg); break; case 'r': run_interval = atoi(optarg); break; case 's': interval = atoi(optarg); break; case 'l': nr_runs = atoi(optarg); break; case 'm': max_err = usec2nano(atoi(optarg)); break; case 'c': check = 1; break; case '?': case 'h': usage(argv); break; } } } static unsigned long long get_time(void) { struct timeval tv; unsigned long long time; gettimeofday(&tv, NULL); time = sec2nano(tv.tv_sec); time += usec2nano(tv.tv_usec); return time; } static void record_time(int id, unsigned long long time, unsigned long l) { unsigned long long ltime; if (loop >= nr_runs) return; time -= now; ltime = get_time(); ltime -= now; intervals[loop][id] = time; intervals_length[loop][id] = ltime; intervals_loops[loop][id] = l; } static void print_results(void) { int i; int t; unsigned long long tasks_max[nr_tasks]; unsigned long long tasks_min[nr_tasks]; unsigned long long tasks_avg[nr_tasks]; memset(tasks_max, 0, sizeof(tasks_max[0])*nr_tasks); memset(tasks_min, 0xff, sizeof(tasks_min[0])*nr_tasks); memset(tasks_avg, 0, sizeof(tasks_avg[0])*nr_tasks); printf("Iter: "); for (t=0; t < nr_tasks; t++) printf("%6d ", t); printf("\n"); for (i=0; i < nr_runs; i++) { printf("%4d: ", i); for (t=0; t < nr_tasks; t++) { unsigned long long itv = intervals[i][t]; if (tasks_max[t] < itv) tasks_max[t] = itv; if (tasks_min[t] > itv) tasks_min[t] = itv; tasks_avg[t] += itv; printf("%6lld ", nano2usec(itv)); } printf("\n"); printf(" len: "); for (t=0; t < nr_tasks; t++) { unsigned long long len = intervals_length[i][t]; printf("%6lld ", nano2usec(len)); } printf("\n"); printf(" loops: "); for (t=0; t < nr_tasks; t++) { unsigned long loops = intervals_loops[i][t]; printf("%6ld ", loops); } printf("\n"); printf("\n"); } printf("Parent pid: %d\n", getpid()); for (t=0; t < nr_tasks; t++) { printf(" Task %d (prio %d) (pid %ld):\n", t, t + prio_start, thread_pids[t]); printf(" Max: %lld us\n", nano2usec(tasks_max[t])); printf(" Min: %lld us\n", nano2usec(tasks_min[t])); printf(" Tot: %lld us\n", nano2usec(tasks_avg[t])); printf(" Avg: %lld us\n", nano2usec(tasks_avg[t] / nr_runs)); printf("\n"); } if (check) { if (check < 0) printf(" Failed!\n"); else printf(" Passed!\n"); } } static unsigned long busy_loop(unsigned long long start_time) { unsigned long long time; unsigned long l = 0; do { l++; time = get_time(); } while ((time - start_time) < RUN_INTERVAL); return l; } void *start_task(void *data) { long id = (long)data; unsigned long long start_time; struct sched_param param = { .sched_priority = id + prio_start, }; int ret; int high = 0; cpu_set_t cpumask; cpu_set_t save_cpumask; int cpu = 0; unsigned long l; long pid; ret = sched_getaffinity(0, sizeof(save_cpumask), &save_cpumask); if (ret < 0) perr("getting affinity"); pid = gettid(); /* Check if we are the highest prio task */ if (id == nr_tasks-1) high = 1; ret = sched_setscheduler(0, SCHED_FIFO, ¶m); if (ret < 0 && !id) fprintf(stderr, "Warning, can't set priorities\n"); while (!done) { if (high) { /* rotate around the CPUS */ if (!CPU_ISSET(cpu, &save_cpumask)) cpu = 0; CPU_ZERO(&cpumask); CPU_SET(cpu, &cpumask); cpu++; sched_setaffinity(0, sizeof(cpumask), &cpumask); } pthread_barrier_wait(&start_barrier); start_time = get_time(); lgprint(lfd, "Thread %d: started %lld diff %lld\n", pid, start_time, start_time - now); l = busy_loop(start_time); record_time(id, start_time, l); pthread_barrier_wait(&end_barrier); } return (void*)pid; } static int check_times(int l) { int i; unsigned long long last; unsigned long long last_loops; unsigned long long last_length; for (i=0; i < nr_tasks; i++) { if (i && last < intervals[l][i] && ((intervals[l][i] - last) > max_err)) { /* * May be a false positive. * Make sure that we did more loops * our start is before the end * and the end should be tested. */ if (intervals_loops[l][i] < last_loops || intervals[l][i] > last_length || (intervals_length[l][i] > last_length && intervals_length[l][i] - last_length > max_err)) { check = -1; return 1; } } last = intervals[l][i]; last_loops = intervals_loops[l][i]; last_length = intervals_length[l][i]; } return 0; } static void stop_log(int sig) { logdev_print_set(0); logdev_switch_set(0); stop = 1; } static int count_cpus(void) { FILE *fp; char buf[1024]; int cpus = 0; char *pbuf; size_t *pn; size_t n; int r; n = 1024; pn = &n; pbuf = buf; fp = fopen("/proc/cpuinfo", "r"); if (!fp) perr("Can not read cpuinfo"); while ((r = getline(&pbuf, pn, fp)) >= 0) { char *p; if (strncmp(buf, "processor", 9) != 0) continue; for (p = buf+9; isspace(*p); p++) ; if (*p == ':') cpus++; } fclose(fp); return cpus; } int main (int argc, char **argv) { pthread_t *threads; long i; int ret; struct timespec intv; struct sched_param param; parse_options(argc, argv); signal(SIGINT, stop_log); if (argc >= (optind + 1)) nr_tasks = atoi(argv[optind]); else nr_tasks = count_cpus() + 1; threads = malloc(sizeof(*threads) * nr_tasks); if (!threads) perr("malloc"); memset(threads, 0, sizeof(*threads) * nr_tasks); ret = pthread_barrier_init(&start_barrier, NULL, nr_tasks + 1); ret = pthread_barrier_init(&end_barrier, NULL, nr_tasks + 1); if (ret < 0) perr("pthread_barrier_init"); intervals = malloc(sizeof(void*) * nr_runs); if (!intervals) perr("malloc intervals array"); intervals_length = malloc(sizeof(void*) * nr_runs); if (!intervals_length) perr("malloc intervals length array"); intervals_loops = malloc(sizeof(void*) * nr_runs); if (!intervals_loops) perr("malloc intervals loops array"); thread_pids = malloc(sizeof(long) * nr_tasks); if (!thread_pids) perr("malloc thread_pids"); for (i=0; i < nr_runs; i++) { intervals[i] = malloc(sizeof(unsigned long long)*nr_tasks); if (!intervals[i]) perr("malloc intervals"); memset(intervals[i], 0, sizeof(unsigned long long)*nr_tasks); intervals_length[i] = malloc(sizeof(unsigned long long)*nr_tasks); if (!intervals_length[i]) perr("malloc length intervals"); memset(intervals_length[i], 0, sizeof(unsigned long long)*nr_tasks); intervals_loops[i] = malloc(sizeof(unsigned long)*nr_tasks); if (!intervals_loops[i]) perr("malloc loops intervals"); memset(intervals_loops[i], 0, sizeof(unsigned long)*nr_tasks); } for (i=0; i < nr_tasks; i++) { if (pthread_create(&threads[i], NULL, start_task, (void *)i)) perr("pthread_create"); } /* * Progress bar uses stderr to let users see it when * redirecting output. So we convert stderr to use line * buffering so the progress bar doesn't flicker. */ setlinebuf(stderr); /* up our prio above all tasks */ memset(¶m, 0, sizeof(param)); param.sched_priority = nr_tasks + prio_start; if (sched_setscheduler(0, SCHED_FIFO, ¶m)) fprintf(stderr, "Warning, can't set priority of main thread!\n"); intv.tv_sec = nano2sec(INTERVAL); intv.tv_nsec = INTERVAL % sec2nano(1); print_progress_bar(0); lfd = do_logdev_open(); logdev_print_set(1); logdev_switch_set(1); for (loop=0; loop < nr_runs; loop++) { unsigned long long end; now = get_time(); lgprint(lfd, "Loop %d now=%lld\n", loop, now); pthread_barrier_wait(&start_barrier); lgprint(lfd, "All running!!!\n"); nanosleep(&intv, NULL); print_progress_bar((loop * 100)/ nr_runs); end = get_time(); lgprint(lfd, "Loop %d end now=%lld diff=%lld\n", loop, end, end - now); pthread_barrier_wait(&end_barrier); if (stop || (check && check_times(loop))) { loop++; nr_runs = loop; break; } } do_logdev_close(lfd); putc('\n', stderr); pthread_barrier_wait(&start_barrier); done = 1; pthread_barrier_wait(&end_barrier); for (i=0; i < nr_tasks; i++) pthread_join(threads[i], (void*)&thread_pids[i]); logdev_print_set(0); logdev_switch_set(0); print_results(); if (stop) { /* * We use this test in bash while loops * So if we hit Ctrl-C then let the while * loop know to break. */ if (check < 0) exit(-1); else exit(1); } if (check < 0) exit(-1); else exit(0); return 0; }