| 1 | /* @title: Topology */ |
| 2 | #pragma once |
| 3 | #include <stdatomic.h> |
| 4 | #include <stdbool.h> |
| 5 | #include <stddef.h> |
| 6 | #include <stdint.h> |
| 7 | #include <types/types.h> |
| 8 | |
| 9 | enum topology_level { |
| 10 | TOPOLOGY_LEVEL_SMT, /* Symmetric multiprocessing threads */ |
| 11 | TOPOLOGY_LEVEL_CORE, /* SMTs under a core */ |
| 12 | TOPOLOGY_LEVEL_NUMA, /* NUMA node */ |
| 13 | TOPOLOGY_LEVEL_LLC, /* Last level cache (some processors |
| 14 | * have multiple L3 caches for a given |
| 15 | * physical processor) */ |
| 16 | TOPOLOGY_LEVEL_PACKAGE, /* Physical processor in a socket */ |
| 17 | TOPOLOGY_LEVEL_MACHINE, /* All processors in a machine */ |
| 18 | TOPOLOGY_LEVEL_MAX, /* count */ |
| 19 | TOPOLOGY_LEVEL_COUNT = TOPOLOGY_LEVEL_MAX |
| 20 | }; |
| 21 | |
| 22 | struct cpu_mask { |
| 23 | bool uses_large; |
| 24 | union { |
| 25 | _Atomic uint64_t small; |
| 26 | _Atomic uint64_t *large; |
| 27 | }; |
| 28 | size_t nbits; |
| 29 | }; |
| 30 | #define CPU_MASK_WORD_BITS 64 |
| 31 | #define CPU_MASK_WORDS(nbits) \ |
| 32 | (((nbits) + CPU_MASK_WORD_BITS - 1) / CPU_MASK_WORD_BITS) |
| 33 | |
| 34 | struct topology_cache_info { |
| 35 | uint8_t level; /* 1, 2, 3 */ |
| 36 | uint8_t type; /* Data, unified, instruction */ |
| 37 | uint32_t size_kb; |
| 38 | uint32_t line_size; |
| 39 | uint32_t cores_sharing; /* Who shares this */ |
| 40 | }; |
| 41 | |
| 42 | struct topology_package_info { |
| 43 | uint32_t package_id; |
| 44 | struct cpu_mask cores; |
| 45 | }; |
| 46 | |
| 47 | struct topology_node { |
| 48 | enum topology_level level; |
| 49 | uint64_t id; /* Index in this node */ |
| 50 | uint64_t parent; /* Parent node index, -1 for root */ |
| 51 | struct topology_node *parent_node; |
| 52 | int32_t first_child; /* For cores this is in the cores array. |
| 53 | * For NUMA this is also in the cores array. |
| 54 | * For LLC this is in the numa array. |
| 55 | * For package this is LLC. |
| 56 | * For machine this is package. */ |
| 57 | int32_t nr_children; |
| 58 | |
| 59 | struct cpu_mask cpus; |
| 60 | struct cpu_mask idle; |
| 61 | struct cpu_mask rt_shed_rq_active; /* For RT scheduler */ |
| 62 | |
| 63 | struct core *core; /* Pointer to this node's `core` struct */ |
| 64 | |
| 65 | union { |
| 66 | struct numa_node *numa; |
| 67 | struct topology_cache_info *cache; |
| 68 | struct topology_package_info *package; |
| 69 | } data; |
| 70 | }; |
| 71 | |
| 72 | struct topology { |
| 73 | struct topology_node *level[TOPOLOGY_LEVEL_MAX]; |
| 74 | uint16_t count[TOPOLOGY_LEVEL_MAX]; |
| 75 | }; |
| 76 | |
| 77 | /* TODO: move CPUmask into its own header */ |
| 78 | struct cpu_mask *cpu_mask_create(void); |
| 79 | bool cpu_mask_init(struct cpu_mask *m, size_t nbits); |
| 80 | void cpu_mask_set(struct cpu_mask *m, size_t cpu); |
| 81 | void cpu_mask_set_all(struct cpu_mask *m); |
| 82 | void cpu_mask_clear(struct cpu_mask *m, size_t cpu); |
| 83 | bool cpu_mask_test(const struct cpu_mask *m, size_t cpu); |
| 84 | void cpu_mask_or(struct cpu_mask *dst, const struct cpu_mask *b); |
| 85 | bool cpu_mask_empty(const struct cpu_mask *mask); |
| 86 | void cpu_mask_clear_all(struct cpu_mask *m); |
| 87 | size_t cpu_mask_popcount(struct cpu_mask *m); |
| 88 | void cpu_mask_free(struct cpu_mask *m); |
| 89 | void cpu_mask_deinit(struct cpu_mask *m); |
| 90 | #define cpu_mask_for_each(iter, mask) \ |
| 91 | for (iter = 0; iter < (mask).nbits; ++iter) \ |
| 92 | if ((mask).uses_large \ |
| 93 | ? atomic_load(&(mask).large[iter / CPU_MASK_WORD_BITS]) & \ |
| 94 | (1ULL << (iter % CPU_MASK_WORD_BITS)) \ |
| 95 | : atomic_load(&(mask).small) & (1ULL << iter)) |
| 96 | |
| 97 | void topology_mark_core_idle(cpu_id_t cpu_id, bool idle); |
| 98 | struct core *topology_find_idle_core(struct core *local_core, |
| 99 | enum topology_level max_search); |
| 100 | struct core **topology_get_smts_under_numa(struct topology_node *numa, |
| 101 | size_t *count); |
| 102 | const char *topology_level_name(enum topology_level l); |
| 103 | |