summaryrefslogtreecommitdiffstats
path: root/tools/power/cpupower/bench
diff options
context:
space:
mode:
Diffstat (limited to 'tools/power/cpupower/bench')
-rw-r--r--tools/power/cpupower/bench/Makefile43
-rw-r--r--tools/power/cpupower/bench/README-BENCH124
-rw-r--r--tools/power/cpupower/bench/benchmark.c181
-rw-r--r--tools/power/cpupower/bench/benchmark.h16
-rw-r--r--tools/power/cpupower/bench/config.h23
-rw-r--r--tools/power/cpupower/bench/cpufreq-bench_plot.sh91
-rw-r--r--tools/power/cpupower/bench/cpufreq-bench_script.sh88
-rw-r--r--tools/power/cpupower/bench/example.cfg11
-rw-r--r--tools/power/cpupower/bench/main.c189
-rw-r--r--tools/power/cpupower/bench/parse.c225
-rw-r--r--tools/power/cpupower/bench/parse.h40
-rw-r--r--tools/power/cpupower/bench/system.c179
-rw-r--r--tools/power/cpupower/bench/system.h16
13 files changed, 1226 insertions, 0 deletions
diff --git a/tools/power/cpupower/bench/Makefile b/tools/power/cpupower/bench/Makefile
new file mode 100644
index 0000000000..d9d9923af8
--- /dev/null
+++ b/tools/power/cpupower/bench/Makefile
@@ -0,0 +1,43 @@
+# SPDX-License-Identifier: GPL-2.0
+OUTPUT := ./
+ifeq ("$(origin O)", "command line")
+ifneq ($(O),)
+ OUTPUT := $(O)/
+endif
+endif
+
+ifeq ($(strip $(STATIC)),true)
+LIBS = -L../ -L$(OUTPUT) -lm
+OBJS = $(OUTPUT)main.o $(OUTPUT)parse.o $(OUTPUT)system.o $(OUTPUT)benchmark.o \
+ $(OUTPUT)../lib/cpufreq.o $(OUTPUT)../lib/cpupower.o
+else
+LIBS = -L../ -L$(OUTPUT) -lm -lcpupower
+OBJS = $(OUTPUT)main.o $(OUTPUT)parse.o $(OUTPUT)system.o $(OUTPUT)benchmark.o
+endif
+
+CFLAGS += -D_GNU_SOURCE -I../lib -DDEFAULT_CONFIG_FILE=\"$(confdir)/cpufreq-bench.conf\"
+
+$(OUTPUT)%.o : %.c
+ $(ECHO) " CC " $@
+ $(QUIET) $(CC) -c $(CFLAGS) $< -o $@
+
+$(OUTPUT)cpufreq-bench: $(OBJS)
+ $(ECHO) " CC " $@
+ $(QUIET) $(CC) -o $@ $(CFLAGS) $(LDFLAGS) $(OBJS) $(LIBS)
+
+all: $(OUTPUT)cpufreq-bench
+
+install: $(OUTPUT)cpufreq-bench
+ mkdir -p $(DESTDIR)/$(sbindir)
+ mkdir -p $(DESTDIR)/$(bindir)
+ mkdir -p $(DESTDIR)/$(docdir)
+ mkdir -p $(DESTDIR)/$(confdir)
+ install -m 755 $(OUTPUT)cpufreq-bench $(DESTDIR)/$(sbindir)/cpufreq-bench
+ install -m 755 cpufreq-bench_plot.sh $(DESTDIR)/$(bindir)/cpufreq-bench_plot.sh
+ install -m 644 README-BENCH $(DESTDIR)/$(docdir)/README-BENCH
+ install -m 755 cpufreq-bench_script.sh $(DESTDIR)/$(docdir)/cpufreq-bench_script.sh
+ install -m 644 example.cfg $(DESTDIR)/$(confdir)/cpufreq-bench.conf
+
+clean:
+ rm -f $(OUTPUT)*.o
+ rm -f $(OUTPUT)cpufreq-bench
diff --git a/tools/power/cpupower/bench/README-BENCH b/tools/power/cpupower/bench/README-BENCH
new file mode 100644
index 0000000000..97727aed61
--- /dev/null
+++ b/tools/power/cpupower/bench/README-BENCH
@@ -0,0 +1,124 @@
+This is cpufreq-bench, a microbenchmark for the cpufreq framework.
+
+Purpose
+=======
+
+What is this benchmark for:
+ - Identify worst case performance loss when doing dynamic frequency
+ scaling using Linux kernel governors
+ - Identify average reaction time of a governor to CPU load changes
+ - (Stress) Testing whether a cpufreq low level driver or governor works
+ as expected
+ - Identify cpufreq related performance regressions between kernels
+ - Possibly Real time priority testing? -> what happens if there are
+ processes with a higher prio than the governor's kernel thread
+ - ...
+
+What this benchmark does *not* cover:
+ - Power saving related regressions (In fact as better the performance
+ throughput is, the worse the power savings will be, but the first should
+ mostly count more...)
+ - Real world (workloads)
+
+
+Description
+===========
+
+cpufreq-bench helps to test the condition of a given cpufreq governor.
+For that purpose, it compares the performance governor to a configured
+powersave module.
+
+
+How it works
+============
+You can specify load (100% CPU load) and sleep (0% CPU load) times in us which
+will be run X time in a row (cycles):
+
+ sleep=25000
+ load=25000
+ cycles=20
+
+This part of the configuration file will create 25ms load/sleep turns,
+repeated 20 times.
+
+Adding this:
+ sleep_step=25000
+ load_step=25000
+ rounds=5
+Will increase load and sleep time by 25ms 5 times.
+Together you get following test:
+25ms load/sleep time repeated 20 times (cycles).
+50ms load/sleep time repeated 20 times (cycles).
+..
+100ms load/sleep time repeated 20 times (cycles).
+
+First it is calibrated how long a specific CPU intensive calculation
+takes on this machine and needs to be run in a loop using the performance
+governor.
+Then the above test runs are processed using the performance governor
+and the governor to test. The time the calculation really needed
+with the dynamic freq scaling governor is compared with the time needed
+on full performance and you get the overall performance loss.
+
+
+Example of expected results with ondemand governor:
+
+This shows expected results of the first two test run rounds from
+above config, you there have:
+
+100% CPU load (load) | 0 % CPU load (sleep) | round
+ 25 ms | 25 ms | 1
+ 50 ms | 50 ms | 2
+
+For example if ondemand governor is configured to have a 50ms
+sampling rate you get:
+
+In round 1, ondemand should have rather static 50% load and probably
+won't ever switch up (as long as up_threshold is above).
+
+In round 2, if the ondemand sampling times exactly match the load/sleep
+trigger of the cpufreq-bench, you will see no performance loss (compare with
+below possible ondemand sample kick ins (1)):
+
+But if ondemand always kicks in in the middle of the load sleep cycles, it
+will always see 50% loads and you get worst performance impact never
+switching up (compare with below possible ondemand sample kick ins (2))::
+
+ 50 50 50 50ms ->time
+load -----| |-----| |-----| |-----|
+ | | | | | | |
+sleep |-----| |-----| |-----| |----
+ |-----|-----|-----|-----|-----|-----|-----|---- ondemand sampling (1)
+ 100 0 100 0 100 0 100 load seen by ondemand(%)
+ |-----|-----|-----|-----|-----|-----|-----|-- ondemand sampling (2)
+ 50 50 50 50 50 50 50 load seen by ondemand(%)
+
+You can easily test all kind of load/sleep times and check whether your
+governor in average behaves as expected.
+
+
+ToDo
+====
+
+Provide a gnuplot utility script for easy generation of plots to present
+the outcome nicely.
+
+
+cpufreq-bench Command Usage
+===========================
+-l, --load=<long int> initial load time in us
+-s, --sleep=<long int> initial sleep time in us
+-x, --load-step=<long int> time to be added to load time, in us
+-y, --sleep-step=<long int> time to be added to sleep time, in us
+-c, --cpu=<unsigned int> CPU Number to use, starting at 0
+-p, --prio=<priority> scheduler priority, HIGH, LOW or DEFAULT
+-g, --governor=<governor> cpufreq governor to test
+-n, --cycles=<int> load/sleep cycles to get an average value to compare
+-r, --rounds<int> load/sleep rounds
+-f, --file=<configfile> config file to use
+-o, --output=<dir> output dir, must exist
+-v, --verbose verbose output on/off
+
+Due to the high priority, the application may not be responsible for some time.
+After the benchmark, the logfile is saved in OUTPUTDIR/benchmark_TIMESTAMP.log
+
diff --git a/tools/power/cpupower/bench/benchmark.c b/tools/power/cpupower/bench/benchmark.c
new file mode 100644
index 0000000000..c7234cf3f6
--- /dev/null
+++ b/tools/power/cpupower/bench/benchmark.c
@@ -0,0 +1,181 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* cpufreq-bench CPUFreq microbenchmark
+ *
+ * Copyright (C) 2008 Christian Kornacker <ckornacker@suse.de>
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <math.h>
+
+#include "config.h"
+#include "system.h"
+#include "benchmark.h"
+
+/* Print out progress if we log into a file */
+#define show_progress(total_time, progress_time) \
+if (config->output != stdout) { \
+ fprintf(stdout, "Progress: %02lu %%\r", \
+ (progress_time * 100) / total_time); \
+ fflush(stdout); \
+}
+
+/**
+ * compute how many rounds of calculation we should do
+ * to get the given load time
+ *
+ * @param load aimed load time in µs
+ *
+ * @retval rounds of calculation
+ **/
+
+unsigned int calculate_timespace(long load, struct config *config)
+{
+ int i;
+ long long now, then;
+ unsigned int estimated = GAUGECOUNT;
+ unsigned int rounds = 0;
+ unsigned int timed = 0;
+
+ if (config->verbose)
+ printf("calibrating load of %lius, please wait...\n", load);
+
+ /* get the initial calculation time for a specific number of rounds */
+ now = get_time();
+ ROUNDS(estimated);
+ then = get_time();
+
+ timed = (unsigned int)(then - now);
+
+ /* approximation of the wanted load time by comparing with the
+ * initial calculation time */
+ for (i = 0; i < 4; i++) {
+ rounds = (unsigned int)(load * estimated / timed);
+ dprintf("calibrating with %u rounds\n", rounds);
+ now = get_time();
+ ROUNDS(rounds);
+ then = get_time();
+
+ timed = (unsigned int)(then - now);
+ estimated = rounds;
+ }
+ if (config->verbose)
+ printf("calibration done\n");
+
+ return estimated;
+}
+
+/**
+ * benchmark
+ * generates a specific sleep an load time with the performance
+ * governor and compares the used time for same calculations done
+ * with the configured powersave governor
+ *
+ * @param config config values for the benchmark
+ *
+ **/
+
+void start_benchmark(struct config *config)
+{
+ unsigned int _round, cycle;
+ long long now, then;
+ long sleep_time = 0, load_time = 0;
+ long performance_time = 0, powersave_time = 0;
+ unsigned int calculations;
+ unsigned long total_time = 0, progress_time = 0;
+
+ sleep_time = config->sleep;
+ load_time = config->load;
+
+ /* For the progress bar */
+ for (_round = 1; _round <= config->rounds; _round++)
+ total_time += _round * (config->sleep + config->load);
+ total_time *= 2; /* powersave and performance cycles */
+
+ for (_round = 0; _round < config->rounds; _round++) {
+ performance_time = 0LL;
+ powersave_time = 0LL;
+
+ show_progress(total_time, progress_time);
+
+ /* set the cpufreq governor to "performance" which disables
+ * P-State switching. */
+ if (set_cpufreq_governor("performance", config->cpu) != 0)
+ return;
+
+ /* calibrate the calculation time. the resulting calculation
+ * _rounds should produce a load which matches the configured
+ * load time */
+ calculations = calculate_timespace(load_time, config);
+
+ if (config->verbose)
+ printf("_round %i: doing %u cycles with %u calculations"
+ " for %lius\n", _round + 1, config->cycles,
+ calculations, load_time);
+
+ fprintf(config->output, "%u %li %li ",
+ _round, load_time, sleep_time);
+
+ if (config->verbose)
+ printf("average: %lius, rps:%li\n",
+ load_time / calculations,
+ 1000000 * calculations / load_time);
+
+ /* do some sleep/load cycles with the performance governor */
+ for (cycle = 0; cycle < config->cycles; cycle++) {
+ now = get_time();
+ usleep(sleep_time);
+ ROUNDS(calculations);
+ then = get_time();
+ performance_time += then - now - sleep_time;
+ if (config->verbose)
+ printf("performance cycle took %lius, "
+ "sleep: %lius, "
+ "load: %lius, rounds: %u\n",
+ (long)(then - now), sleep_time,
+ load_time, calculations);
+ }
+ fprintf(config->output, "%li ",
+ performance_time / config->cycles);
+
+ progress_time += sleep_time + load_time;
+ show_progress(total_time, progress_time);
+
+ /* set the powersave governor which activates P-State switching
+ * again */
+ if (set_cpufreq_governor(config->governor, config->cpu) != 0)
+ return;
+
+ /* again, do some sleep/load cycles with the
+ * powersave governor */
+ for (cycle = 0; cycle < config->cycles; cycle++) {
+ now = get_time();
+ usleep(sleep_time);
+ ROUNDS(calculations);
+ then = get_time();
+ powersave_time += then - now - sleep_time;
+ if (config->verbose)
+ printf("powersave cycle took %lius, "
+ "sleep: %lius, "
+ "load: %lius, rounds: %u\n",
+ (long)(then - now), sleep_time,
+ load_time, calculations);
+ }
+
+ progress_time += sleep_time + load_time;
+
+ /* compare the average sleep/load cycles */
+ fprintf(config->output, "%li ",
+ powersave_time / config->cycles);
+ fprintf(config->output, "%.3f\n",
+ performance_time * 100.0 / powersave_time);
+ fflush(config->output);
+
+ if (config->verbose)
+ printf("performance is at %.2f%%\n",
+ performance_time * 100.0 / powersave_time);
+
+ sleep_time += config->sleep_step;
+ load_time += config->load_step;
+ }
+}
diff --git a/tools/power/cpupower/bench/benchmark.h b/tools/power/cpupower/bench/benchmark.h
new file mode 100644
index 0000000000..bf612de897
--- /dev/null
+++ b/tools/power/cpupower/bench/benchmark.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* cpufreq-bench CPUFreq microbenchmark
+ *
+ * Copyright (C) 2008 Christian Kornacker <ckornacker@suse.de>
+ */
+
+/* load loop, this schould take about 1 to 2ms to complete */
+#define ROUNDS(x) {unsigned int rcnt; \
+ for (rcnt = 0; rcnt < x*1000; rcnt++) { \
+ (void)(((int)(pow(rcnt, rcnt) * \
+ sqrt(rcnt*7230970)) ^ 7230716) ^ \
+ (int)atan2(rcnt, rcnt)); \
+ } } \
+
+
+void start_benchmark(struct config *config);
diff --git a/tools/power/cpupower/bench/config.h b/tools/power/cpupower/bench/config.h
new file mode 100644
index 0000000000..fec5b0b055
--- /dev/null
+++ b/tools/power/cpupower/bench/config.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* cpufreq-bench CPUFreq microbenchmark
+ *
+ * Copyright (C) 2008 Christian Kornacker <ckornacker@suse.de>
+ */
+
+/* initial loop count for the load calibration */
+#define GAUGECOUNT 1500
+
+/* default scheduling policy SCHED_OTHER */
+#define SCHEDULER SCHED_OTHER
+
+#define PRIORITY_DEFAULT 0
+#define PRIORITY_HIGH sched_get_priority_max(SCHEDULER)
+#define PRIORITY_LOW sched_get_priority_min(SCHEDULER)
+
+/* enable further debug messages */
+#ifdef DEBUG
+#define dprintf printf
+#else
+#define dprintf(...) do { } while (0)
+#endif
+
diff --git a/tools/power/cpupower/bench/cpufreq-bench_plot.sh b/tools/power/cpupower/bench/cpufreq-bench_plot.sh
new file mode 100644
index 0000000000..f5f8b3c8f0
--- /dev/null
+++ b/tools/power/cpupower/bench/cpufreq-bench_plot.sh
@@ -0,0 +1,91 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+
+# Author/Copyright(c): 2009, Thomas Renninger <trenn@suse.de>, Novell Inc.
+
+# Helper script to easily create nice plots of your cpufreq-bench results
+
+dir=`mktemp -d`
+output_file="cpufreq-bench.png"
+global_title="cpufreq-bench plot"
+picture_type="jpeg"
+file[0]=""
+
+function usage()
+{
+ echo "cpufreq-bench_plot.sh [OPTIONS] logfile [measure_title] [logfile [measure_title]] ...]"
+ echo
+ echo "Options"
+ echo " -o output_file"
+ echo " -t global_title"
+ echo " -p picture_type [jpeg|gif|png|postscript|...]"
+ exit 1
+}
+
+if [ $# -eq 0 ];then
+ echo "No benchmark results file provided"
+ echo
+ usage
+fi
+
+while getopts o:t:p: name ; do
+ case $name in
+ o)
+ output_file="$OPTARG".$picture_type
+ ;;
+ t)
+ global_title="$OPTARG"
+ ;;
+ p)
+ picture_type="$OPTARG"
+ ;;
+ ?)
+ usage
+ ;;
+ esac
+done
+shift $(($OPTIND -1))
+
+plots=0
+while [ "$1" ];do
+ if [ ! -f "$1" ];then
+ echo "File $1 does not exist"
+ usage
+ fi
+ file[$plots]="$1"
+ title[$plots]="$2"
+ # echo "File: ${file[$plots]} - ${title[plots]}"
+ shift;shift
+ plots=$((plots + 1))
+done
+
+echo "set terminal $picture_type" >> $dir/plot_script.gpl
+echo "set output \"$output_file\"" >> $dir/plot_script.gpl
+echo "set title \"$global_title\"" >> $dir/plot_script.gpl
+echo "set xlabel \"sleep/load time\"" >> $dir/plot_script.gpl
+echo "set ylabel \"Performance (%)\"" >> $dir/plot_script.gpl
+
+for((plot=0;plot<$plots;plot++));do
+
+ # Sanity check
+ ###### I am to dump to get this redirected to stderr/stdout in one awk call... #####
+ cat ${file[$plot]} |grep -v "^#" |awk '{if ($2 != $3) printf("Error in measure %d:Load time %s does not equal sleep time %s, plot will not be correct\n", $1, $2, $3); ERR=1}'
+ ###### I am to dump to get this redirected in one awk call... #####
+
+ # Parse out load time (which must be equal to sleep time for a plot), divide it by 1000
+ # to get ms and parse out the performance in percentage and write it to a temp file for plotting
+ cat ${file[$plot]} |grep -v "^#" |awk '{printf "%lu %.1f\n",$2/1000, $6}' >$dir/data_$plot
+
+ if [ $plot -eq 0 ];then
+ echo -n "plot " >> $dir/plot_script.gpl
+ fi
+ echo -n "\"$dir/data_$plot\" title \"${title[$plot]}\" with lines" >> $dir/plot_script.gpl
+ if [ $(($plot + 1)) -ne $plots ];then
+ echo -n ", " >> $dir/plot_script.gpl
+ fi
+done
+echo >> $dir/plot_script.gpl
+
+gnuplot $dir/plot_script.gpl
+rm -r $dir
diff --git a/tools/power/cpupower/bench/cpufreq-bench_script.sh b/tools/power/cpupower/bench/cpufreq-bench_script.sh
new file mode 100644
index 0000000000..785a3679c7
--- /dev/null
+++ b/tools/power/cpupower/bench/cpufreq-bench_script.sh
@@ -0,0 +1,88 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+
+# Author/Copyright(c): 2009, Thomas Renninger <trenn@suse.de>, Novell Inc.
+
+# Ondemand up_threshold and sampling rate test script for cpufreq-bench
+# mircobenchmark.
+# Modify the general variables at the top or extend or copy out parts
+# if you want to test other things
+#
+
+# Default with latest kernels is 95, before micro account patches
+# it was 80, cmp. with git commit 808009131046b62ac434dbc796
+UP_THRESHOLD="60 80 95"
+# Depending on the kernel and the HW sampling rate could be restricted
+# and cannot be set that low...
+# E.g. before git commit cef9615a853ebc4972084f7 one could only set
+# min sampling rate of 80000 if CONFIG_HZ=250
+SAMPLING_RATE="20000 80000"
+
+function measure()
+{
+ local -i up_threshold_set
+ local -i sampling_rate_set
+
+ for up_threshold in $UP_THRESHOLD;do
+ for sampling_rate in $SAMPLING_RATE;do
+ # Set values in sysfs
+ echo $up_threshold >/sys/devices/system/cpu/cpu0/cpufreq/ondemand/up_threshold
+ echo $sampling_rate >/sys/devices/system/cpu/cpu0/cpufreq/ondemand/sampling_rate
+ up_threshold_set=$(cat /sys/devices/system/cpu/cpu0/cpufreq/ondemand/up_threshold)
+ sampling_rate_set=$(cat /sys/devices/system/cpu/cpu0/cpufreq/ondemand/sampling_rate)
+
+ # Verify set values in sysfs
+ if [ ${up_threshold_set} -eq ${up_threshold} ];then
+ echo "up_threshold: $up_threshold, set in sysfs: ${up_threshold_set}"
+ else
+ echo "WARNING: Tried to set up_threshold: $up_threshold, set in sysfs: ${up_threshold_set}"
+ fi
+ if [ ${sampling_rate_set} -eq ${sampling_rate} ];then
+ echo "sampling_rate: $sampling_rate, set in sysfs: ${sampling_rate_set}"
+ else
+ echo "WARNING: Tried to set sampling_rate: $sampling_rate, set in sysfs: ${sampling_rate_set}"
+ fi
+
+ # Benchmark
+ cpufreq-bench -o /var/log/cpufreq-bench/up_threshold_${up_threshold}_sampling_rate_${sampling_rate}
+ done
+ done
+}
+
+function create_plots()
+{
+ local command
+
+ for up_threshold in $UP_THRESHOLD;do
+ command="cpufreq-bench_plot.sh -o \"sampling_rate_${SAMPLING_RATE}_up_threshold_${up_threshold}\" -t \"Ondemand sampling_rate: ${SAMPLING_RATE} comparison - Up_threshold: $up_threshold %\""
+ for sampling_rate in $SAMPLING_RATE;do
+ command="${command} /var/log/cpufreq-bench/up_threshold_${up_threshold}_sampling_rate_${sampling_rate}/* \"sampling_rate = $sampling_rate\""
+ done
+ echo $command
+ eval "$command"
+ echo
+ done
+
+ for sampling_rate in $SAMPLING_RATE;do
+ command="cpufreq-bench_plot.sh -o \"up_threshold_${UP_THRESHOLD}_sampling_rate_${sampling_rate}\" -t \"Ondemand up_threshold: ${UP_THRESHOLD} % comparison - sampling_rate: $sampling_rate\""
+ for up_threshold in $UP_THRESHOLD;do
+ command="${command} /var/log/cpufreq-bench/up_threshold_${up_threshold}_sampling_rate_${sampling_rate}/* \"up_threshold = $up_threshold\""
+ done
+ echo $command
+ eval "$command"
+ echo
+ done
+
+ command="cpufreq-bench_plot.sh -o \"up_threshold_${UP_THRESHOLD}_sampling_rate_${SAMPLING_RATE}\" -t \"Ondemand up_threshold: ${UP_THRESHOLD} and sampling_rate ${SAMPLING_RATE} comparison\""
+ for sampling_rate in $SAMPLING_RATE;do
+ for up_threshold in $UP_THRESHOLD;do
+ command="${command} /var/log/cpufreq-bench/up_threshold_${up_threshold}_sampling_rate_${sampling_rate}/* \"up_threshold = $up_threshold - sampling_rate = $sampling_rate\""
+ done
+ done
+ echo "$command"
+ eval "$command"
+}
+
+measure
+create_plots
diff --git a/tools/power/cpupower/bench/example.cfg b/tools/power/cpupower/bench/example.cfg
new file mode 100644
index 0000000000..f91f643606
--- /dev/null
+++ b/tools/power/cpupower/bench/example.cfg
@@ -0,0 +1,11 @@
+sleep = 50000
+load = 50000
+cpu = 0
+priority = LOW
+output = /var/log/cpufreq-bench
+sleep_step = 50000
+load_step = 50000
+cycles = 20
+rounds = 40
+verbose = 0
+governor = ondemand
diff --git a/tools/power/cpupower/bench/main.c b/tools/power/cpupower/bench/main.c
new file mode 100644
index 0000000000..429d1b6b8b
--- /dev/null
+++ b/tools/power/cpupower/bench/main.c
@@ -0,0 +1,189 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* cpufreq-bench CPUFreq microbenchmark
+ *
+ * Copyright (C) 2008 Christian Kornacker <ckornacker@suse.de>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <errno.h>
+
+#include "config.h"
+#include "system.h"
+#include "benchmark.h"
+
+static struct option long_options[] = {
+ {"output", 1, 0, 'o'},
+ {"sleep", 1, 0, 's'},
+ {"load", 1, 0, 'l'},
+ {"verbose", 0, 0, 'v'},
+ {"cpu", 1, 0, 'c'},
+ {"governor", 1, 0, 'g'},
+ {"prio", 1, 0, 'p'},
+ {"file", 1, 0, 'f'},
+ {"cycles", 1, 0, 'n'},
+ {"rounds", 1, 0, 'r'},
+ {"load-step", 1, 0, 'x'},
+ {"sleep-step", 1, 0, 'y'},
+ {"help", 0, 0, 'h'},
+ {0, 0, 0, 0}
+};
+
+/*******************************************************************
+ usage
+*******************************************************************/
+
+void usage()
+{
+ printf("usage: ./bench\n");
+ printf("Options:\n");
+ printf(" -l, --load=<long int>\t\tinitial load time in us\n");
+ printf(" -s, --sleep=<long int>\t\tinitial sleep time in us\n");
+ printf(" -x, --load-step=<long int>\ttime to be added to load time, in us\n");
+ printf(" -y, --sleep-step=<long int>\ttime to be added to sleep time, in us\n");
+ printf(" -c, --cpu=<cpu #>\t\t\tCPU Nr. to use, starting at 0\n");
+ printf(" -p, --prio=<priority>\t\t\tscheduler priority, HIGH, LOW or DEFAULT\n");
+ printf(" -g, --governor=<governor>\t\tcpufreq governor to test\n");
+ printf(" -n, --cycles=<int>\t\t\tload/sleep cycles\n");
+ printf(" -r, --rounds<int>\t\t\tload/sleep rounds\n");
+ printf(" -f, --file=<configfile>\t\tconfig file to use\n");
+ printf(" -o, --output=<dir>\t\t\toutput path. Filename will be OUTPUTPATH/benchmark_TIMESTAMP.log\n");
+ printf(" -v, --verbose\t\t\t\tverbose output on/off\n");
+ printf(" -h, --help\t\t\t\tPrint this help screen\n");
+ exit(1);
+}
+
+/*******************************************************************
+ main
+*******************************************************************/
+
+int main(int argc, char **argv)
+{
+ int c;
+ int option_index = 0;
+ struct config *config = NULL;
+
+ config = prepare_default_config();
+
+ if (config == NULL)
+ return EXIT_FAILURE;
+
+ while (1) {
+ c = getopt_long (argc, argv, "hg:o:s:l:vc:p:f:n:r:x:y:",
+ long_options, &option_index);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'o':
+ if (config->output != NULL)
+ fclose(config->output);
+
+ config->output = prepare_output(optarg);
+
+ if (config->output == NULL)
+ return EXIT_FAILURE;
+
+ dprintf("user output path -> %s\n", optarg);
+ break;
+ case 's':
+ sscanf(optarg, "%li", &config->sleep);
+ dprintf("user sleep time -> %s\n", optarg);
+ break;
+ case 'l':
+ sscanf(optarg, "%li", &config->load);
+ dprintf("user load time -> %s\n", optarg);
+ break;
+ case 'c':
+ sscanf(optarg, "%u", &config->cpu);
+ dprintf("user cpu -> %s\n", optarg);
+ break;
+ case 'g':
+ strncpy(config->governor, optarg, 14);
+ dprintf("user governor -> %s\n", optarg);
+ break;
+ case 'p':
+ if (string_to_prio(optarg) != SCHED_ERR) {
+ config->prio = string_to_prio(optarg);
+ dprintf("user prio -> %s\n", optarg);
+ } else {
+ if (config != NULL) {
+ if (config->output != NULL)
+ fclose(config->output);
+ free(config);
+ }
+ usage();
+ }
+ break;
+ case 'n':
+ sscanf(optarg, "%u", &config->cycles);
+ dprintf("user cycles -> %s\n", optarg);
+ break;
+ case 'r':
+ sscanf(optarg, "%u", &config->rounds);
+ dprintf("user rounds -> %s\n", optarg);
+ break;
+ case 'x':
+ sscanf(optarg, "%li", &config->load_step);
+ dprintf("user load_step -> %s\n", optarg);
+ break;
+ case 'y':
+ sscanf(optarg, "%li", &config->sleep_step);
+ dprintf("user sleep_step -> %s\n", optarg);
+ break;
+ case 'f':
+ if (prepare_config(optarg, config))
+ return EXIT_FAILURE;
+ break;
+ case 'v':
+ config->verbose = 1;
+ dprintf("verbose output enabled\n");
+ break;
+ case 'h':
+ case '?':
+ default:
+ if (config != NULL) {
+ if (config->output != NULL)
+ fclose(config->output);
+ free(config);
+ }
+ usage();
+ }
+ }
+
+ if (config->verbose) {
+ printf("starting benchmark with parameters:\n");
+ printf("config:\n\t"
+ "sleep=%li\n\t"
+ "load=%li\n\t"
+ "sleep_step=%li\n\t"
+ "load_step=%li\n\t"
+ "cpu=%u\n\t"
+ "cycles=%u\n\t"
+ "rounds=%u\n\t"
+ "governor=%s\n\n",
+ config->sleep,
+ config->load,
+ config->sleep_step,
+ config->load_step,
+ config->cpu,
+ config->cycles,
+ config->rounds,
+ config->governor);
+ }
+
+ prepare_user(config);
+ prepare_system(config);
+ start_benchmark(config);
+
+ if (config->output != stdout)
+ fclose(config->output);
+
+ free(config);
+
+ return EXIT_SUCCESS;
+}
+
diff --git a/tools/power/cpupower/bench/parse.c b/tools/power/cpupower/bench/parse.c
new file mode 100644
index 0000000000..e63dc11fa3
--- /dev/null
+++ b/tools/power/cpupower/bench/parse.c
@@ -0,0 +1,225 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* cpufreq-bench CPUFreq microbenchmark
+ *
+ * Copyright (C) 2008 Christian Kornacker <ckornacker@suse.de>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <time.h>
+#include <dirent.h>
+
+#include <sys/utsname.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "parse.h"
+#include "config.h"
+
+/**
+ * converts priority string to priority
+ *
+ * @param str string that represents a scheduler priority
+ *
+ * @retval priority
+ * @retval SCHED_ERR when the priority doesn't exit
+ **/
+
+enum sched_prio string_to_prio(const char *str)
+{
+ if (strncasecmp("high", str, strlen(str)) == 0)
+ return SCHED_HIGH;
+ else if (strncasecmp("default", str, strlen(str)) == 0)
+ return SCHED_DEFAULT;
+ else if (strncasecmp("low", str, strlen(str)) == 0)
+ return SCHED_LOW;
+ else
+ return SCHED_ERR;
+}
+
+/**
+ * create and open logfile
+ *
+ * @param dir directory in which the logfile should be created
+ *
+ * @retval logfile on success
+ * @retval NULL when the file can't be created
+ **/
+
+FILE *prepare_output(const char *dirname)
+{
+ FILE *output = NULL;
+ int len;
+ char *filename, *filename_tmp;
+ struct utsname sysdata;
+ DIR *dir;
+
+ dir = opendir(dirname);
+ if (dir == NULL) {
+ if (mkdir(dirname, 0755)) {
+ perror("mkdir");
+ fprintf(stderr, "error: Cannot create dir %s\n",
+ dirname);
+ return NULL;
+ }
+ }
+
+ len = strlen(dirname) + 30;
+ filename = malloc(sizeof(char) * len);
+ if (!filename) {
+ perror("malloc");
+ goto out_dir;
+ }
+
+ if (uname(&sysdata) == 0) {
+ len += strlen(sysdata.nodename) + strlen(sysdata.release);
+ filename_tmp = realloc(filename, sizeof(*filename) * len);
+
+ if (filename_tmp == NULL) {
+ free(filename);
+ perror("realloc");
+ goto out_dir;
+ }
+
+ filename = filename_tmp;
+ snprintf(filename, len - 1, "%s/benchmark_%s_%s_%li.log",
+ dirname, sysdata.nodename, sysdata.release, time(NULL));
+ } else {
+ snprintf(filename, len - 1, "%s/benchmark_%li.log",
+ dirname, time(NULL));
+ }
+
+ dprintf("logfilename: %s\n", filename);
+
+ output = fopen(filename, "w+");
+ if (output == NULL) {
+ perror("fopen");
+ fprintf(stderr, "error: unable to open logfile\n");
+ goto out;
+ }
+
+ fprintf(stdout, "Logfile: %s\n", filename);
+
+ fprintf(output, "#round load sleep performance powersave percentage\n");
+out:
+ free(filename);
+out_dir:
+ closedir(dir);
+ return output;
+}
+
+/**
+ * returns the default config
+ *
+ * @retval default config on success
+ * @retval NULL when the output file can't be created
+ **/
+
+struct config *prepare_default_config()
+{
+ struct config *config = malloc(sizeof(struct config));
+
+ dprintf("loading defaults\n");
+
+ config->sleep = 500000;
+ config->load = 500000;
+ config->sleep_step = 500000;
+ config->load_step = 500000;
+ config->cycles = 5;
+ config->rounds = 50;
+ config->cpu = 0;
+ config->prio = SCHED_HIGH;
+ config->verbose = 0;
+ strncpy(config->governor, "ondemand", sizeof(config->governor));
+
+ config->output = stdout;
+
+#ifdef DEFAULT_CONFIG_FILE
+ if (prepare_config(DEFAULT_CONFIG_FILE, config))
+ return NULL;
+#endif
+ return config;
+}
+
+/**
+ * parses config file and returns the config to the caller
+ *
+ * @param path config file name
+ *
+ * @retval 1 on error
+ * @retval 0 on success
+ **/
+
+int prepare_config(const char *path, struct config *config)
+{
+ size_t len = 0;
+ char opt[16], val[32], *line = NULL;
+ FILE *configfile;
+
+ if (config == NULL) {
+ fprintf(stderr, "error: config is NULL\n");
+ return 1;
+ }
+
+ configfile = fopen(path, "r");
+ if (configfile == NULL) {
+ perror("fopen");
+ fprintf(stderr, "error: unable to read configfile\n");
+ free(config);
+ return 1;
+ }
+
+ while (getline(&line, &len, configfile) != -1) {
+ if (line[0] == '#' || line[0] == ' ' || line[0] == '\n')
+ continue;
+
+ if (sscanf(line, "%14s = %30s", opt, val) < 2)
+ continue;
+
+ dprintf("parsing: %s -> %s\n", opt, val);
+
+ if (strcmp("sleep", opt) == 0)
+ sscanf(val, "%li", &config->sleep);
+
+ else if (strcmp("load", opt) == 0)
+ sscanf(val, "%li", &config->load);
+
+ else if (strcmp("load_step", opt) == 0)
+ sscanf(val, "%li", &config->load_step);
+
+ else if (strcmp("sleep_step", opt) == 0)
+ sscanf(val, "%li", &config->sleep_step);
+
+ else if (strcmp("cycles", opt) == 0)
+ sscanf(val, "%u", &config->cycles);
+
+ else if (strcmp("rounds", opt) == 0)
+ sscanf(val, "%u", &config->rounds);
+
+ else if (strcmp("verbose", opt) == 0)
+ sscanf(val, "%u", &config->verbose);
+
+ else if (strcmp("output", opt) == 0)
+ config->output = prepare_output(val);
+
+ else if (strcmp("cpu", opt) == 0)
+ sscanf(val, "%u", &config->cpu);
+
+ else if (strcmp("governor", opt) == 0) {
+ strncpy(config->governor, val,
+ sizeof(config->governor));
+ config->governor[sizeof(config->governor) - 1] = '\0';
+ }
+
+ else if (strcmp("priority", opt) == 0) {
+ if (string_to_prio(val) != SCHED_ERR)
+ config->prio = string_to_prio(val);
+ }
+ }
+
+ free(line);
+
+ return 0;
+}
diff --git a/tools/power/cpupower/bench/parse.h b/tools/power/cpupower/bench/parse.h
new file mode 100644
index 0000000000..d5b3e34d70
--- /dev/null
+++ b/tools/power/cpupower/bench/parse.h
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* cpufreq-bench CPUFreq microbenchmark
+ *
+ * Copyright (C) 2008 Christian Kornacker <ckornacker@suse.de>
+ */
+
+/* struct that holds the required config parameters */
+struct config
+{
+ long sleep; /* sleep time in µs */
+ long load; /* load time in µs */
+ long sleep_step; /* time value which changes the
+ * sleep time after every round in µs */
+ long load_step; /* time value which changes the
+ * load time after every round in µs */
+ unsigned int cycles; /* calculation cycles with the same sleep/load time */
+ unsigned int rounds; /* calculation rounds with iterated sleep/load time */
+ unsigned int cpu; /* cpu for which the affinity is set */
+ char governor[15]; /* cpufreq governor */
+ enum sched_prio /* possible scheduler priorities */
+ {
+ SCHED_ERR = -1,
+ SCHED_HIGH,
+ SCHED_DEFAULT,
+ SCHED_LOW
+ } prio;
+
+ unsigned int verbose; /* verbose output */
+ FILE *output; /* logfile */
+ char *output_filename; /* logfile name, must be freed at the end
+ if output != NULL and output != stdout*/
+};
+
+enum sched_prio string_to_prio(const char *str);
+
+FILE *prepare_output(const char *dir);
+
+int prepare_config(const char *path, struct config *config);
+struct config *prepare_default_config();
+
diff --git a/tools/power/cpupower/bench/system.c b/tools/power/cpupower/bench/system.c
new file mode 100644
index 0000000000..40f3679e70
--- /dev/null
+++ b/tools/power/cpupower/bench/system.c
@@ -0,0 +1,179 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* cpufreq-bench CPUFreq microbenchmark
+ *
+ * Copyright (C) 2008 Christian Kornacker <ckornacker@suse.de>
+ */
+
+#include <stdio.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <sched.h>
+
+#include <cpufreq.h>
+#include <cpupower.h>
+
+#include "config.h"
+#include "system.h"
+
+/**
+ * returns time since epoch in µs
+ *
+ * @retval time
+ **/
+
+long long int get_time()
+{
+ struct timeval now;
+
+ gettimeofday(&now, NULL);
+
+ return (long long int)(now.tv_sec * 1000000LL + now.tv_usec);
+}
+
+/**
+ * sets the cpufreq governor
+ *
+ * @param governor cpufreq governor name
+ * @param cpu cpu for which the governor should be set
+ *
+ * @retval 0 on success
+ * @retval -1 when failed
+ **/
+
+int set_cpufreq_governor(char *governor, unsigned int cpu)
+{
+
+ dprintf("set %s as cpufreq governor\n", governor);
+
+ if (cpupower_is_cpu_online(cpu) != 1) {
+ perror("cpufreq_cpu_exists");
+ fprintf(stderr, "error: cpu %u does not exist\n", cpu);
+ return -1;
+ }
+
+ if (cpufreq_modify_policy_governor(cpu, governor) != 0) {
+ perror("cpufreq_modify_policy_governor");
+ fprintf(stderr, "error: unable to set %s governor\n", governor);
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * sets cpu affinity for the process
+ *
+ * @param cpu cpu# to which the affinity should be set
+ *
+ * @retval 0 on success
+ * @retval -1 when setting the affinity failed
+ **/
+
+int set_cpu_affinity(unsigned int cpu)
+{
+ cpu_set_t cpuset;
+
+ CPU_ZERO(&cpuset);
+ CPU_SET(cpu, &cpuset);
+
+ dprintf("set affinity to cpu #%u\n", cpu);
+
+ if (sched_setaffinity(getpid(), sizeof(cpu_set_t), &cpuset) < 0) {
+ perror("sched_setaffinity");
+ fprintf(stderr, "warning: unable to set cpu affinity\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * sets the process priority parameter
+ *
+ * @param priority priority value
+ *
+ * @retval 0 on success
+ * @retval -1 when setting the priority failed
+ **/
+
+int set_process_priority(int priority)
+{
+ struct sched_param param;
+
+ dprintf("set scheduler priority to %i\n", priority);
+
+ param.sched_priority = priority;
+
+ if (sched_setscheduler(0, SCHEDULER, &param) < 0) {
+ perror("sched_setscheduler");
+ fprintf(stderr, "warning: unable to set scheduler priority\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * notifies the user that the benchmark may run some time
+ *
+ * @param config benchmark config values
+ *
+ **/
+
+void prepare_user(const struct config *config)
+{
+ unsigned long sleep_time = 0;
+ unsigned long load_time = 0;
+ unsigned int round;
+
+ for (round = 0; round < config->rounds; round++) {
+ sleep_time += 2 * config->cycles *
+ (config->sleep + config->sleep_step * round);
+ load_time += 2 * config->cycles *
+ (config->load + config->load_step * round) +
+ (config->load + config->load_step * round * 4);
+ }
+
+ if (config->verbose || config->output != stdout)
+ printf("approx. test duration: %im\n",
+ (int)((sleep_time + load_time) / 60000000));
+}
+
+/**
+ * sets up the cpu affinity and scheduler priority
+ *
+ * @param config benchmark config values
+ *
+ **/
+
+void prepare_system(const struct config *config)
+{
+ if (config->verbose)
+ printf("set cpu affinity to cpu #%u\n", config->cpu);
+
+ set_cpu_affinity(config->cpu);
+
+ switch (config->prio) {
+ case SCHED_HIGH:
+ if (config->verbose)
+ printf("high priority condition requested\n");
+
+ set_process_priority(PRIORITY_HIGH);
+ break;
+ case SCHED_LOW:
+ if (config->verbose)
+ printf("low priority condition requested\n");
+
+ set_process_priority(PRIORITY_LOW);
+ break;
+ default:
+ if (config->verbose)
+ printf("default priority condition requested\n");
+
+ set_process_priority(PRIORITY_DEFAULT);
+ }
+}
+
diff --git a/tools/power/cpupower/bench/system.h b/tools/power/cpupower/bench/system.h
new file mode 100644
index 0000000000..530fa28230
--- /dev/null
+++ b/tools/power/cpupower/bench/system.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* cpufreq-bench CPUFreq microbenchmark
+ *
+ * Copyright (C) 2008 Christian Kornacker <ckornacker@suse.de>
+ */
+
+#include "parse.h"
+
+long long get_time();
+
+int set_cpufreq_governor(char *governor, unsigned int cpu);
+int set_cpu_affinity(unsigned int cpu);
+int set_process_priority(int priority);
+
+void prepare_user(const struct config *config);
+void prepare_system(const struct config *config);