]> granicus.if.org Git - procps-ng/commitdiff
library: provided for cpu p-core/e-core identification
authorJim Warner <james.warner@comcast.net>
Wed, 28 Sep 2022 05:00:00 +0000 (00:00 -0500)
committerCraig Small <csmall@dropbear.xyz>
Wed, 28 Sep 2022 07:30:56 +0000 (17:30 +1000)
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>
library/include/stat.h
library/stat.c

index f4b6f75aa854aaeeb61deeb9c30f388f443a44e7..8b0f9d72f69d87db603cb194cd58e3d65a8fb648 100644 (file)
@@ -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         "
index 3b2182e045297e8d4624de3a71c7e46d36031be1..c7cdef5296a6461d2bdbad96c8ff923cf310ddf7 100644 (file)
 
 
 #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);