From a5fb8431d9888cbd4b40e6435d4379bf0c86cdec Mon Sep 17 00:00:00 2001
From: Lyonel Vincent <lyonel@ezix.org>
Date: Thu, 20 Oct 2016 00:27:58 +0200
Subject: [PATCH 18/43] merge github pull request 23: parse CPU information
FIXME: `dimminfo` magic needs cleansing, ugly code getting uglier
---
src/core/device-tree.cc | 267 ++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 237 insertions(+), 30 deletions(-)
diff --git a/src/core/device-tree.cc b/src/core/device-tree.cc
index c2b7d15..700dff0 100644
--- a/src/core/device-tree.cc
+++ b/src/core/device-tree.cc
@@ -26,6 +26,8 @@
#include <string.h>
#include <unistd.h>
#include <dirent.h>
+#include <utility>
+#include <map>
__ID("@(#) $Id$");
@@ -227,6 +229,42 @@ static string cpubusinfo(int cpu)
}
+static void set_cpu(hwNode & cpu, int currentcpu, const string & basepath)
+{
+ cpu.setProduct(get_string(basepath + "/name"));
+ cpu.claim();
+ cpu.setBusInfo(cpubusinfo(currentcpu));
+
+ cpu.setSize(get_u32(basepath + "/clock-frequency"));
+ cpu.setClock(get_u32(basepath + "/bus-frequency"));
+
+ if (exists(basepath + "/altivec"))
+ cpu.addCapability("altivec");
+
+ if (exists(basepath + "/performance-monitor"))
+ cpu.addCapability("performance-monitor");
+}
+
+
+static void fill_cache_info(string cache_type, string cachebase,
+ hwNode & cache, hwNode & icache)
+{
+ cache.claim();
+ cache.setDescription(cache_type);
+ cache.setSize(get_u32(cachebase + "/d-cache-size"));
+
+ if (exists(cachebase + "/cache-unified"))
+ cache.setDescription(cache.getDescription() + " (unified)");
+ else
+ {
+ icache = cache;
+ cache.setDescription(cache.getDescription() + " (data)");
+ icache.setDescription(icache.getDescription() + " (instruction)");
+ icache.setSize(get_u32(cachebase + "/i-cache-size"));
+ }
+}
+
+
static void scan_devtree_cpu(hwNode & core)
{
struct dirent **namelist;
@@ -254,14 +292,8 @@ static void scan_devtree_cpu(hwNode & core)
hw::strip(get_string(basepath + "/device_type")) != "cpu")
break; // oops, not a CPU!
- cpu.setProduct(get_string(basepath + "/name"));
cpu.setDescription("CPU");
- cpu.claim();
- cpu.setBusInfo(cpubusinfo(currentcpu++));
- cpu.setSize(get_u32(basepath + "/clock-frequency"));
- cpu.setClock(get_u32(basepath + "/bus-frequency"));
- if (exists(basepath + "/altivec"))
- cpu.addCapability("altivec");
+ set_cpu(cpu, currentcpu++, basepath);
version = get_u32(basepath + "/cpu-version");
if (version != 0)
@@ -273,14 +305,11 @@ static void scan_devtree_cpu(hwNode & core)
snprintf(buffer, sizeof(buffer), "%lx.%d.%d",
(version & 0xffff0000) >> 16, major, minor);
cpu.setVersion(buffer);
-
}
+
if (hw::strip(get_string(basepath + "/state")) != "running")
cpu.disable();
- if (exists(basepath + "/performance-monitor"))
- cpu.addCapability("performance-monitor");
-
if (exists(basepath + "/d-cache-size"))
{
hwNode cache("cache",
@@ -302,29 +331,19 @@ static void scan_devtree_cpu(hwNode & core)
{
hwNode cache("cache",
hw::memory);
+ hwNode icache("cache",
+ hw::memory);
string cachebase = basepath + "/" + cachelist[j]->d_name;
if (hw::strip(get_string(cachebase + "/device_type")) != "cache" &&
hw::strip(get_string(cachebase + "/device_type")) != "l2-cache")
break; // oops, not a cache!
- cache.claim();
- cache.setDescription("L2 Cache");
- cache.setSize(get_u32(cachebase + "/d-cache-size"));
- cache.setClock(get_u32(cachebase + "/clock-frequency"));
+ cache.setClock(get_u32(cachebase + "/clock-frequency"));
+ fill_cache_info("L2 Cache", cachebase, cache, icache);
- if (exists(cachebase + "/cache-unified"))
- cache.setDescription(cache.getDescription() + " (unified)");
- else
- {
- hwNode icache = cache;
- cache.setDescription(cache.getDescription() + " (data)");
- icache.setDescription(icache.getDescription() + " (instruction)");
- icache.setSize(get_u32(cachebase + "/i-cache-size"));
-
- if (icache.getSize() > 0)
- cpu.addChild(icache);
- }
+ if (icache.getSize() > 0)
+ cpu.addChild(icache);
if (cache.getSize() > 0)
cpu.addChild(cache);
@@ -342,6 +361,191 @@ static void scan_devtree_cpu(hwNode & core)
}
}
+static void set_cpu_config_threads(hwNode & cpu, const string & basepath)
+{
+ static int threads_per_cpu = 0;
+
+ /* In power systems, there are equal no. of threads per cpu-core */
+ if (threads_per_cpu == 0)
+ {
+ int rc;
+ struct stat sbuf;
+ string p = hw::strip(basepath + string("/ibm,ppc-interrupt-server#s"));
+
+ /*
+ * This file contains as many 32 bit interrupt server numbers, as the
+ * number of threads per CPU (in hexadecimal format). st_size gives size
+ * in bytes of a file. Hence, grouping by 4 bytes, we get the thread
+ * count.
+ */
+ rc = stat(p.c_str(), &sbuf);
+ if (!rc)
+ threads_per_cpu = sbuf.st_size / 4;
+ }
+
+ cpu.setConfig("threads", threads_per_cpu);
+}
+
+
+static void scan_devtree_cpu_power(hwNode & core)
+{
+ int n;
+ int currentcpu = 0;
+ struct dirent **namelist;
+ map <uint32_t, pair<uint32_t, vector <hwNode> > > l2_caches;
+ map <uint32_t, vector <hwNode> > l3_caches;
+
+ pushd(DEVICETREE "/cpus");
+ n = scandir(".", &namelist, selectdir, alphasort);
+ popd();
+ if (n < 0)
+ return;
+
+ /*
+ * 'cpus' node contains CPU, L2 and L3 cache nodes. L1 cache information is
+ * available under CPU node itself. l2-cache (or next-level-cache) property
+ * contains next level cache node phandle/ibm,phanle number.
+ * First pass creates cache nodes and second pass will link cache nodes to
+ * corresponding CPU nodes.
+ */
+ for (int i = 0; i < n; i++)
+ {
+ string product;
+ string basepath = string(DEVICETREE "/cpus/") + string(namelist[i]->d_name);
+ hwNode cache("cache", hw::memory);
+ hwNode icache("cache", hw::memory);
+ vector <hwNode> value;
+
+ if (!exists(basepath + "/device_type"))
+ continue;
+
+ if (hw::strip(get_string(basepath + "/device_type")) != "cache")
+ continue;
+
+ product = hw::strip(get_string(basepath + "/name"));
+
+ if (hw::strip(get_string(basepath + "/status")) != "okay")
+ cache.disable();
+
+ if (product == "l2-cache")
+ fill_cache_info("L2 Cache", basepath, cache, icache);
+ else
+ fill_cache_info("L3 Cache", basepath, cache, icache);
+
+ if (icache.getSize() > 0)
+ value.insert(value.begin(), icache);
+
+ if (cache.getSize() > 0)
+ value.insert(value.begin(), cache);
+
+ if (value.size() > 0)
+ {
+ uint32_t phandle = 0;
+
+ if (exists(basepath + "/phandle"))
+ phandle = get_u32(basepath + "/phandle");
+ else if (exists(basepath + "/ibm,phandle")) // on pSeries LPARs
+ phandle = get_u32(basepath + "/ibm,phandle");
+
+ if (!phandle)
+ continue;
+
+ if (product == "l2-cache")
+ {
+ uint32_t l3_key = 0; // 0 indicating no next level of cache
+
+ if (exists(basepath + "/l2-cache"))
+ l3_key = get_u32(basepath + "/l2-cache");
+ else if (exists(basepath + "/next-level-cache")) //on OpenPOWER systems
+ l3_key = get_u32(basepath + "/next-level-cache");
+
+ pair <uint32_t, vector <hwNode> > p (l3_key, value);
+ l2_caches[phandle] = p;
+ }
+ else if (product == "l3-cache")
+ {
+ l3_caches[phandle] = value;
+ }
+ }
+ } // first pass end
+
+ for (int i = 0; i < n; i++) //second and final pass
+ {
+ uint32_t l2_key = 0;
+ uint32_t version = 0;
+ string basepath = string(DEVICETREE "/cpus/") + string(namelist[i]->d_name);
+ hwNode cpu("cpu", hw::processor);
+
+ if (!exists(basepath + "/device_type"))
+ {
+ free(namelist[i]);
+ continue;
+ }
+
+ if (hw::strip(get_string(basepath + "/device_type")) != "cpu")
+ {
+ free(namelist[i]);
+ continue;
+ }
+
+ cpu.setDescription("CPU");
+ set_cpu(cpu, currentcpu++, basepath);
+
+ version = get_u32(basepath + "/cpu-version");
+ if (version != 0)
+ cpu.setVersion(tostring(version));
+
+ if (hw::strip(get_string(basepath + "/status")) != "okay")
+ cpu.disable();
+
+ set_cpu_config_threads(cpu, basepath);
+
+ if (exists(basepath + "/d-cache-size"))
+ {
+ hwNode cache("cache", hw::memory);
+ hwNode icache("cache", hw::memory);
+
+ fill_cache_info("L1 Cache", basepath, cache, icache);
+
+ if (icache.getSize() > 0)
+ cpu.addChild(icache);
+
+ if (cache.getSize() > 0)
+ cpu.addChild(cache);
+ }
+
+ if (exists(basepath + "/l2-cache"))
+ l2_key = get_u32(basepath + "/l2-cache");
+ else if (exists(basepath + "/next-level-cache"))
+ l2_key = get_u32(basepath + "/next-level-cache");
+
+ if (l2_key != 0)
+ {
+ map <uint32_t, pair <uint32_t, vector <hwNode> > >::
+ const_iterator got = l2_caches.find(l2_key);
+
+ if (!(got == l2_caches.end()))
+ for (uint32_t j = 0; j < (got->second).second.size(); j++)
+ cpu.addChild((got->second).second[j]);
+
+ if ((got->second).first != 0) // we have another level of cache
+ {
+ map <uint32_t, vector <hwNode> >::const_iterator got_l3 =
+ l3_caches.find ((got->second).first);
+
+ if (!(got_l3 == l3_caches.end()))
+ for (uint32_t j = 0; j < (got_l3->second).size(); j++)
+ cpu.addChild((got_l3->second)[j]);
+ }
+ }
+
+ core.addChild(cpu);
+
+ free(namelist[i]);
+ }
+ free(namelist);
+}
+
void add_memory_bank(string name, string path, hwNode & core)
{
struct dirent **dirlist;
@@ -728,7 +932,7 @@ bool scan_device_tree(hwNode & n)
core->addHint("icon", string("board"));
scan_devtree_root(*core);
scan_devtree_memory_powernv(*core);
- scan_devtree_cpu(*core);
+ scan_devtree_cpu_power(*core);
n.addCapability("powernv", "Non-virtualized");
n.addCapability("opal", "OPAL firmware");
}
@@ -764,7 +968,7 @@ bool scan_device_tree(hwNode & n)
{
core->addHint("icon", string("board"));
scan_devtree_root(*core);
- scan_devtree_cpu(*core);
+ scan_devtree_cpu_power(*core);
core->addCapability("qemu", "QEMU virtualization");
core->addCapability("guest", "Virtualization guest");
}
@@ -779,7 +983,10 @@ bool scan_device_tree(hwNode & n)
scan_devtree_root(*core);
scan_devtree_bootrom(*core);
scan_devtree_memory(*core);
- scan_devtree_cpu(*core);
+ if (exists(DEVICETREE "/ibm,lpar-capable"))
+ scan_devtree_cpu_power(*core);
+ else
+ scan_devtree_cpu(*core);
}
}
--
2.10.2