This patch provides a couple of tests for cpuidle in addition with a small program which behaves to sollicitate the cpuidle drivers especially when both cores needs to reach the same C-states.
The program forks as many online processors present on the system and set the affinity to it. Then it runs, during 120 secs, small chunks of busy loops and idle loops. When there is something wrong with the cpuidle driver, this program often triggers the problem which results in a kernel panic, tasks hung warning or system hang.
Signed-off-by: Daniel Lezcano daniel.lezcano@linaro.org --- .gitignore | 5 +- Test.mk | 11 ++- cpuidle/cpuidle_02.sh | 37 ++++++++++ cpuidle/cpuidle_02.txt | 1 + cpuidle/cpuidle_03.sh | 60 +++++++++++++++ cpuidle/cpuidle_03.txt | 1 + cpuidle/cpuidle_killer.c | 180 ++++++++++++++++++++++++++++++++++++++++++++++ include/functions.sh | 8 ++ utils/Makefile | 12 +--- 9 files changed, 300 insertions(+), 15 deletions(-) create mode 100755 cpuidle/cpuidle_02.sh create mode 100644 cpuidle/cpuidle_02.txt create mode 100755 cpuidle/cpuidle_03.sh create mode 100644 cpuidle/cpuidle_03.txt create mode 100644 cpuidle/cpuidle_killer.c
diff --git a/.gitignore b/.gitignore index c833157..e21ff87 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,7 @@ - +.pc +*.log utils/cpuburn utils/cpucycle utils/nanosleep +cpuidle/cpuidle_killer +patches diff --git a/Test.mk b/Test.mk index a265286..a48a18c 100644 --- a/Test.mk +++ b/Test.mk @@ -24,8 +24,12 @@
TST=$(wildcard *.sh) LOG=$(TST:.sh=.log) +CFLAGS?=-g -Wall +CC?=gcc +SRC=$(wildcard *.c) +EXEC=$(SRC:%.c=%)
-check: uncheck $(LOG) +check: uncheck $(EXEC) $(LOG)
%.log: %.sh @echo "###" @@ -36,8 +40,9 @@ check: uncheck $(LOG) @./$< 2> $@
clean: - @test ! -z "$(LOG)" && rm -f $(LOG) + rm -f *.o $(EXEC)
-uncheck: clean +uncheck: + -@test ! -z "$(LOG)" && rm -f $(LOG)
recheck: uncheck check diff --git a/cpuidle/cpuidle_02.sh b/cpuidle/cpuidle_02.sh new file mode 100755 index 0000000..1b155c1 --- /dev/null +++ b/cpuidle/cpuidle_02.sh @@ -0,0 +1,37 @@ +#!/bin/bash +# +# PM-QA validation test suite for the power management on Linux +# +# Copyright (C) 2011, Linaro Limited. +# +# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# Contributors: +# Daniel Lezcano daniel.lezcano@linaro.org (IBM Corporation) +# - initial API and implementation +# + +# URL : https://wiki.linaro.org/WorkingGroups/PowerManagement/Doc/QA/Scripts#cpuidle... + +source ../include/functions.sh + +CPUIDLE_KILLER=./cpuidle_killer + +if [ $(id -u) != 0 ]; then + log_skip "run as non-root" + exit 0 +fi + +check "cpuidle program runs successfully (120 secs)" "./$CPUIDLE_KILLER" diff --git a/cpuidle/cpuidle_02.txt b/cpuidle/cpuidle_02.txt new file mode 100644 index 0000000..3f0b1ef --- /dev/null +++ b/cpuidle/cpuidle_02.txt @@ -0,0 +1 @@ +Run a cpuidle program killer diff --git a/cpuidle/cpuidle_03.sh b/cpuidle/cpuidle_03.sh new file mode 100755 index 0000000..22bca10 --- /dev/null +++ b/cpuidle/cpuidle_03.sh @@ -0,0 +1,60 @@ +#!/bin/bash +# +# PM-QA validation test suite for the power management on Linux +# +# Copyright (C) 2011, Linaro Limited. +# +# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# Contributors: +# Daniel Lezcano daniel.lezcano@linaro.org (IBM Corporation) +# - initial API and implementation +# + +# URL : https://wiki.linaro.org/WorkingGroups/PowerManagement/Doc/QA/Scripts#cpuidle... + +source ../include/functions.sh + +CPUIDLE_KILLER=./cpuidle_killer + +if [ $(id -u) != 0 ]; then + log_skip "run as non-root" + exit 0 +fi + +restore_cpus() { + for_each_cpu set_online +} + +check_cpuidle_kill() { + + if [ "$1" = "cpu0" ]; then + log_skip "skipping cpu0" + return 0 + fi + + set_offline $1 + check "cpuidle program runs successfully (120 secs)" "./$CPUIDLE_KILLER" +} + +if [ $(id -u) != 0 ]; then + log_skip "run as non-root" + exit 0 +fi + +trap "restore_cpus; sigtrap" SIGHUP SIGINT SIGTERM + +for_each_cpu check_cpuidle_kill +restore_cpus diff --git a/cpuidle/cpuidle_03.txt b/cpuidle/cpuidle_03.txt new file mode 100644 index 0000000..1bf5c02 --- /dev/null +++ b/cpuidle/cpuidle_03.txt @@ -0,0 +1 @@ +Run a cpuidle program killer and unplug one by one the cpus (except cpu0) diff --git a/cpuidle/cpuidle_killer.c b/cpuidle/cpuidle_killer.c new file mode 100644 index 0000000..67a675e --- /dev/null +++ b/cpuidle/cpuidle_killer.c @@ -0,0 +1,180 @@ +#define _GNU_SOURCE +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/timex.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/param.h> +#include <sched.h> +#include <signal.h> + +#define DURATION 120 /* seconds */ +#define SEC_TO_USEC(d) ((d) * 1000 * 1000) + +static int sigon; + +void sighandler(int sig) +{ + sigon = 1; +} + +void timeout(int sig) +{ + printf("Test duration exceeded\n"); + exit(1); +} + +int tick_me(int nrsleeps, int delay, int cpu) +{ + int i; + cpu_set_t mask; + unsigned long counter = 0; + struct itimerval t = { }; + + CPU_ZERO(&mask); + CPU_SET(cpu, &mask); + + /* stick on the specified cpu */ + if (sched_setaffinity(getpid(), sizeof(mask), &mask)) { + fprintf(stderr, "sched_setaffinity (%d): %m", cpu); + return -1; + } + + signal(SIGALRM, sighandler); + + for (i = 0; i < nrsleeps / 2; i++) { + usleep(delay); + + sigon = 0; + t.it_value.tv_sec = 0; + t.it_value.tv_usec = delay; + + if (setitimer(ITIMER_REAL, &t, NULL)) { + perror("setitimer"); + return 1; + } + + while (!sigon) + counter++; + } + + fprintf(stderr, "counter value %lu\n", counter); + + return 0; +} + +int isonline(int cpu) +{ + FILE *f; + char path[MAXPATHLEN]; + int online; + + if (!cpu) + return 1; + + sprintf(path, "/sys/devices/system/cpu/cpu%d/online", cpu); + + f = fopen(path, "r"); + if (!f) { + perror("fopen"); + return -1; + } + + fscanf(f, "%d", &online); + + fclose(f); + + return online; +} + +int main(int argc, char *argv[]) +{ + int ret, i, nrcpus = 2; + int nrsleeps, delay; + pid_t pids[nrcpus]; + struct timex timex = { 0 }; + + if (adjtimex(&timex) < 0) { + perror("adjtimex"); + return 1; + } + + fprintf(stderr, "jiffies are : %ld usecs\n", timex.tick); + + nrcpus = sysconf(_SC_NPROCESSORS_CONF); + if (nrcpus < 0) { + perror("sysconf"); + return 1; + } + + fprintf(stderr, "found %d cpu(s)\n", nrcpus); + + for (i = 0; i < nrcpus; i++) { + + ret = isonline(i); + if (!ret) { + fprintf(stderr, "cpu%d is offline, ignoring\n", i); + continue; + } + + pids[i] = fork(); + if (pids[i] < 0) { + perror("fork"); + return 1; + } + + if (!pids[i]) { + + struct timeval before, after; + long duration; + float deviation; + + nrsleeps = SEC_TO_USEC(DURATION) / (timex.tick * 10); + delay = SEC_TO_USEC(DURATION) / nrsleeps; + + fprintf(stderr, "duration: %d secs, #sleep: %d, delay: %d us\n", + DURATION, nrsleeps, delay); + + gettimeofday(&before, NULL); + if (tick_me(nrsleeps, delay, i)) + return 1; + gettimeofday(&after, NULL); + + duration = SEC_TO_USEC(after.tv_sec - before.tv_sec) + + (after.tv_usec - before.tv_usec); + + fprintf(stderr, "test duration: %f secs\n", + (float)(duration) / (float)SEC_TO_USEC(1)); + + deviation = ((float)duration - (float)SEC_TO_USEC(DURATION)) / + (float)SEC_TO_USEC(DURATION); + + fprintf(stderr, "deviation %f\n", deviation); + if (deviation > 0.5) { + fprintf(stderr, "expected test duration too long\n"); + return 1; + } + + return 0; + } + } + + ret = 0; + + signal(SIGALRM, timeout); + + alarm(DURATION + 5); + + for (i = 0; i < nrcpus; i++) { + int status; + + waitpid(pids[i], &status, 0); + if (status != 0) { + fprintf(stderr, "test for cpu %d has failed\n", i); + ret = 1; + } + } + + return ret; +} diff --git a/include/functions.sh b/include/functions.sh index 1d06249..c39b6b7 100644 --- a/include/functions.sh +++ b/include/functions.sh @@ -179,6 +179,10 @@ set_online() { local cpu=$1 local dirpath=$CPU_PATH/$cpu
+ if [ "$cpu" = "cpu0" ]; then + return 0 + fi + echo 1 > $dirpath/online }
@@ -186,6 +190,10 @@ set_offline() { local cpu=$1 local dirpath=$CPU_PATH/$cpu
+ if [ "$cpu" = "cpu0" ]; then + return 0 + fi + echo 0 > $dirpath/online }
diff --git a/utils/Makefile b/utils/Makefile index 6d2783f..df0b8f4 100644 --- a/utils/Makefile +++ b/utils/Makefile @@ -22,14 +22,4 @@ # - initial API and implementation #
-CFLAGS?=-g -Wall -CC?=gcc -SRC=$(wildcard *.c) -EXEC=$(SRC:%.c=%) - -check: $(EXEC) - -all: $(EXEC) - -clean: - rm -f *.o $(EXEC) +include ../Test.mk