1/* @title: Topology */
2#pragma once
3#include <stdbool.h>
4#include <stddef.h>
5#include <stdint.h>
6#include <structures/cpu_mask.h>
7#include <types/types.h>
8
9enum 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
20 TOPOLOGY_LEVEL_EX_DOMAIN, /* This exists solely because the topology_contract
21 * has a domain granularity that does not
22 * necessarily correspond to one of the others,
23 * thus, it is outside of MAX and is its
24 * own layer just for that API */
25};
26
27/* This enum is used to verify topology contracts to guarantee a caller
28 * cannot be migrated out of a given scope of logical processors.
29 *
30 * wrt. naming, we'll suffix all instances of caller contracts with a C.
31 * e.g. SMPC, NODEC, PACKAGEC(specific bits go here)
32 *
33 * TODO: we might need to come up with a naming document in docs/ because
34 * I've been doing this ad-hoc naming scheme stuff a few times
35 */
36enum topology_caller {
37 TOPC_NONE = 0,
38 TOPC_IRQL = 1 << 0,
39
40 TOPC_PINNED = 1 << 1,
41 TOPC_IRQ = 1 << 2,
42 TOPC_IFLAG = 1 << 3,
43
44 /* IRQ, IRQL and IFLAG: IRQ checks in_interrupt, IRQL that >= DISPATCH */
45 TOPC_ANY = TOPC_IRQL | TOPC_PINNED | TOPC_IRQ | TOPC_IFLAG,
46};
47
48/* Contracts essentially state "I will not migrate outside of this scope",
49 * with a set of named, verifiable reasons, so that "self topology" related
50 * functions can prove that they are in a safe state (specified), or
51 * in the edge case, have TOPOC_NONE contracts to uphold */
52struct topology_contract {
53 enum topology_level scope;
54 enum topology_caller caller;
55};
56
57/* TODO: enum this stuff */
58struct topology_cache_info {
59 uint8_t level; /* 1, 2, 3 */
60 uint8_t type; /* Data, unified, instruction */
61 uint32_t size_kb;
62 uint32_t line_size;
63 uint32_t cores_sharing; /* Who shares this */
64};
65
66struct topology_package_info {
67 uint32_t package_id;
68 struct cpu_mask cores;
69};
70
71struct topology_node {
72 enum topology_level level;
73 uint64_t id; /* Index in this node */
74 uint64_t parent; /* Parent node index, -1 for root */
75 struct topology_node *parent_node;
76 int32_t first_child; /* For cores this is in the cores array.
77 * For NUMA this is also in the cores array.
78 * For LLC this is in the numa array.
79 * For package this is LLC.
80 * For machine this is package. */
81 int32_t nr_children;
82
83 struct cpu_mask cpus;
84 struct cpu_mask idle;
85 struct cpu_mask rt_sched_rq_active; /* For RT scheduler */
86
87 struct core *core; /* Pointer to this node's `core` struct */
88
89 union {
90 struct numa_node *numa;
91 struct topology_cache_info *cache;
92 struct topology_package_info *package;
93 } data;
94};
95
96struct topology {
97 struct topology_node *level[TOPOLOGY_LEVEL_MAX];
98 uint16_t count[TOPOLOGY_LEVEL_MAX];
99};
100
101void topology_mark_core_idle(cpu_id_t cpu_id, bool idle);
102struct core *topology_find_idle_core(struct core *local_core,
103 enum topology_level max_search);
104struct core **topology_get_smts_under_numa(struct topology_node *numa,
105 size_t *count);
106const char *topology_level_name(enum topology_level l);
107bool topology_contract_verify(struct topology_contract c);
108
109#define topology_for_each_id(n_, l_) \
110 for ((n_) = 0; (n_) < global.topology.count[l_]; (n_)++)
111