[Powertop] [PATCH] Report accurate wakeup numbers for hyper-threaded CPU

Igor Zhbanov <i.zhbanov at samsung.com> Wed, 18 Jul 2012 18:53:04 +0400
Newsgroups dev.linux.lists.powertop
Message-ID <[email protected]>
This is the commit 0f7e6dd0ab7b1b02ef7e55749d6a34d6801c3973 from Linaro's
PowerTOP repository.
Author: John Mathew <johnx.mathew(a)intel.com>

Intel hyperthreaded cores does not exactly behave like if it was 2 real cores.
-   Actual HW cstate is roughly minimal cstate of the 2 threads.
As most of the resources are shared, HW resources are only really powered
down when both threads are idle.
-   Interrupts always wakes up the 2 threads. i.e. terminate the 2 mwait.
So in case of an hyperthreaded core a wakeup event is affecting power only when
both the threads are asleep. A wakeup event that occurs when one of
the thread is active should not be considered as a wakeup event.

In case of hyperthreaded cores the thread ids that execute on the same core
will be reported in the sys/devices/system/cpu/cpu%i/topology/thread_siblings_list.
A table is initially created with this thread sibling information corresponding to
a cpu. When powertop gets a power_end event on a thread the thread_sibling table
is looked up to identify the siblings of the cpu and each sibling is checked it
has a wakeup pending. If any one sibling does not have a wakeup event pending
the power end event is ignored.
---
 src/cpu/cpu.cpp            |   28 ++++++++++++++++++++++++++++
 src/cpu/cpu.h              |    7 +++++++
 src/process/do_process.cpp |   24 ++++++++++++++++++++++++
 3 files changed, 59 insertions(+), 0 deletions(-)

diff --git a/src/cpu/cpu.cpp b/src/cpu/cpu.cpp
index 941bcff..ab7e724 100644
--- a/src/cpu/cpu.cpp
+++ b/src/cpu/cpu.cpp
@@ -28,6 +28,7 @@
 #include <string.h>
 #include <stdlib.h>
 #include <ncurses.h>
+#include <sstream>
 
 #include "cpu.h"
 #include "cpudevice.h"
@@ -38,12 +39,15 @@
 #include "../display.h"
 #include "../report.h"
 
+using namespace std;
+
 static class abstract_cpu system_level;
 
 vector<class abstract_cpu *> all_cpus;
 
 static	class perf_bundle * perf_events;
 
+vector<struct thread_sibling_info *> thread_sibling_table;
 
 
 class perf_power_bundle: public perf_bundle
@@ -163,6 +167,7 @@ static void handle_one_cpu(unsigned int number, char *vendor, int family, int mo
 	unsigned int package_number = 0;
 	unsigned int core_number = 0;
 	class abstract_cpu *package, *core, *cpu;
+	vector<unsigned int> thread_siblings;
 
 	sprintf(filename, "/sys/devices/system/cpu/cpu%i/topology/core_id", number);
 	file.open(filename, ios::in);
@@ -180,6 +185,29 @@ static void handle_one_cpu(unsigned int number, char *vendor, int family, int mo
 		file.close();
 	}
 
+	sprintf(filename,
+		"/sys/devices/system/cpu/cpu%i/topology/thread_siblings_list",
+		number);
+	file.open(filename, ios::in);
+	if (file) {
+		string line;
+		getline(file,line);
+		istringstream linestream(line);
+		string item;
+		struct thread_sibling_info *info;
+
+		while (getline(linestream, item, '-'))
+			thread_siblings.push_back(atoi(item.c_str()));
+
+		if (thread_siblings.size() > 1) {
+			info = new struct thread_sibling_info;
+			info->cpunum = number;
+			info->thread_siblings = thread_siblings;
+			thread_sibling_table.push_back(info);
+		}
+
+		file.close();
+	}
 
 	if (system_level.children.size() <= package_number)
 		system_level.children.resize(package_number + 1, NULL);
diff --git a/src/cpu/cpu.h b/src/cpu/cpu.h
index b48ada9..54da093 100644
--- a/src/cpu/cpu.h
+++ b/src/cpu/cpu.h
@@ -39,6 +39,13 @@ class abstract_cpu;
 #define LEVEL_C0 -1
 #define LEVEL_HEADER -2
 
+struct thread_sibling_info {
+	unsigned int cpunum;
+	vector<unsigned int> thread_siblings;
+};
+
+extern vector<struct thread_sibling_info *> thread_sibling_table;
+
 #define PSTATE 1
 #define CSTATE 2
 
diff --git a/src/process/do_process.cpp b/src/process/do_process.cpp
index 731fe3e..60bc790 100644
--- a/src/process/do_process.cpp
+++ b/src/process/do_process.cpp
@@ -44,6 +44,7 @@
 #include "../parameters/parameters.h"
 #include "../display.h"
 #include "../measurement/measurement.h"
+#include "../cpu/cpu.h"
 
 static  class perf_bundle * perf_events;
 
@@ -147,6 +148,27 @@ static void change_blame(unsigned int cpu, class power_consumer *consumer, int l
 	cpu_level[cpu] = level;
 }
 
+static bool sibling_wakeup_pending(unsigned int cpu)
+{
+	unsigned int i, j;
+	struct thread_sibling_info *info;
+	bool wakeup_pending = true;
+
+	for (i = 0; i < thread_sibling_table.size(); i++) {
+		info = thread_sibling_table[i];
+		if (cpu != info->cpunum)
+			continue;
+
+		for (j = 0; j < info->thread_siblings.size(); j++)
+			if (!get_wakeup_pending(info->thread_siblings[j])) {
+				wakeup_pending = false;
+				break;
+			}
+	}
+
+	return wakeup_pending;
+}
+
 static void consume_blame(unsigned int cpu)
 {
 	if (!get_wakeup_pending(cpu))
@@ -157,6 +179,8 @@ static void consume_blame(unsigned int cpu)
 		return;
 	if (!cpu_blame[cpu])
 		return;
+	if (!sibling_wakeup_pending(cpu))
+		return;
 
 	cpu_blame[cpu]->wake_ups++;
 	cpu_blame[cpu] = NULL;
-- 
1.7.5.4