Parse /proc/cpuinfo on Linux/x86 to detect APIC IDs
diff --git a/jni/Android.mk b/jni/Android.mk
index be17ebc..26de2cf 100644
--- a/jni/Android.mk
+++ b/jni/Android.mk
@@ -43,6 +43,7 @@
$(LOCAL_PATH)/src/x86/cache/init.c \
$(LOCAL_PATH)/src/x86/cache/descriptor.c \
$(LOCAL_PATH)/src/x86/cache/deterministic.c \
+ $(LOCAL_PATH)/src/x86/linux/cpuinfo.c \
$(LOCAL_PATH)/src/x86/linux/init.c
endif # x86 or x86_64
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
@@ -94,6 +95,7 @@
$(LOCAL_PATH)/src/x86/cache/init.c \
$(LOCAL_PATH)/src/x86/cache/descriptor.c \
$(LOCAL_PATH)/src/x86/cache/deterministic.c \
+ $(LOCAL_PATH)/src/x86/linux/cpuinfo.c \
$(LOCAL_PATH)/src/x86/linux/init.c
endif # x86 or x86_64
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
diff --git a/src/linux/api.h b/src/linux/api.h
index 33de37f..968d65d 100644
--- a/src/linux/api.h
+++ b/src/linux/api.h
@@ -10,10 +10,13 @@
#define CPUINFO_LINUX_MASK_USABLE UINT32_C(0x00000003)
#define CPUINFO_LINUX_FLAG_MAX_FREQUENCY UINT32_C(0x00000004)
#define CPUINFO_LINUX_FLAG_MIN_FREQUENCY UINT32_C(0x00000008)
-#define CPUINFO_LINUX_FLAG_CORE_ID UINT32_C(0x00000010)
-#define CPUINFO_LINUX_FLAG_CORE_CLUSTER UINT32_C(0x00000020)
+#define CPUINFO_LINUX_FLAG_SMT_ID UINT32_C(0x00000010)
+#define CPUINFO_LINUX_FLAG_CORE_ID UINT32_C(0x00000020)
#define CPUINFO_LINUX_FLAG_PACKAGE_ID UINT32_C(0x00000040)
-#define CPUINFO_LINUX_FLAG_PACKAGE_CLUSTER UINT32_C(0x00000080)
+#define CPUINFO_LINUX_FLAG_APIC_ID UINT32_C(0x00000080)
+#define CPUINFO_LINUX_FLAG_SMT_CLUSTER UINT32_C(0x00000100)
+#define CPUINFO_LINUX_FLAG_CORE_CLUSTER UINT32_C(0x00000200)
+#define CPUINFO_LINUX_FLAG_PACKAGE_CLUSTER UINT32_C(0x00000400)
typedef bool (*cpuinfo_cpulist_callback)(uint32_t, uint32_t, void*);
diff --git a/src/x86/api.h b/src/x86/api.h
index ab837a7..9c691b7 100644
--- a/src/x86/api.h
+++ b/src/x86/api.h
@@ -34,8 +34,6 @@
};
struct cpuinfo_x86_model_info {
- uint32_t cpuid;
-
uint32_t model;
uint32_t family;
@@ -56,12 +54,12 @@
};
struct cpuinfo_x86_processor {
+ uint32_t cpuid;
enum cpuinfo_vendor vendor;
enum cpuinfo_uarch uarch;
#ifdef __linux__
int linux_id;
#endif
- struct cpuinfo_x86_model_info model_info;
struct cpuinfo_x86_caches cache;
struct {
struct cpuinfo_tlb itlb_4KB;
diff --git a/src/x86/info.c b/src/x86/info.c
index 99314e8..ceb6b84 100644
--- a/src/x86/info.c
+++ b/src/x86/info.c
@@ -6,7 +6,6 @@
struct cpuinfo_x86_model_info cpuinfo_x86_decode_model_info(uint32_t eax) {
struct cpuinfo_x86_model_info model_info;
- model_info.cpuid = eax;
model_info.stepping = eax & 0xF;
model_info.base_model = (eax >> 4) & 0xF;
model_info.base_family = (eax >> 8) & 0xF;
diff --git a/src/x86/init.c b/src/x86/init.c
index b545536..3dcbb28 100644
--- a/src/x86/init.c
+++ b/src/x86/init.c
@@ -26,6 +26,8 @@
if (max_base_index >= 1) {
const struct cpuid_regs leaf1 = cpuid(1);
+ processor->cpuid = leaf1.eax;
+
const struct cpuinfo_x86_model_info model_info = cpuinfo_x86_decode_model_info(leaf1.eax);
const enum cpuinfo_uarch uarch = processor->uarch =
cpuinfo_x86_decode_uarch(vendor, &model_info);
@@ -71,5 +73,6 @@
brand_string[i] = cpuid(UINT32_C(0x80000002) + i);
}
memcpy(processor->brand_string, brand_string, sizeof(processor->brand_string));
+ cpuinfo_log_debug("raw CPUID brand string: \"%48s\"", processor->brand_string);
}
}
diff --git a/src/x86/linux/api.h b/src/x86/linux/api.h
new file mode 100644
index 0000000..33cd7f2
--- /dev/null
+++ b/src/x86/linux/api.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <cpuinfo.h>
+#include <arm/midr.h>
+#include <arm/api.h>
+#include <linux/api.h>
+
+
+struct cpuinfo_x86_linux_processor {
+ uint32_t apic_id;
+ uint32_t linux_id;
+ uint32_t flags;
+};
+
+bool cpuinfo_x86_linux_parse_proc_cpuinfo(
+ uint32_t max_processors_count,
+ struct cpuinfo_x86_linux_processor processors[restrict static max_processors_count]);
diff --git a/src/x86/linux/cpuinfo.c b/src/x86/linux/cpuinfo.c
new file mode 100644
index 0000000..5042cb0
--- /dev/null
+++ b/src/x86/linux/cpuinfo.c
@@ -0,0 +1,205 @@
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+
+#include <linux/api.h>
+#include <x86/linux/api.h>
+#include <log.h>
+
+/*
+ * Size, in chars, of the on-stack buffer used for parsing lines of /proc/cpuinfo.
+ * This is also the limit on the length of a single line.
+ */
+#define BUFFER_SIZE 2048
+
+
+static uint32_t parse_processor_number(
+ const char* processor_start,
+ const char* processor_end)
+{
+ const size_t processor_length = (size_t) (processor_end - processor_start);
+
+ if (processor_length == 0) {
+ cpuinfo_log_warning("Processor number in /proc/cpuinfo is ignored: string is empty");
+ return 0;
+ }
+
+ uint32_t processor_number = 0;
+ for (const char* digit_ptr = processor_start; digit_ptr != processor_end; digit_ptr++) {
+ const uint32_t digit = (uint32_t) (*digit_ptr - '0');
+ if (digit > 10) {
+ cpuinfo_log_warning("non-decimal suffix %.*s in /proc/cpuinfo processor number is ignored",
+ (int) (processor_end - digit_ptr), digit_ptr);
+ break;
+ }
+
+ processor_number = processor_number * 10 + digit;
+ }
+
+ return processor_number;
+}
+
+/*
+ * Decode APIC ID reported by Linux kernel for x86/x86-64 architecture.
+ * Example of APIC ID reported in /proc/cpuinfo:
+ *
+ * apicid : 2
+ */
+static void parse_apic_id(
+ const char* apic_start,
+ const char* apic_end,
+ struct cpuinfo_x86_linux_processor processor[restrict static 1])
+{
+ uint32_t apic_id = 0;
+ for (const char* digit_ptr = apic_start; digit_ptr != apic_end; digit_ptr++) {
+ const uint32_t digit = *digit_ptr - '0';
+ if (digit >= 10) {
+ cpuinfo_log_warning("APIC ID %.*s in /proc/cpuinfo is ignored due to unexpected non-digit character '%c' at offset %zu",
+ (int) (apic_end - apic_start), apic_start,
+ *digit_ptr, (size_t) (digit_ptr - apic_start));
+ return;
+ }
+
+ apic_id = apic_id * 10 + digit;
+ }
+
+ processor->apic_id = apic_id;
+ processor->flags |= CPUINFO_LINUX_FLAG_APIC_ID;
+}
+
+struct proc_cpuinfo_parser_state {
+ uint32_t processor_index;
+ uint32_t max_processors_count;
+ struct cpuinfo_x86_linux_processor* processors;
+ struct cpuinfo_x86_linux_processor dummy_processor;
+};
+
+/*
+ * Decode a single line of /proc/cpuinfo information.
+ * Lines have format <words-with-spaces>[ ]*:[ ]<space-separated words>
+ */
+static bool parse_line(
+ const char* line_start,
+ const char* line_end,
+ struct proc_cpuinfo_parser_state state[restrict static 1],
+ uint64_t line_number)
+{
+ /* Empty line. Skip. */
+ if (line_start == line_end) {
+ return true;
+ }
+
+ /* Search for ':' on the line. */
+ const char* separator = line_start;
+ for (; separator != line_end; separator++) {
+ if (*separator == ':') {
+ break;
+ }
+ }
+ /* Skip line if no ':' separator was found. */
+ if (separator == line_end) {
+ cpuinfo_log_warning("Line %.*s in /proc/cpuinfo is ignored: key/value separator ':' not found",
+ (int) (line_end - line_start), line_start);
+ return true;
+ }
+
+ /* Skip trailing spaces in key part. */
+ const char* key_end = separator;
+ for (; key_end != line_start; key_end--) {
+ if (key_end[-1] != ' ' && key_end[-1] != '\t') {
+ break;
+ }
+ }
+ /* Skip line if key contains nothing but spaces. */
+ if (key_end == line_start) {
+ cpuinfo_log_warning("Line %.*s in /proc/cpuinfo is ignored: key contains only spaces",
+ (int) (line_end - line_start), line_start);
+ return true;
+ }
+
+ /* Skip leading spaces in value part. */
+ const char* value_start = separator + 1;
+ for (; value_start != line_end; value_start++) {
+ if (*value_start != ' ') {
+ break;
+ }
+ }
+ /* Value part contains nothing but spaces. Skip line. */
+ if (value_start == line_end) {
+ cpuinfo_log_warning("Line %.*s in /proc/cpuinfo is ignored: value contains only spaces",
+ (int) (line_end - line_start), line_start);
+ return true;
+ }
+
+ /* Skip trailing spaces in value part (if any) */
+ const char* value_end = line_end;
+ for (; value_end != value_start; value_end--) {
+ if (value_end[-1] != ' ') {
+ break;
+ }
+ }
+
+ const uint32_t processor_index = state->processor_index;
+ const uint32_t max_processors_count = state->max_processors_count;
+ struct cpuinfo_x86_linux_processor* processors = state->processors;
+ struct cpuinfo_x86_linux_processor* processor = &state->dummy_processor;
+ if (processor_index < max_processors_count) {
+ processor = &processors[processor_index];
+ }
+
+ const size_t key_length = key_end - line_start;
+ switch (key_length) {
+ case 6:
+ if (memcmp(line_start, "apicid", key_length) == 0) {
+ parse_apic_id(value_start, value_end, processor);
+ } else {
+ goto unknown;
+ }
+ break;
+ case 9:
+ if (memcmp(line_start, "processor", key_length) == 0) {
+ const uint32_t new_processor_index = parse_processor_number(value_start, value_end);
+ if (new_processor_index < processor_index) {
+ /* Strange: decreasing processor number */
+ cpuinfo_log_warning(
+ "unexpectedly low processor number %"PRIu32" following processor %"PRIu32" in /proc/cpuinfo",
+ new_processor_index, processor_index);
+ } else if (new_processor_index > processor_index + 1) {
+ /* Strange, but common: skipped processor $(processor_index + 1) */
+ cpuinfo_log_info(
+ "unexpectedly high processor number %"PRIu32" following processor %"PRIu32" in /proc/cpuinfo",
+ new_processor_index, processor_index);
+ }
+ if (new_processor_index >= max_processors_count) {
+ /* Log and ignore processor */
+ cpuinfo_log_warning("processor %"PRIu32" in /proc/cpuinfo is ignored: index exceeds system limit %"PRIu32,
+ new_processor_index, max_processors_count - 1);
+ }
+ state->processor_index = new_processor_index;
+ return true;
+ } else {
+ goto unknown;
+ }
+ break;
+ default:
+ unknown:
+ cpuinfo_log_debug("unknown /proc/cpuinfo key: %.*s", (int) key_length, line_start);
+
+ }
+ return true;
+}
+
+bool cpuinfo_x86_linux_parse_proc_cpuinfo(
+ uint32_t max_processors_count,
+ struct cpuinfo_x86_linux_processor processors[restrict static max_processors_count])
+{
+ struct proc_cpuinfo_parser_state state = {
+ .processor_index = 0,
+ .max_processors_count = max_processors_count,
+ .processors = processors,
+ };
+ return cpuinfo_linux_parse_multiline_file("/proc/cpuinfo", BUFFER_SIZE,
+ (cpuinfo_line_callback) parse_line, &state);
+}
diff --git a/src/x86/linux/init.c b/src/x86/linux/init.c
index b412bf4..822dea8 100644
--- a/src/x86/linux/init.c
+++ b/src/x86/linux/init.c
@@ -2,12 +2,10 @@
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
-#include <errno.h>
-
-#include <sched.h>
#include <cpuinfo.h>
#include <x86/api.h>
+#include <x86/linux/api.h>
#include <gpu/api.h>
#include <linux/api.h>
#include <api.h>
@@ -18,20 +16,39 @@
return (UINT32_C(1) << bits) - UINT32_C(1);
}
-static int cmp_x86_processor_by_apic_id(const void* processor_a, const void* processor_b) {
- const uint32_t id_a = ((const struct cpuinfo_x86_processor*) processor_a)->topology.apic_id;
- const uint32_t id_b = ((const struct cpuinfo_x86_processor*) processor_b)->topology.apic_id;
+static inline bool bitmask_all(uint32_t bitfield, uint32_t mask) {
+ return (bitfield & mask) == mask;
+}
- if (id_a < id_b) {
- return -1;
- } else {
- return id_a > id_b;
+static inline uint32_t min(uint32_t a, uint32_t b) {
+ return a < b ? a : b;
+}
+
+static inline int cmp(uint32_t a, uint32_t b) {
+ return (a > b) - (a < b);
+}
+
+static int cmp_x86_linux_processor(const void* ptr_a, const void* ptr_b) {
+ const struct cpuinfo_x86_linux_processor* processor_a = (const struct cpuinfo_x86_linux_processor*) ptr_a;
+ const struct cpuinfo_x86_linux_processor* processor_b = (const struct cpuinfo_x86_linux_processor*) ptr_b;
+
+ /* Move usable processors towards the start of the array */
+ const bool usable_a = bitmask_all(processor_a->flags, CPUINFO_LINUX_MASK_USABLE);
+ const bool usable_b = bitmask_all(processor_b->flags, CPUINFO_LINUX_MASK_USABLE);
+ if (usable_a != usable_b) {
+ return (int) usable_b - (int) usable_a;
}
+
+ /* Compare based on APIC ID (i.e. processor 0 < processor 1) */
+ const uint32_t id_a = processor_a->apic_id;
+ const uint32_t id_b = processor_b->apic_id;
+ return cmp(id_a, id_b);
}
static void cpuinfo_x86_count_objects(
- const struct cpuinfo_x86_processor* processors,
- uint32_t processors_count,
+ uint32_t linux_processors_count,
+ const struct cpuinfo_x86_linux_processor linux_processors[restrict static linux_processors_count],
+ const struct cpuinfo_x86_processor processor[restrict static 1],
uint32_t cores_count_ptr[restrict static 1],
uint32_t packages_count_ptr[restrict static 1],
uint32_t l1i_count_ptr[restrict static 1],
@@ -45,55 +62,59 @@
uint32_t last_core_id = UINT32_MAX, last_package_id = UINT32_MAX;
uint32_t last_l1i_id = UINT32_MAX, last_l1d_id = UINT32_MAX;
uint32_t last_l2_id = UINT32_MAX, last_l3_id = UINT32_MAX, last_l4_id = UINT32_MAX;
- for (uint32_t i = 0; i < processors_count; i++) {
- const uint32_t apic_id = processors[i].topology.apic_id;
- /* All bits of APIC ID except thread ID mask */
- const uint32_t core_id = apic_id &
- ~(bit_mask(processors[i].topology.thread_bits_length) << processors[i].topology.thread_bits_offset);
- if (core_id != last_core_id) {
- last_core_id = core_id;
- cores_count++;
- }
- /* All bits of APIC ID except thread ID and core ID masks */
- const uint32_t package_id = core_id &
- ~(bit_mask(processors[i].topology.core_bits_length) << processors[i].topology.core_bits_offset);
- if (package_id != last_package_id) {
- last_package_id = package_id;
- packages_count++;
- }
- if (processors[i].cache.l1i.size != 0) {
- const uint32_t l1i_id = apic_id & ~bit_mask(processors[i].cache.l1i.apic_bits);
- if (l1i_id != last_l1i_id) {
- last_l1i_id = l1i_id;
- l1i_count++;
+ for (uint32_t i = 0; i < linux_processors_count; i++) {
+ if (bitmask_all(linux_processors[i].flags, CPUINFO_LINUX_MASK_USABLE)) {
+ const uint32_t apic_id = linux_processors[i].apic_id;
+ cpuinfo_log_debug("APID ID %"PRIu32": system processor %"PRIu32, apic_id, linux_processors[i].linux_id);
+
+ /* All bits of APIC ID except thread ID mask */
+ const uint32_t core_id = apic_id &
+ ~(bit_mask(processor->topology.thread_bits_length) << processor->topology.thread_bits_offset);
+ if (core_id != last_core_id) {
+ last_core_id = core_id;
+ cores_count++;
}
- }
- if (processors[i].cache.l1d.size != 0) {
- const uint32_t l1d_id = apic_id & ~bit_mask(processors[i].cache.l1d.apic_bits);
- if (l1d_id != last_l1d_id) {
- last_l1d_id = l1d_id;
- l1d_count++;
+ /* All bits of APIC ID except thread ID and core ID masks */
+ const uint32_t package_id = core_id &
+ ~(bit_mask(processor->topology.core_bits_length) << processor->topology.core_bits_offset);
+ if (package_id != last_package_id) {
+ last_package_id = package_id;
+ packages_count++;
}
- }
- if (processors[i].cache.l2.size != 0) {
- const uint32_t l2_id = apic_id & ~bit_mask(processors[i].cache.l2.apic_bits);
- if (l2_id != last_l2_id) {
- last_l2_id = l2_id;
- l2_count++;
+ if (processor->cache.l1i.size != 0) {
+ const uint32_t l1i_id = apic_id & ~bit_mask(processor->cache.l1i.apic_bits);
+ if (l1i_id != last_l1i_id) {
+ last_l1i_id = l1i_id;
+ l1i_count++;
+ }
}
- }
- if (processors[i].cache.l3.size != 0) {
- const uint32_t l3_id = apic_id & ~bit_mask(processors[i].cache.l3.apic_bits);
- if (l3_id != last_l3_id) {
- last_l3_id = l3_id;
- l3_count++;
+ if (processor->cache.l1d.size != 0) {
+ const uint32_t l1d_id = apic_id & ~bit_mask(processor->cache.l1d.apic_bits);
+ if (l1d_id != last_l1d_id) {
+ last_l1d_id = l1d_id;
+ l1d_count++;
+ }
}
- }
- if (processors[i].cache.l4.size != 0) {
- const uint32_t l4_id = apic_id & ~bit_mask(processors[i].cache.l4.apic_bits);
- if (l4_id != last_l4_id) {
- last_l4_id = l4_id;
- l4_count++;
+ if (processor->cache.l2.size != 0) {
+ const uint32_t l2_id = apic_id & ~bit_mask(processor->cache.l2.apic_bits);
+ if (l2_id != last_l2_id) {
+ last_l2_id = l2_id;
+ l2_count++;
+ }
+ }
+ if (processor->cache.l3.size != 0) {
+ const uint32_t l3_id = apic_id & ~bit_mask(processor->cache.l3.apic_bits);
+ if (l3_id != last_l3_id) {
+ last_l3_id = l3_id;
+ l3_count++;
+ }
+ }
+ if (processor->cache.l4.size != 0) {
+ const uint32_t l4_id = apic_id & ~bit_mask(processor->cache.l4.apic_bits);
+ if (l4_id != last_l4_id) {
+ last_l4_id = l4_id;
+ l4_count++;
+ }
}
}
}
@@ -106,88 +127,83 @@
*l4_count_ptr = l4_count;
}
-static bool cpuinfo_x86_linux_cpulist_callback(uint32_t cpulist_start, uint32_t cpulist_end, void* context) {
- cpu_set_t* cpuset = (cpu_set_t*) context;
- for (uint32_t cpu = cpulist_start; cpu < cpulist_end; cpu++) {
- CPU_SET((int) cpu, cpuset);
- }
- return true;
-}
-
void cpuinfo_x86_linux_init(void) {
+ struct cpuinfo_x86_linux_processor* x86_linux_processors = NULL;
+ struct cpuinfo_processor* processors = NULL;
+ struct cpuinfo_core* cores = NULL;
+ struct cpuinfo_package* packages = NULL;
const struct cpuinfo_processor** linux_cpu_to_processor_map = NULL;
const struct cpuinfo_core** linux_cpu_to_core_map = NULL;
- struct cpuinfo_x86_processor* x86_processors = NULL;
- struct cpuinfo_processor* processors = NULL;
- struct cpuinfo_package* packages = NULL;
- struct cpuinfo_core* cores = NULL;
struct cpuinfo_cache* l1i = NULL;
struct cpuinfo_cache* l1d = NULL;
struct cpuinfo_cache* l2 = NULL;
struct cpuinfo_cache* l3 = NULL;
struct cpuinfo_cache* l4 = NULL;
- cpu_set_t old_affinity;
- if (sched_getaffinity(0, sizeof(cpu_set_t), &old_affinity) != 0) {
- cpuinfo_log_error("sched_getaffinity failed: %s", strerror(errno));
+ const uint32_t max_processors_count = cpuinfo_linux_get_max_processors_count();
+ cpuinfo_log_debug("system maximum processors count: %"PRIu32, max_processors_count);
+
+ const uint32_t max_possible_processors_count = 1 +
+ cpuinfo_linux_get_max_possible_processor(max_processors_count);
+ cpuinfo_log_debug("maximum possible processors count: %"PRIu32, max_possible_processors_count);
+ const uint32_t max_present_processors_count = 1 +
+ cpuinfo_linux_get_max_possible_processor(max_processors_count);
+ cpuinfo_log_debug("maximum present processors count: %"PRIu32, max_present_processors_count);
+
+ const uint32_t x86_linux_processors_count = min(max_possible_processors_count, max_present_processors_count);
+ x86_linux_processors = calloc(x86_linux_processors_count, sizeof(struct cpuinfo_x86_linux_processor));
+ if (x86_linux_processors == NULL) {
+ cpuinfo_log_error(
+ "failed to allocate %zu bytes for descriptions of %"PRIu32" x86 logical processors",
+ x86_linux_processors_count * sizeof(struct cpuinfo_x86_linux_processor),
+ x86_linux_processors_count);
return;
}
- cpu_set_t present_set;
- CPU_ZERO(&present_set);
- cpuinfo_linux_parse_cpulist("/sys/devices/system/cpu/present", cpuinfo_x86_linux_cpulist_callback, &present_set);
+ cpuinfo_linux_detect_possible_processors(
+ x86_linux_processors_count, &x86_linux_processors->flags,
+ sizeof(struct cpuinfo_x86_linux_processor),
+ CPUINFO_LINUX_FLAG_POSSIBLE);
- cpu_set_t possible_set;
- CPU_ZERO(&possible_set);
- cpuinfo_linux_parse_cpulist("/sys/devices/system/cpu/possible", cpuinfo_x86_linux_cpulist_callback, &possible_set);
+ cpuinfo_linux_detect_present_processors(
+ x86_linux_processors_count, &x86_linux_processors->flags,
+ sizeof(struct cpuinfo_x86_linux_processor),
+ CPUINFO_LINUX_FLAG_PRESENT);
- cpu_set_t processors_set;
- CPU_AND(&processors_set, &present_set, &possible_set);
- const uint32_t processors_count = (uint32_t) CPU_COUNT(&processors_set);
- cpuinfo_log_debug("detected %"PRIu32" logical processors", processors_count);
-
- x86_processors = calloc(processors_count, sizeof(struct cpuinfo_x86_processor));
- if (x86_processors == NULL) {
- cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %d x86 logical processors",
- processors_count * sizeof(struct cpuinfo_x86_processor), processors_count);
- goto cleanup;
+ if (!cpuinfo_x86_linux_parse_proc_cpuinfo(x86_linux_processors_count, x86_linux_processors)) {
+ cpuinfo_log_error("failed to parse processor information from /proc/cpuinfo");
+ return;
}
- int processor_bit = 0;
- for (int i = 0; i < processors_count; i++, processor_bit++) {
- while (!CPU_ISSET(processor_bit, &processors_set)) {
- processor_bit++;
- }
- cpu_set_t processor_set;
- CPU_ZERO(&processor_set);
- CPU_SET(processor_bit, &processor_set);
- if (sched_setaffinity(0, sizeof(cpu_set_t), &processor_set) != 0) {
- cpuinfo_log_error("sched_setaffinity for processor %d (bit %d) failed: %s",
- i, processor_bit, strerror(errno));
- goto cleanup;
- }
+ struct cpuinfo_x86_processor x86_processor;
+ memset(&x86_processor, 0, sizeof(x86_processor));
+ cpuinfo_x86_init_processor(&x86_processor);
- cpuinfo_x86_init_processor(&x86_processors[i]);
- x86_processors[i].linux_id = processor_bit;
+ uint32_t processors_count = 0;
+ for (uint32_t i = 0; i < x86_linux_processors_count; i++) {
+ if (bitmask_all(x86_linux_processors[i].flags, CPUINFO_LINUX_MASK_USABLE)) {
+ x86_linux_processors[i].linux_id = i;
+ processors_count++;
+ }
}
- qsort(x86_processors, (size_t) processors_count, sizeof(struct cpuinfo_x86_processor),
- cmp_x86_processor_by_apic_id);
+ qsort(x86_linux_processors, x86_linux_processors_count, sizeof(struct cpuinfo_x86_linux_processor),
+ cmp_x86_linux_processor);
- processors = calloc((size_t) processors_count, sizeof(struct cpuinfo_processor));
+ processors = calloc(processors_count, sizeof(struct cpuinfo_processor));
if (processors == NULL) {
- cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %d logical processors",
- (size_t) processors_count * sizeof(struct cpuinfo_processor), processors_count);
+ cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" logical processors",
+ processors_count * sizeof(struct cpuinfo_processor), processors_count);
goto cleanup;
}
uint32_t packages_count = 0, cores_count = 0;
uint32_t l1i_count = 0, l1d_count = 0, l2_count = 0, l3_count = 0, l4_count = 0;
- cpuinfo_x86_count_objects(x86_processors, processors_count,
+ cpuinfo_x86_count_objects(x86_linux_processors_count, x86_linux_processors, &x86_processor,
&cores_count, &packages_count, &l1i_count, &l1d_count, &l2_count, &l3_count, &l4_count);
- cpuinfo_log_debug("detected %"PRIu32" cores caches", cores_count);
- cpuinfo_log_debug("detected %"PRIu32" packages caches", packages_count);
+ cpuinfo_log_debug("detected %"PRIu32" cores", cores_count);
+ cpuinfo_log_debug("detected %"PRIu32" packages", packages_count);
cpuinfo_log_debug("detected %"PRIu32" L1I caches", l1i_count);
cpuinfo_log_debug("detected %"PRIu32" L1D caches", l1d_count);
cpuinfo_log_debug("detected %"PRIu32" L2 caches", l2_count);
@@ -261,192 +277,199 @@
}
}
- uint32_t core_index = UINT32_MAX, package_index = UINT32_MAX;
+ uint32_t processor_index = UINT32_MAX, core_index = UINT32_MAX, package_index = UINT32_MAX;
uint32_t l1i_index = UINT32_MAX, l1d_index = UINT32_MAX, l2_index = UINT32_MAX, l3_index = UINT32_MAX, l4_index = UINT32_MAX;
- uint32_t last_core_id = UINT32_MAX, last_package_id = UINT32_MAX;
+ uint32_t core_id = 0, smt_id = 0;
+ uint32_t last_apic_core_id = UINT32_MAX, last_apic_package_id = UINT32_MAX;
uint32_t last_l1i_id = UINT32_MAX, last_l1d_id = UINT32_MAX;
uint32_t last_l2_id = UINT32_MAX, last_l3_id = UINT32_MAX, last_l4_id = UINT32_MAX;
- for (uint32_t i = 0; i < processors_count; i++) {
- const uint32_t apic_id = x86_processors[i].topology.apic_id;
+ for (uint32_t i = 0; i < x86_linux_processors_count; i++) {
+ if (bitmask_all(x86_linux_processors[i].flags, CPUINFO_LINUX_MASK_USABLE)) {
+ const uint32_t apic_id = x86_linux_processors[i].apic_id;
+ processor_index++;
+ smt_id++;
- /* All bits of APIC ID except thread ID mask */
- const uint32_t core_id = apic_id &
- ~(bit_mask(x86_processors[i].topology.thread_bits_length) << x86_processors[i].topology.thread_bits_offset);
- if (core_id != last_core_id) {
- core_index++;
- }
- /* All bits of APIC ID except thread ID and core ID masks */
- const uint32_t package_id = core_id &
- ~(bit_mask(x86_processors[i].topology.core_bits_length) << x86_processors[i].topology.core_bits_offset);
- if (package_id != last_package_id) {
- package_index++;
- }
-
- /* Initialize topology information */
- const uint32_t thread_mask = bit_mask(x86_processors[i].topology.thread_bits_length);
- processors[i].smt_id = (apic_id >> x86_processors[i].topology.thread_bits_offset) & thread_mask;
- processors[i].core = cores + core_index;
- processors[i].package = packages + package_index;
- processors[i].linux_id = x86_processors[i].linux_id;
- processors[i].apic_id = x86_processors[i].topology.apic_id;
-
- if (core_id != last_core_id) {
- /* new core */
- cores[core_index] = (struct cpuinfo_core) {
- .processor_start = i,
- .processor_count = 1,
- .core_id = core_id,
- .package = packages + package_index,
- .vendor = x86_processors[i].vendor,
- .uarch = x86_processors[i].uarch,
- .cpuid = x86_processors[i].model_info.cpuid,
- };
- packages[package_index].core_count += 1;
- last_core_id = core_id;
- } else {
- /* another logical processor on the same core */
- cores[core_index].processor_count++;
- }
-
- if (package_id != last_package_id) {
- /* new package */
- packages[package_index].processor_start = i;
- packages[package_index].processor_count = 1;
- packages[package_index].core_start = core_index;
- cpuinfo_x86_normalize_brand_string(x86_processors[i].brand_string, packages[package_index].name);
- last_package_id = package_id;
- } else {
- /* another logical processor on the same package */
- packages[package_index].processor_count++;
- }
-
- linux_cpu_to_processor_map[x86_processors[i].linux_id] = &processors[i];
- linux_cpu_to_core_map[x86_processors[i].linux_id] = &cores[core_index];
-
- if (x86_processors[i].cache.l1i.size != 0) {
- const uint32_t l1i_id = apic_id & ~bit_mask(x86_processors[i].cache.l1i.apic_bits);
- processors[i].cache.l1i = &l1i[l1i_index];
- if (l1i_id != last_l1i_id) {
- /* new cache */
- last_l1i_id = l1i_id;
- l1i[++l1i_index] = (struct cpuinfo_cache) {
- .size = x86_processors[i].cache.l1i.size,
- .associativity = x86_processors[i].cache.l1i.associativity,
- .sets = x86_processors[i].cache.l1i.sets,
- .partitions = x86_processors[i].cache.l1i.partitions,
- .line_size = x86_processors[i].cache.l1i.line_size,
- .flags = x86_processors[i].cache.l1i.flags,
- .processor_start = i,
- .processor_count = 1,
- };
- } else {
- /* another processor sharing the same cache */
- l1i[l1i_index].processor_count += 1;
+ /* All bits of APIC ID except thread ID mask */
+ const uint32_t apid_core_id = apic_id &
+ ~(bit_mask(x86_processor.topology.thread_bits_length) << x86_processor.topology.thread_bits_offset);
+ if (apid_core_id != last_apic_core_id) {
+ core_index++;
+ core_id++;
+ smt_id = 0;
}
- processors[i].cache.l1i = &l1i[l1i_index];
- } else {
- /* reset cache id */
- last_l1i_id = UINT32_MAX;
- }
- if (x86_processors[i].cache.l1d.size != 0) {
- const uint32_t l1d_id = apic_id & ~bit_mask(x86_processors[i].cache.l1d.apic_bits);
- processors[i].cache.l1d = &l1d[l1d_index];
- if (l1d_id != last_l1d_id) {
- /* new cache */
- last_l1d_id = l1d_id;
- l1d[++l1d_index] = (struct cpuinfo_cache) {
- .size = x86_processors[i].cache.l1d.size,
- .associativity = x86_processors[i].cache.l1d.associativity,
- .sets = x86_processors[i].cache.l1d.sets,
- .partitions = x86_processors[i].cache.l1d.partitions,
- .line_size = x86_processors[i].cache.l1d.line_size,
- .flags = x86_processors[i].cache.l1d.flags,
- .processor_start = i,
- .processor_count = 1,
- };
- } else {
- /* another processor sharing the same cache */
- l1d[l1d_index].processor_count += 1;
+ /* All bits of APIC ID except thread ID and core ID masks */
+ const uint32_t apic_package_id = apid_core_id &
+ ~(bit_mask(x86_processor.topology.core_bits_length) << x86_processor.topology.core_bits_offset);
+ if (apic_package_id != last_apic_package_id) {
+ package_index++;
+ core_id = 0;
}
- processors[i].cache.l1d = &l1d[l1d_index];
- } else {
- /* reset cache id */
- last_l1d_id = UINT32_MAX;
- }
- if (x86_processors[i].cache.l2.size != 0) {
- const uint32_t l2_id = apic_id & ~bit_mask(x86_processors[i].cache.l2.apic_bits);
- processors[i].cache.l2 = &l2[l2_index];
- if (l2_id != last_l2_id) {
- /* new cache */
- last_l2_id = l2_id;
- l2[++l2_index] = (struct cpuinfo_cache) {
- .size = x86_processors[i].cache.l2.size,
- .associativity = x86_processors[i].cache.l2.associativity,
- .sets = x86_processors[i].cache.l2.sets,
- .partitions = x86_processors[i].cache.l2.partitions,
- .line_size = x86_processors[i].cache.l2.line_size,
- .flags = x86_processors[i].cache.l2.flags,
- .processor_start = i,
+
+ /* Initialize logical processor object */
+ processors[processor_index].smt_id = smt_id;
+ processors[processor_index].core = cores + core_index;
+ processors[processor_index].package = packages + package_index;
+ processors[processor_index].linux_id = x86_linux_processors[i].linux_id;
+ processors[processor_index].apic_id = x86_linux_processors[i].apic_id;
+
+ if (apid_core_id != last_apic_core_id) {
+ /* new core */
+ cores[core_index] = (struct cpuinfo_core) {
+ .processor_start = processor_index,
.processor_count = 1,
+ .core_id = core_id,
+ .package = packages + package_index,
+ .vendor = x86_processor.vendor,
+ .uarch = x86_processor.uarch,
+ .cpuid = x86_processor.cpuid,
};
+ packages[package_index].core_count += 1;
+ last_apic_core_id = apid_core_id;
} else {
- /* another processor sharing the same cache */
- l2[l2_index].processor_count += 1;
+ /* another logical processor on the same core */
+ cores[core_index].processor_count++;
}
- processors[i].cache.l2 = &l2[l2_index];
- } else {
- /* reset cache id */
- last_l2_id = UINT32_MAX;
- }
- if (x86_processors[i].cache.l3.size != 0) {
- const uint32_t l3_id = apic_id & ~bit_mask(x86_processors[i].cache.l3.apic_bits);
- processors[i].cache.l3 = &l3[l3_index];
- if (l3_id != last_l3_id) {
- /* new cache */
- last_l3_id = l3_id;
- l3[++l3_index] = (struct cpuinfo_cache) {
- .size = x86_processors[i].cache.l3.size,
- .associativity = x86_processors[i].cache.l3.associativity,
- .sets = x86_processors[i].cache.l3.sets,
- .partitions = x86_processors[i].cache.l3.partitions,
- .line_size = x86_processors[i].cache.l3.line_size,
- .flags = x86_processors[i].cache.l3.flags,
- .processor_start = i,
- .processor_count = 1,
- };
+
+ if (apic_package_id != last_apic_package_id) {
+ /* new package */
+ packages[package_index].processor_start = processor_index;
+ packages[package_index].processor_count = 1;
+ packages[package_index].core_start = core_index;
+ cpuinfo_x86_normalize_brand_string(x86_processor.brand_string, packages[package_index].name);
+ last_apic_package_id = apic_package_id;
} else {
- /* another processor sharing the same cache */
- l3[l3_index].processor_count += 1;
+ /* another logical processor on the same package */
+ packages[package_index].processor_count++;
}
- processors[i].cache.l3 = &l3[l3_index];
- } else {
- /* reset cache id */
- last_l3_id = UINT32_MAX;
- }
- if (x86_processors[i].cache.l4.size != 0) {
- const uint32_t l4_id = apic_id & ~bit_mask(x86_processors[i].cache.l4.apic_bits);
- processors[i].cache.l4 = &l4[l4_index];
- if (l4_id != last_l4_id) {
- /* new cache */
- last_l4_id = l4_id;
- l4[++l4_index] = (struct cpuinfo_cache) {
- .size = x86_processors[i].cache.l4.size,
- .associativity = x86_processors[i].cache.l4.associativity,
- .sets = x86_processors[i].cache.l4.sets,
- .partitions = x86_processors[i].cache.l4.partitions,
- .line_size = x86_processors[i].cache.l4.line_size,
- .flags = x86_processors[i].cache.l4.flags,
- .processor_start = i,
- .processor_count = 1,
- };
+
+ linux_cpu_to_processor_map[x86_linux_processors[i].linux_id] = processors + processor_index;
+ linux_cpu_to_core_map[x86_linux_processors[i].linux_id] = cores + core_index;
+
+ if (x86_processor.cache.l1i.size != 0) {
+ const uint32_t l1i_id = apic_id & ~bit_mask(x86_processor.cache.l1i.apic_bits);
+ processors[i].cache.l1i = &l1i[l1i_index];
+ if (l1i_id != last_l1i_id) {
+ /* new cache */
+ last_l1i_id = l1i_id;
+ l1i[++l1i_index] = (struct cpuinfo_cache) {
+ .size = x86_processor.cache.l1i.size,
+ .associativity = x86_processor.cache.l1i.associativity,
+ .sets = x86_processor.cache.l1i.sets,
+ .partitions = x86_processor.cache.l1i.partitions,
+ .line_size = x86_processor.cache.l1i.line_size,
+ .flags = x86_processor.cache.l1i.flags,
+ .processor_start = processor_index,
+ .processor_count = 1,
+ };
+ } else {
+ /* another processor sharing the same cache */
+ l1i[l1i_index].processor_count += 1;
+ }
+ processors[i].cache.l1i = &l1i[l1i_index];
} else {
- /* another processor sharing the same cache */
- l4[l4_index].processor_count += 1;
+ /* reset cache id */
+ last_l1i_id = UINT32_MAX;
}
- processors[i].cache.l4 = &l4[l4_index];
- } else {
- /* reset cache id */
- last_l4_id = UINT32_MAX;
+ if (x86_processor.cache.l1d.size != 0) {
+ const uint32_t l1d_id = apic_id & ~bit_mask(x86_processor.cache.l1d.apic_bits);
+ processors[i].cache.l1d = &l1d[l1d_index];
+ if (l1d_id != last_l1d_id) {
+ /* new cache */
+ last_l1d_id = l1d_id;
+ l1d[++l1d_index] = (struct cpuinfo_cache) {
+ .size = x86_processor.cache.l1d.size,
+ .associativity = x86_processor.cache.l1d.associativity,
+ .sets = x86_processor.cache.l1d.sets,
+ .partitions = x86_processor.cache.l1d.partitions,
+ .line_size = x86_processor.cache.l1d.line_size,
+ .flags = x86_processor.cache.l1d.flags,
+ .processor_start = processor_index,
+ .processor_count = 1,
+ };
+ } else {
+ /* another processor sharing the same cache */
+ l1d[l1d_index].processor_count += 1;
+ }
+ processors[i].cache.l1d = &l1d[l1d_index];
+ } else {
+ /* reset cache id */
+ last_l1d_id = UINT32_MAX;
+ }
+ if (x86_processor.cache.l2.size != 0) {
+ const uint32_t l2_id = apic_id & ~bit_mask(x86_processor.cache.l2.apic_bits);
+ processors[i].cache.l2 = &l2[l2_index];
+ if (l2_id != last_l2_id) {
+ /* new cache */
+ last_l2_id = l2_id;
+ l2[++l2_index] = (struct cpuinfo_cache) {
+ .size = x86_processor.cache.l2.size,
+ .associativity = x86_processor.cache.l2.associativity,
+ .sets = x86_processor.cache.l2.sets,
+ .partitions = x86_processor.cache.l2.partitions,
+ .line_size = x86_processor.cache.l2.line_size,
+ .flags = x86_processor.cache.l2.flags,
+ .processor_start = processor_index,
+ .processor_count = 1,
+ };
+ } else {
+ /* another processor sharing the same cache */
+ l2[l2_index].processor_count += 1;
+ }
+ processors[i].cache.l2 = &l2[l2_index];
+ } else {
+ /* reset cache id */
+ last_l2_id = UINT32_MAX;
+ }
+ if (x86_processor.cache.l3.size != 0) {
+ const uint32_t l3_id = apic_id & ~bit_mask(x86_processor.cache.l3.apic_bits);
+ processors[i].cache.l3 = &l3[l3_index];
+ if (l3_id != last_l3_id) {
+ /* new cache */
+ last_l3_id = l3_id;
+ l3[++l3_index] = (struct cpuinfo_cache) {
+ .size = x86_processor.cache.l3.size,
+ .associativity = x86_processor.cache.l3.associativity,
+ .sets = x86_processor.cache.l3.sets,
+ .partitions = x86_processor.cache.l3.partitions,
+ .line_size = x86_processor.cache.l3.line_size,
+ .flags = x86_processor.cache.l3.flags,
+ .processor_start = processor_index,
+ .processor_count = 1,
+ };
+ } else {
+ /* another processor sharing the same cache */
+ l3[l3_index].processor_count += 1;
+ }
+ processors[i].cache.l3 = &l3[l3_index];
+ } else {
+ /* reset cache id */
+ last_l3_id = UINT32_MAX;
+ }
+ if (x86_processor.cache.l4.size != 0) {
+ const uint32_t l4_id = apic_id & ~bit_mask(x86_processor.cache.l4.apic_bits);
+ processors[i].cache.l4 = &l4[l4_index];
+ if (l4_id != last_l4_id) {
+ /* new cache */
+ last_l4_id = l4_id;
+ l4[++l4_index] = (struct cpuinfo_cache) {
+ .size = x86_processor.cache.l4.size,
+ .associativity = x86_processor.cache.l4.associativity,
+ .sets = x86_processor.cache.l4.sets,
+ .partitions = x86_processor.cache.l4.partitions,
+ .line_size = x86_processor.cache.l4.line_size,
+ .flags = x86_processor.cache.l4.flags,
+ .processor_start = processor_index,
+ .processor_count = 1,
+ };
+ } else {
+ /* another processor sharing the same cache */
+ l4[l4_index].processor_count += 1;
+ }
+ processors[i].cache.l4 = &l4[l4_index];
+ } else {
+ /* reset cache id */
+ last_l4_id = UINT32_MAX;
+ }
}
}
@@ -484,14 +507,9 @@
l1i = l1d = l2 = l3 = l4 = NULL;
cleanup:
- if (sched_setaffinity(0, sizeof(cpu_set_t), &old_affinity) != 0) {
- cpuinfo_log_warning("could not restore initial process affinity: "
- "sched_getaffinity failed: %s", strerror(errno));
- }
-
free(linux_cpu_to_processor_map);
free(linux_cpu_to_core_map);
- free(x86_processors);
+ free(x86_linux_processors);
free(processors);
free(cores);
free(packages);