| /****************************************************************************** |
| * |
| * Copyright © International Business Machines Corp., 2007, 2008 |
| * |
| * 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; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * 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 |
| * |
| * NAME |
| * hrtimer-prio.c |
| * |
| * DESCRIPTION |
| * Test the latency of hrtimers under rt load. |
| * The busy_threads should run at a priority higher than the system |
| * softirq_hrtimer, but lower than the timer_thread. The timer_thread |
| * measure the time it takes to return from a nanosleep call. If the |
| * lower priority threads can increase the latency of the higher |
| * priority thread, it is considered a failure. |
| * |
| * USAGE: |
| * Use run_auto.sh script in current directory to build and run test. |
| * |
| * AUTHOR |
| * Darren Hart <dvhltc@us.ibm.com> |
| * |
| * HISTORY |
| * 2007-Aug-08: Initial version by Darren Hart <dvhltc@us.ibm.com> |
| * |
| * This line has to be added to avoid a stupid CVS problem |
| *****************************************************************************/ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <math.h> |
| #include <librttest.h> |
| #include <libstats.h> |
| |
| #define DEF_MED_PRIO 60 // (softirqd-hrtimer,98) |
| #define DEF_ITERATIONS 10000 |
| #define HIST_BUCKETS 100 |
| #define DEF_BUSY_TIME 10 // Duration of busy work in milliseconds |
| #define DEF_SLEEP_TIME 10000 // Duration of nanosleep in nanoseconds |
| #define DEF_CRITERIA 10 // maximum timer latency in microseconds |
| |
| static int med_prio = DEF_MED_PRIO; |
| static int high_prio; |
| static int busy_time = DEF_BUSY_TIME; |
| static int iterations = DEF_ITERATIONS; |
| static int busy_threads; |
| |
| static stats_container_t dat; |
| static stats_record_t rec; |
| static atomic_t busy_threads_started; |
| static unsigned long min_delta; |
| static unsigned long max_delta; |
| |
| void usage(void) |
| { |
| rt_help(); |
| printf("hrtimer-prio specific options:\n"); |
| printf(" -t# #:busy work time in ms, defaults to %d ms\n", DEF_BUSY_TIME); |
| printf(" -i# #:number of iterations, defaults to %d\n", DEF_ITERATIONS); |
| printf(" -n# #:number of busy threads, defaults to NR_CPUS*2\n"); |
| printf(" -f# #:rt fifo priority of busy threads (1,98), defaults to %d\n", DEF_MED_PRIO); |
| printf(" -m# #:maximum timer latency in microseconds, defaults to %d\n", DEF_CRITERIA); |
| } |
| |
| int parse_args(int c, char *v) |
| { |
| |
| int handled = 1; |
| switch (c) { |
| case 'h': |
| usage(); |
| exit(0); |
| case 't': |
| busy_time = atoi(v); |
| break; |
| case 'n': |
| busy_threads = atoi(v); |
| break; |
| case 'f': |
| med_prio = MIN(atoi(v), 98); |
| break; |
| case 'i': |
| iterations = atoi(v); |
| if (iterations < 100) { |
| fprintf(stderr, "Number of iterations cannot be less than 100.\n"); |
| exit(1); |
| } |
| break; |
| default: |
| handled = 0; |
| break; |
| } |
| return handled; |
| } |
| |
| void *busy_thread(void *thread) |
| { |
| atomic_inc(&busy_threads_started); |
| while (1) { |
| busy_work_ms(busy_time); |
| sched_yield(); |
| } |
| return NULL; |
| } |
| |
| void *timer_thread(void *thread) |
| { |
| int i; |
| nsec_t start, end; |
| unsigned long delta_us; |
| while (atomic_get(&busy_threads_started) < busy_threads) |
| { |
| rt_nanosleep(10000); |
| } |
| printf("All Busy Threads started, commencing test\n"); // FIXME: use debug infrastructure |
| max_delta = 0; |
| for (i = 0; i < iterations; i++) |
| { |
| start = rt_gettime(); |
| rt_nanosleep(DEF_SLEEP_TIME); |
| end = rt_gettime(); |
| delta_us = ((unsigned long)(end - start) - DEF_SLEEP_TIME)/NS_PER_US; |
| rec.x = i; |
| rec.y = delta_us; |
| stats_container_append(&dat, rec); |
| max_delta = MAX(max_delta, delta_us); |
| min_delta = (i == 0) ? delta_us : MIN(min_delta, delta_us); |
| } |
| return NULL; |
| } |
| |
| int main(int argc, char *argv[]) |
| { |
| int ret = 1; |
| int b; |
| float avg_delta; |
| int t_id; |
| setup(); |
| busy_threads = 2 * sysconf(_SC_NPROCESSORS_ONLN); // default busy_threads |
| pass_criteria = DEF_CRITERIA; |
| rt_init("f:i:jhn:t:", parse_args, argc, argv); |
| high_prio = med_prio + 1; |
| |
| // Set main()'s prio to one above the timer_thread so it is sure to not |
| // be starved |
| if (set_priority(high_prio+1) < 0) |
| { |
| printf("Failed to set main()'s priority to %d\n", high_prio+1); |
| exit(1); |
| } |
| |
| printf("\n-------------------------------------------\n"); |
| printf("High Resolution Timer Priority (Starvation)\n"); |
| printf("-------------------------------------------\n\n"); |
| printf("Running %d iterations\n", iterations); |
| printf("Running with %d busy threads\n", busy_threads); |
| printf("Busy thread work time: %d\n", busy_time); |
| printf("Busy thread priority: %d\n", med_prio); |
| printf("Timer thread priority: %d\n", high_prio); |
| |
| stats_container_t hist; |
| stats_quantiles_t quantiles; |
| if (stats_container_init(&dat, iterations)) { |
| printf("Cannot init stat containers for dat\n"); |
| exit(1); |
| } |
| if (stats_container_init(&hist, HIST_BUCKETS)) { |
| printf("Cannot init stat containers for hist\n"); |
| exit(1); |
| } |
| if (stats_quantiles_init(&quantiles, (int)log10(iterations))) { |
| printf("Cannot init stat quantiles\n"); |
| exit(1); |
| } |
| |
| t_id = create_fifo_thread(timer_thread, NULL, high_prio); |
| if (t_id == -1) { |
| printf("Failed to create timer thread\n"); |
| exit(1); |
| } |
| for (b = 0; b < busy_threads; b++) |
| { |
| if (create_fifo_thread(busy_thread, NULL, med_prio) < 0) { |
| printf("Failed to create a busy thread\n"); |
| exit(1); |
| } |
| } |
| join_thread(t_id); |
| |
| avg_delta = stats_avg(&dat); |
| stats_hist(&hist, &dat); |
| stats_container_save("samples", "High Resolution Timer Latency Scatter Plot",\ |
| "Iteration", "Latency (us)", &dat, "points"); |
| stats_container_save("hist", "High Resolution Timer Latency Histogram",\ |
| "Latency (us)", "Samples", &hist, "steps"); |
| |
| if (max_delta <= pass_criteria) |
| ret = 0; |
| |
| printf("Minimum: %ld us\n", min_delta); |
| printf("Maximum: %ld us\n", max_delta); |
| printf("Average: %f us\n", avg_delta); |
| printf("Standard Deviation: %f\n", stats_stddev(&dat)); |
| printf("Quantiles:\n"); |
| stats_quantiles_calc(&dat, &quantiles); |
| stats_quantiles_print(&quantiles); |
| printf("\nCriteria: Maximum wakeup latency < %lu us\n", (unsigned long)pass_criteria); |
| printf("Result: %s\n", ret ? "FAIL" : "PASS"); |
| |
| return ret; |
| } |