As pointed by Lorenzo, when a cpu powers down, the L1 cache must be flushed before, otherwise:
* data cachelines are not empty and the other cpu may fetch data * cpu will lost some data leading to a memory corruption
Note this bug is very difficult to reproduce and this test will not spot the issue everytime.
Signed-off-by: Daniel Lezcano daniel.lezcano@linaro.org --- cpuidle/cpuidle-l1.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++ cpuidle/cpuidle_05.sh | 42 ++++++++++++++++++++++++++++ cpuidle/cpuidle_05.txt | 1 + 3 files changed, 115 insertions(+) create mode 100644 cpuidle/cpuidle-l1.c create mode 100755 cpuidle/cpuidle_05.sh create mode 100644 cpuidle/cpuidle_05.txt
diff --git a/cpuidle/cpuidle-l1.c b/cpuidle/cpuidle-l1.c new file mode 100644 index 0000000..bbcde28 --- /dev/null +++ b/cpuidle/cpuidle-l1.c @@ -0,0 +1,72 @@ +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> +#include <pthread.h> +#define BUFSIZE (4*1024) +#define DEADBEEF 0xDEADBEEF +static int buffer[BUFSIZE]; + +static pthread_t threads[64]; + +void *thread_routine(void *arg) +{ + int i, display = *(int *)arg; + int dummy; + + for (i = 0; i < 100; i++) { + + int j; + + for (j = 0; j < BUFSIZE * 1000; j++) { + dummy = buffer[j % BUFSIZE]; + dummy++; + } + + usleep(200000); + + if (buffer[i] != DEADBEEF) { + fprintf(stderr, "memory corruption\n"); + return (void *)-1; + } + + if (display == 0) + printf("%d%%%s", i, i < 10 ? "\b\b" : "\b\b\b"); + } + + if (display == 0) + printf(" \b\b\b\b"); + + return NULL; +} + +int main(int argc, char *argv[]) +{ + int i, ret = 0; + int nrcpus = sysconf(_SC_NPROCESSORS_ONLN); + + for (i = 0; i < BUFSIZE; i++) + buffer[i] = DEADBEEF; + + setbuf(stdout, NULL); + + for(i = 0; i < nrcpus; i++) { + + if (pthread_create(&threads[i], NULL, thread_routine, &i)) { + perror("pthread_create"); + return 1; + } + + } + + for (i = 0; i < nrcpus; i++) { + void *result; + pthread_join(threads[i], &result); + + if (result == (void *)-1) + ret = 1; + } + + return ret; +} diff --git a/cpuidle/cpuidle_05.sh b/cpuidle/cpuidle_05.sh new file mode 100755 index 0000000..679439d --- /dev/null +++ b/cpuidle/cpuidle_05.sh @@ -0,0 +1,42 @@ +#!/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/Resources/TestSuite/Pm... + +source ../include/functions.sh + +CPUIDLE_L1=./cpuidle-l1 + +if [ $(id -u) -ne 0 ]; then + log_skip "run as non-root" + exit 0 +fi + +check_cpuidle_l1() { + check "Fill L1 cache and sleep" "./$CPUIDLE_L1" +} + +check_cpuidle_l1 +test_status_show diff --git a/cpuidle/cpuidle_05.txt b/cpuidle/cpuidle_05.txt new file mode 100644 index 0000000..1f80e36 --- /dev/null +++ b/cpuidle/cpuidle_05.txt @@ -0,0 +1 @@ +Run cpuidle L1 test program to catch L1 flush missing vs cpu power down