[PATCH]: Add a T-state test case (resent)
Yi Yang <yi.y.yang <at> intel.com>
2008-03-30 15:22:15 GMT
This test case seems missed, it is very similar to cpufreq test case, please consider to
merge it and let me know if any problem, thanks.
Add T-state test case to linuxfirmwarekit, performance evaluation method
is from cpufreq test case but is modified in order to provide a good
precision.
Signed-off-by: Yi Yang <yi.y.yang <at> intel.com>
---
Makefile | 25 +++
tstates.c | 428 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 453 insertions(+)
--- /dev/null 2008-02-21 05:07:22.465822503 +0800
+++ linuxfirmwarekit/tstates/tstates.c 2008-02-21 06:57:20.000000000 +0800
<at> <at> -0,0 +1,428 <at> <at>
+/*
+ * Copyright (C) 2006, Intel Corporation
+ *
+ * This file is part of the Linux-ready Firmware Developer Kit
+ *
+ * This program file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation;version 2.1 of the License.
+ *
+ * This library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program in a file named COPYING; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ */
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <stdint.h>
+#include <sched.h>
+#include <time.h>
+#include <math.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/mman.h>
+
+#include <glib.h>
+
+#include <firmwarekit.h>
+
+struct tstateinfo {
+ int tperf;
+ int speed;
+ int diff;
+};
+
+#define GET_PERFORMANCE_MAX (0)
+#define GET_PERFORMANCE_MIN (1)
+#define GET_PERFORMANCE_AVG (2)
+
+static cpu_set_t mask, oldset;
+static int oldscheduler;
+static int oldpriority;
+
+static int max_tstate = 0;
+static int oldtstate = 0;
+static struct tstateinfo * tstates = NULL;
+static char governor[256];
+static char oldfreq[256];
+
+#define rdtscll(val) \
+ __asm__ __volatile__ ("rdtsc" : "=A" (val))
+#define abs(x) ((x) < 0 ? -x : x)
+
+static void bind_to_cpu(int cpunr)
+{
+
+ sched_getaffinity(0, sizeof(oldset), &oldset);
+
+ CPU_ZERO(&mask);
+ CPU_SET(cpunr, &mask);
+ sched_setaffinity(0, sizeof(mask), &mask);
+}
+
+static void unbind_to_cpu(int cpunr)
+{
+ sched_setaffinity(0, sizeof(oldset), &oldset);
+}
+
+static int godme(void)
+{
+ struct sched_param schedparam;
+ /*
+ * set the process to realtime privs
+ */
+ oldscheduler = sched_getscheduler(getpid());
+ oldpriority = getpriority(PRIO_PROCESS, getpid());
+
+ memset(&schedparam, 0, sizeof(schedparam));
+ schedparam.sched_priority = sched_get_priority_max(SCHED_FIFO);
+
+ if (sched_setscheduler(0, SCHED_FIFO, &schedparam) != 0) {
+ perror("sched_setscheduler");
+ exit(1);
+ }
+
+ if (mlockall(MCL_CURRENT|MCL_FUTURE) != 0) {
+ perror("mlockall");
+ exit(1);
+ }
+
+ return 0;
+}
+
+static int commonme(void)
+{
+ struct sched_param schedparam;
+
+ munlockall();
+ memset(&schedparam, 0, sizeof(schedparam));
+ schedparam.sched_priority = oldpriority;
+ if (sched_setscheduler(0, oldscheduler, &schedparam) != 0) {
+ perror("sched_setscheduler");
+ exit(1);
+ }
+}
+
+static unsigned long diff_time(struct timeval start, struct timeval end)
+{
+ return (end.tv_sec - start.tv_sec) * 1000000 + (end.tv_usec - start.tv_usec);
+}
+
+static unsigned long get_performance(int cpunr)
+{
+ struct timeval t1, t2;
+ unsigned long loopcount = 0;
+ unsigned long interval = 4000; //4 milliseconds
+ double A, B;
+
+ godme();
+ bind_to_cpu(cpunr);
+
+ gettimeofday(&t1, NULL);
+ do {
+ A = 1.234567;
+ B = 3.121213;
+ A = A * B;
+ B = A * A;
+ A = A - B + sqrt(A);
+ A = A * B;
+ B = A * A;
+ A = A - B + sqrt(A);
+ A = A * B;
+ B = A * A;
+ A = A - B + sqrt(A);
+ A = A * B;
+ B = A * A;
+ A = A - B + sqrt(A);
+ loopcount++;
+ gettimeofday(&t2, NULL);
+ } while (diff_time(t1, t2) < interval);
+
+ unbind_to_cpu(cpunr);
+ commonme();
+
+ return loopcount;
+}
+
+static unsigned long get_performance_repeat(int cpunr, int count, int type)
+{
+ int i;
+
+ unsigned long max = 0, min = ULONG_MAX, real_count = 0;
+ unsigned long long cumulative;
+ unsigned long retval;
+
+ for (i = 0; i < count; i++) {
+ unsigned long temp;
+ temp = get_performance(cpunr);
+ if (temp) {
+ if (temp < min)
+ min = temp;
+
+ if (temp > max)
+ max = temp;
+
+ cumulative += temp;
+ real_count++;
+ }
+ }
+ switch (type) {
+ case GET_PERFORMANCE_MAX:
+ retval = max;
+ break;
+ case GET_PERFORMANCE_MIN:
+ retval = min;
+ break;
+ case GET_PERFORMANCE_AVG:
+ retval = cumulative/real_count;
+ break;
+ default:
+ retval = 0;
+ break;
+ }
+ return retval;
+}
+
+static int get_tstates(cpunr)
+{
+ char path[PATH_MAX];
+ char line[4096];
+ FILE *file;
+ int i, ret, tmp;
+
+ sprintf(path, "/proc/acpi/processor/CPU%x/throttling", cpunr);
+ file = fopen(path, "r");
+ if (file == NULL) {
+ return -1;
+ }
+ memset(line, 0, 4096);
+ if (fgets(line, 4095, file)==NULL) {
+ return -1;
+ }
+ ret = sscanf(line, "state count: %d", &max_tstate);
+ if (ret <= 0) {
+ return -1;
+ }
+
+ if (max_tstate > 0) {
+ tstates = malloc(max_tstate * sizeof(struct tstateinfo));
+ if (tstates == NULL) {
+ return -1;
+ }
+ }
+
+ i = 0;
+ while (i < max_tstate) {
+ memset(line, 0, 4096);
+ if (fgets(line, 4095, file)==NULL) {
+ return -1;
+ }
+ ret = sscanf(line + 4, "T%d: %d", &tmp, &tstates[i].tperf);
+ if (ret != 2) {
+ continue;
+ }
+ i++;
+ if (line[3] == '*') {
+ oldtstate = tmp;
+ }
+ }
+
+ fclose(file);
+ return 0;
+}
+
+static int stop_pstate(int cpunr)
+{
+ char filename[256];
+ FILE * fp = NULL;
+ char line[1024];
+
+ sprintf(filename, "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_governor", cpunr);
+ fp = fopen(filename, "r+");
+ if (fp == NULL) {
+ return 0;
+ }
+ memset(line, 0, 1024);
+ if (fgets(line, 1024, fp) == NULL) {
+ return 0;
+ }
+ strcpy(governor, line);
+ fseek(fp, 0L, SEEK_SET);
+ fputs("userspace", fp);
+ fclose(fp);
+
+ sprintf(filename, "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_cur_freq", cpunr);
+ fp = fopen(filename, "r");
+ if (fp == NULL) {
+ return 0;
+ }
+ memset(line, 0, 1024);
+ if (fgets(line, 1024, fp) == NULL) {
+ return 0;
+ }
+ strcpy(oldfreq, line);
+ fclose(fp);
+
+ sprintf(filename, "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_max_freq", cpunr);
+ fp = fopen(filename, "r");
+ if (fp == NULL) {
+ return 0;
+ }
+
+ memset(line, 0, 1024);
+ if (fgets(line, 1024, fp) == NULL) {
+ return 0;
+ }
+ fclose(fp);
+
+ sprintf(filename, "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_setspeed", cpunr);
+ fp = fopen(filename, "r+");
+ if (fp == NULL) {
+ return 0;
+ }
+ fputs(line, fp);
+ fclose(fp);
+
+ return 0;
+}
+
+static int start_pstate(int cpunr)
+{
+ char filename[256];
+ FILE * fp = NULL;
+ char line[1024];
+
+ sprintf(filename, "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_governor", cpunr);
+ fp = fopen(filename, "r+");
+ if (fp == NULL) {
+ return 0;
+ }
+ fputs(governor, fp);
+ fclose(fp);
+
+ sprintf(filename, "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_setspeed", cpunr);
+ fp = fopen(filename, "r+");
+ if (fp == NULL) {
+ return 0;
+ }
+ fputs(oldfreq, fp);
+ fclose(fp);
+
+ return 0;
+}
+
+static int set_tstate(cpunr, tstate)
+{
+ char filename[256];
+ FILE * fp = NULL;
+ char buffer[16];
+
+ sprintf(filename, "/proc/acpi/processor/CPU%x/throttling", cpunr);
+ fp = fopen(filename, "r+");
+ if (fp == NULL) {
+ return 0;
+ }
+ sprintf(buffer, "%d", tstate);
+ fputs(buffer, fp);
+ fclose(fp);
+}
+
+static int do_tstate_test(int cpunr)
+{
+ char summary[256];
+ char details[4096];
+ int ret = 0;
+ int i, j, speed, max_speed;
+ int max_diff = 10;
+ int diff, len;
+
+ ret = get_tstates(cpunr);
+ if (ret == -1) {
+ printf("FATAL: tstates: /proc/acpi/processor/CPU*/throttling didn't exist or format error.\n");
+ return ret;
+ }
+
+ stop_pstate(cpunr);
+ set_tstate(cpunr, 0);
+ max_speed = get_performance(cpunr);
+ for (i = 0; i < max_tstate; i++) {
+ set_tstate(cpunr, i);
+ tstates[i].speed = get_performance(cpunr);
+ tstates[i].diff = (tstates[i].speed * 100)/max_speed - tstates[i].tperf;
+ report_testrun_progress((i+1)*100/max_tstate);
+ if (abs(tstates[i].diff) > max_diff) {
+ break;
+ }
+ }
+ set_tstate(cpunr, oldtstate);
+ start_pstate(cpunr);
+ len = sprintf(details, "CPU #%d\nT-state Theorical speed Real Speed Diff\n"
+ "======= =============== ========== ====\n"
+ ,cpunr);
+ for (j = 0; j < max_tstate; j++) {
+ len += sprintf(details + len, " T%d\t%12d%%\t%12d%%\t%+6d%%\n",
+ j, tstates[j].tperf, (tstates[j].speed * 100)/max_speed, tstates[j].diff);
+ if (j == i) break;
+ }
+ if (i >= max_tstate) {
+ sprintf(summary, "T-state of CPU #%d is good.", cpunr);
+ report_result("tstates", PASS, summary, details, NULL);
+ } else {
+ sprintf(summary, "T-state of CPU #%d is bad, theorical freq is %d%% greater/less than real freq",
cpunr, diff);
+ report_result("tstates", FAIL, summary, details, NULL);
+ }
+ if (tstates != NULL) {
+ free(tstates);
+ tstates = NULL;
+ }
+}
+
+int main(int argc, char **argv)
+{
+ DIR *dir;
+ struct dirent *entry;
+ char cpupath[2048];
+ int ret = 0;
+ int cpunr;
+
+ start_test("tstates", "Processor T state support",
+ "This test checks if all processors work as T-states definition in ACPI spec. it measures the real
runtime frequency for every T-state every CPU, and check if frequency change is consistent with
percentage of every T-state."
+ );
+
+ dir = opendir("/proc/acpi/processor");
+ if (!dir) {
+ printf("FATAL: tstates: /proc/acpi/processor didn't exist.\n");
+ return 0;
+ }
+
+ do {
+ entry = readdir(dir);
+ if (entry && strlen(entry->d_name)>3) {
+ sprintf(cpupath, "/proc/acpi/processor/%s", entry->d_name);
+ cpunr = strtoul(entry->d_name+3,NULL,16);
+ if ((cpunr == 0) && (entry->d_name[3] != '0')) {
+ continue;
+ }
+ ret = do_tstate_test(cpunr);
+ if (ret == -1) {
+ exit(-1);
+ }
+ }
+ } while (entry);
+
+ finish_test("tstates");
+ return 0;
+}
--- /dev/null 2008-02-21 05:07:22.465822503 +0800
+++ linuxfirmwarekit/tstates/Makefile 2008-02-21 05:37:05.000000000 +0800
<at> <at> -0,0 +1,25 <at> <at>
+override CFLAGS += `pkg-config --cflags glib-2.0` -I.. -fPIC -O
+LDFLAGS = `pkg-config --libs glib-2.0` -lm -L.. -lstandalone
+
+
+all: tstates.exe
+
+tstates.exe: tstates.o .depend
+ gcc tstates.o $(LDFLAGS) -o tstates.exe
+ cp tstates.exe ../plugins
+
+clean:
+ rm -rf *~ *.o
+ rm -f tstates.so tstates.exe
+
+# most of the makefile remains as it was before.
+# at the bottom, we add these lines:
+
+# rule for building dependency lists, and writing them to a file
+# named ".depend".
+.depend:
+ rm -f .depend
+ gccmakedep -f- -- $(CFLAGS) -- *.c > .depend
+
+# now add a line to include the dependency list.
+include .depend