summaryrefslogtreecommitdiff
path: root/library
diff options
context:
space:
mode:
authorJim Warner <james.warner@comcast.net>2022-09-28 00:00:00 -0500
committerCraig Small <csmall@dropbear.xyz>2022-09-28 17:30:56 +1000
commit6e5119658989db9dbc74168985a1de7e0dc7f1fe (patch)
tree6376bfc4b45ebfc6c7a341601993423148046e6e /library
parent3e5016c2898d7129d2cdae7b1def8fe85d41619f (diff)
downloadprocps-ng-6e5119658989db9dbc74168985a1de7e0dc7f1fe.tar.gz
library: provided for cpu p-core/e-core identification
With Intel's 12th generation Alder Lake processors now providing two distinct types of core, it would be nice if the library offered some sort of clue to core type. Well, with this patch it does. We'll have 2 additional enumerators. One deals with the cpu's core association and the other provides the type of that core (P or E). [ now, all we need is for some program to exploit it ] Signed-off-by: Jim Warner <james.warner@comcast.net>
Diffstat (limited to 'library')
-rw-r--r--library/include/stat.h2
-rw-r--r--library/stat.c189
2 files changed, 188 insertions, 3 deletions
diff --git a/library/include/stat.h b/library/include/stat.h
index f4b6f75..8b0f9d7 100644
--- a/library/include/stat.h
+++ b/library/include/stat.h
@@ -32,8 +32,10 @@ enum stat_item {
// returns origin, see proc(5)
// ------- -------------------
STAT_TIC_ID, // s_int /proc/stat, cpu or numa node id
+ STAT_TIC_ID_CORE, // s_int /proc/cpuinfo: 'core id', -1 = n/a
STAT_TIC_NUMA_NODE, // s_int [ CPU ID based, see: numa(3) ]
STAT_TIC_NUM_CONTRIBUTORS, // s_int [ total CPUs contributing to TIC counts ]
+ STAT_TIC_TYPE_CORE, // s_int [ 2 = P-core, 1 = E-core, 0 = n/a ]
STAT_TIC_USER, // ull_int /proc/stat
STAT_TIC_NICE, // ull_int "
diff --git a/library/stat.c b/library/stat.c
index 3b2182e..c7cdef5 100644
--- a/library/stat.c
+++ b/library/stat.c
@@ -37,11 +37,21 @@
#define STAT_FILE "/proc/stat"
+#define CORE_FILE "/proc/cpuinfo"
+#define CORE_BUFSIZ 1024 // buf size for line of /proc/cpuinfo
#define BUFFER_INCR 8192 // amount i/p buffer allocations grow
#define STACKS_INCR 64 // amount reap stack allocations grow
#define NEWOLD_INCR 64 // amount jiffs hist allocations grow
+#define ECORE_BEGIN 10 // PRETEND_E_CORES begin at this cpu#
+
+/* ------------------------------------------------------------------------- +
+ this provision just does what its name sugggests - it will create several |
+ E-Core cpus for testing that STAT_TIC_ID_CORE & STAT_TIC_TYPE_CORE stuff! |*/
+// #define PRETEND_E_CORES //----------------------------------------------- |
+// ------------------------------------------------------------------------- +
+
/* ------------------------------------------------------------------------- +
this provision can be used to ensure that our Item_table was synchronized |
with those enumerators found in the associated header file. It's intended |
@@ -73,6 +83,14 @@ struct stat_jifs {
unsigned long long xusr, xsys, xidl, xbsy, xtot;
};
+struct stat_core {
+ int id;
+ int type; // 2 = p-core, 1 = e-core, 0 = unsure
+ int thread_1;
+ int thread_2;
+ struct stat_core *next;
+};
+
struct stat_data {
unsigned long intr;
unsigned long ctxt;
@@ -94,8 +112,10 @@ struct hist_tic {
struct stat_jifs new;
struct stat_jifs old;
#ifdef CPU_IDLE_FORCED
- unsigned long edge; // only valued/valid with cpu summary |
+ unsigned long edge; // only valued/valid with cpu summary
#endif
+ struct stat_core *core;
+ int saved_id;
};
struct stacks_extent {
@@ -135,6 +155,7 @@ struct stat_info {
FILE *stat_fp;
char *stat_buf; // grows to accommodate all /proc/stat
int stat_buf_size; // current size for the above stat_buf
+ int cpu_count_hwm; // if changed, triggers new cores scan
struct hist_sys sys_hist; // SYS type management
struct hist_tic cpu_hist; // TIC type management for cpu summary
struct reap_support cpus; // TIC type management for real cpus
@@ -146,9 +167,9 @@ struct stat_info {
struct item_support reap_items; // items used for reap (shared among 3)
struct item_support select_items; // items unique to select
time_t sav_secs; // used by procps_stat_get to limit i/o
+ struct stat_core *cores; // linked list, also linked from hist_tic
};
-
// ___ Results 'Set' Support ||||||||||||||||||||||||||||||||||||||||||||||||||
#define setNAME(e) set_stat_ ## e
@@ -171,8 +192,10 @@ setDECL(noop) { (void)R; (void)S; (void)T; }
setDECL(extra) { (void)S; (void)T; R->result.ull_int = 0; }
setDECL(TIC_ID) { (void)S; R->result.s_int = T->id; }
+setDECL(TIC_ID_CORE) { (void)S; R->result.s_int = (T->core) ? T->core->id : -1; }
setDECL(TIC_NUMA_NODE) { (void)S; R->result.s_int = T->numa_node; }
setDECL(TIC_NUM_CONTRIBUTORS) { (void)S; R->result.s_int = T->count; }
+setDECL(TIC_TYPE_CORE) { (void)S; R->result.s_int = (T->core) ? T->core->type : 0; }
TIC_set(TIC_USER, ull_int, user)
TIC_set(TIC_NICE, ull_int, nice)
@@ -309,8 +332,10 @@ static struct {
{ RS(extra), QS(ull_int), TS_noop },
{ RS(TIC_ID), QS(s_int), TS(s_int) },
+ { RS(TIC_ID_CORE), QS(s_int), TS(s_int) },
{ RS(TIC_NUMA_NODE), QS(s_int), TS(s_int) },
{ RS(TIC_NUM_CONTRIBUTORS), QS(s_int), TS(s_int) },
+ { RS(TIC_TYPE_CORE), QS(s_int), TS(s_int) },
{ RS(TIC_USER), QS(ull_int), TS(ull_int) },
{ RS(TIC_NICE), QS(ull_int), TS(ull_int) },
{ RS(TIC_SYSTEM), QS(ull_int), TS(ull_int) },
@@ -393,6 +418,132 @@ static inline void stat_assign_results (
} // end: stat_assign_results
+#define E_CORE 1
+#define P_CORE 2
+#define VACANT -1
+
+static int stat_core_add (
+ struct stat_info *info,
+ int a_core,
+ int a_cpu)
+{
+ struct stat_core *last = NULL, *core = info->cores;
+
+ while (core) {
+ if (core->id == a_core) {
+ if (a_cpu == core->thread_1
+ || (a_cpu == core->thread_2))
+ return 1;
+ core->thread_2 = a_cpu;
+ core->type = P_CORE;
+ return 1;
+ }
+ last = core;
+ core = core->next;
+ }
+ if (!(core = calloc(1, sizeof(struct stat_core))))
+ return 0;
+ if (last) last->next = core;
+ else info->cores = core;
+ core->id = a_core;
+ core->thread_1 = a_cpu;
+ core->thread_2 = VACANT;
+ return 1;
+} // end: stat_core_add
+
+
+static void stat_cores_check (
+ struct stat_info *info)
+{
+ struct stat_core *core = info->cores;
+ int p_core = 0;
+
+ core = info->cores;
+ while (core) {
+ if (core->type == P_CORE) {
+ p_core = 1;
+ break;
+ }
+ core = core->next;
+ }
+ if (p_core) {
+ core = info->cores;
+ while (core) {
+ if (core->thread_2 == VACANT)
+ core->type = E_CORE;
+ core = core->next;
+ }
+ }
+} // end: stat_cores_check
+
+#undef E_CORE
+#undef P_CORE
+#undef VACANT
+
+
+static void stat_cores_link (
+ struct stat_info *info,
+ struct hist_tic *this)
+{
+ struct stat_core *core = info->cores;
+
+ while (core) {
+ if (this->id == core->thread_1
+ || (this->id == core->thread_2)) {
+ this->core = core;
+ break;
+ }
+ core = core->next;
+ }
+} // end: stat_cores_link
+
+
+static int stat_cores_verify (
+ struct stat_info *info)
+{
+ char buf[CORE_BUFSIZ];
+ int a_cpu, a_core;
+ FILE *fp;
+
+ if (!(fp = fopen(CORE_FILE, "r")))
+ return 0;
+ for (;;) {
+ if (NULL == fgets(buf, sizeof(buf), fp))
+ break;
+ if (buf[0] != 'p') continue;
+ if (!strstr(buf, "processor"))
+ continue;
+ sscanf(buf, "processor : %d", &a_cpu);
+ for (;;) {
+ if (NULL == fgets(buf, sizeof(buf), fp)) {
+ fclose(fp);
+ errno = EIO;
+ return 0;
+ }
+ if (buf[0] != 'c') continue;
+ if (!strstr(buf, "core id"))
+ continue;
+ sscanf(buf, "core id : %d", &a_core);
+ break;
+ }
+#ifdef PRETEND_E_CORES
+ { static int fake_core;
+ if (a_cpu > ECORE_BEGIN) {
+ if (!fake_core) fake_core = a_core + 1;
+ a_core = fake_core++;
+ } }
+#endif
+ if (!stat_core_add(info, a_core, a_cpu)) {
+ fclose(fp);
+ return 0;
+ }
+ }
+ fclose(fp);
+ stat_cores_check(info);
+ return 1;
+} // end: stat_cores_verify
+
+
static inline void stat_derive_unique (
struct hist_tic *this)
{
@@ -570,7 +721,7 @@ static int stat_read_failed (
#define curSIZ ( maxSIZ - tot_read )
#define curPOS ( info->stat_buf + tot_read )
/* we slurp in the entire directory thus avoiding repeated calls to fread, |
- especially in a massively parallel environment. additionally, each cpu |
+ especially for a massively parallel environment. additionally, each cpu |
line is then frozen in time rather than changing until we get around to |
accessing it. this helps to minimize (not eliminate) some distortions. | */
tot_read = 0;
@@ -647,6 +798,16 @@ reap_em_again:
break; // we must tolerate cpus taken offline
}
stat_derive_unique(cpu_ptr);
+
+ /* this happens if cpus are taken offline/brought back online
+ so we better force the proper current core association ... */
+ if (cpu_ptr->saved_id != cpu_ptr->id) {
+ cpu_ptr->saved_id = cpu_ptr->id;
+ cpu_ptr->core = NULL;
+ }
+ if (!cpu_ptr->core)
+ stat_cores_link(info, cpu_ptr);
+
#ifdef CPU_IDLE_FORCED
// first time through (that priming read) sum_ptr->edge will be zero |
if (cpu_ptr->new.xtot < sum_ptr->edge) {
@@ -668,6 +829,13 @@ reap_em_again:
}
info->cpus.total = info->cpus.hist.n_inuse = sum_ptr->count = i;
+ /* whoa, if a new cpu was brought online, we better
+ ensure that no new cores have now become visible */
+ if (info->cpu_count_hwm < info->cpus.total) {
+ if (!stat_cores_verify(info))
+ return 1;
+ info->cpu_count_hwm = info->cpus.total;
+ }
// remember sys_hist stuff from last time around
memcpy(&info->sys_hist.old, &info->sys_hist.new, sizeof(struct stat_data));
@@ -911,6 +1079,12 @@ PROCPS_EXPORT int procps_stat_new (
numa_init();
+ // identify the current P-cores and E-cores, if any
+ if (!stat_cores_verify(p)) {
+ procps_stat_unref(&p);
+ return -errno;
+ }
+
/* do a priming read here for the following potential benefits: |
1) ensure there will be no problems with subsequent access |
2) make delta results potentially useful, even if 1st time |
@@ -981,6 +1155,15 @@ PROCPS_EXPORT int procps_stat_unref (
if ((*info)->select_items.enums)
free((*info)->select_items.enums);
+ if ((*info)->cores) {
+ struct stat_core *next, *this = (*info)->cores;
+ while (this) {
+ next = this->next;
+ free(this);
+ this = next;
+ };
+ }
+
numa_uninit();
free(*info);