PatchworkOS  69292a3
A non-POSIX operating system.
Loading...
Searching...
No Matches
percpu.c
Go to the documentation of this file.
1#include <kernel/cpu/percpu.h>
2
3#include <kernel/cpu/cpu.h>
4#include <kernel/log/panic.h>
5#include <kernel/mem/vmm.h>
6#include <kernel/sync/lock.h>
7#include <string.h>
8
9#define PERCPU_MAX_SECTIONS 128
10
12
13typedef struct percpu_section
14{
18 bool dying;
20
21/// We cant do memory allocation during early boot so we have to statically allocate the percpu sections.
23static size_t sectionCount = 0;
24
27
28static PERCPU_DEFINE(uint64_t, pcpu_generation);
29static PERCPU_DEFINE(uint64_t, pcpu_ack);
30
32
38
40{
41 size = ROUND_UP(size, PERCPU_ALIGNMENT);
42
44
45 uint64_t offset = bitmap_find_clear_region_and_set(&allocated, 0, allocated.length, size / PERCPU_ALIGNMENT, 1);
46 if (offset == allocated.length)
47 {
48 errno = ENOMEM;
49 return ERR;
50 }
51
52 cpu_t* cpu;
53 CPU_FOR_EACH(cpu)
54 {
55 uintptr_t addr = (uintptr_t)cpu->percpu + (offset * PERCPU_ALIGNMENT);
56 memset((void*)addr, 0, size);
57 assert(cpu->self != NULL);
58 }
59
60 return offsetof(cpu_t, percpu) + (offset * PERCPU_ALIGNMENT);
61}
62
63void percpu_free(percpu_t ptr, size_t size)
64{
65 if (ptr == ERR)
66 {
67 return;
68 }
69 size = ROUND_UP(size, PERCPU_ALIGNMENT);
70
72
73 uint64_t offset = (ptr - offsetof(cpu_t, percpu)) / PERCPU_ALIGNMENT;
74 bitmap_clear_range(&allocated, offset, offset + (size / PERCPU_ALIGNMENT));
75}
76
78{
79 for (percpu_def_t* def = section->start; def < section->end; def++)
80 {
81 if (def->ctor != NULL)
82 {
83 def->ctor();
84 }
85 }
86}
87
89{
90 for (percpu_def_t* def = section->start; def < section->end; def++)
91 {
92 if (def->dtor != NULL)
93 {
94 def->dtor();
95 }
96 }
97}
98
99void percpu_update(void)
100{
102
103 if (*pcpu_generation < globalGeneration)
104 {
105 for (size_t i = 0; i < sectionCount; i++)
106 {
107 percpu_section_t* section = &sections[i];
108 if (section->generation > *pcpu_generation && !section->dying)
109 {
110 percpu_run_ctors(section);
111 }
112 }
113 *pcpu_generation = globalGeneration;
114 }
115
116 if (*pcpu_ack < globalAck)
117 {
118 for (size_t i = 0; i < sectionCount; i++)
119 {
120 percpu_section_t* section = &sections[i];
121 if (section->generation > *pcpu_ack && section->dying)
122 {
123 percpu_run_dtors(section);
124 }
125 }
126 *pcpu_ack = globalAck;
127 }
128}
129
131{
132 for (percpu_def_t* percpu = start; percpu < end; percpu++)
133 {
134 *percpu->ptr = percpu_alloc(percpu->size);
135 if (*percpu->ptr == (percpu_t)ERR)
136 {
137 panic(NULL, "failed to allocate percpu variable");
138 }
139 }
140
143 {
144 panic(NULL, "too many percpu sections");
145 }
146
148 section->start = start;
149 section->end = end;
150 section->dying = false;
151
153 section->generation = globalGeneration;
155
157}
158
160{
161 percpu_section_t* target = NULL;
162
164
165 for (size_t i = 0; i < sectionCount; i++)
166 {
167 percpu_section_t* section = &sections[i];
168 if (section->start == start)
169 {
170 target = section;
171 break;
172 }
173 }
174
175 if (target == NULL)
176 {
178 return;
179 }
180
181 target->dying = true;
182 globalAck++;
184
186
187 bool allAcked = false;
188 while (!allAcked)
189 {
190 allAcked = true;
191
192 cpu_t* cpu;
193 CPU_FOR_EACH(cpu)
194 {
195 uint64_t* cpuAck = CPU_PTR(cpu->id, pcpu_ack);
196 if (*cpuAck < globalAck)
197 {
198 allAcked = false;
199 break;
200 }
201 }
202 if (!allAcked)
203 {
204 ASM("pause");
205 }
206 }
207
209
210 for (size_t i = 0; i < sectionCount; i++)
211 {
212 if (sections[i].start == start)
213 {
214 memmove(&sections[i], &sections[i + 1], (sectionCount - i - 1) * sizeof(percpu_section_t));
215 sectionCount--;
216 break;
217 }
218 }
219
221
222 for (percpu_def_t* percpu = start; percpu < end; percpu++)
223 {
224 percpu_free(*percpu->ptr, percpu->size);
225 *percpu->ptr = 0;
226 }
227}
#define assert(expression)
Definition assert.h:29
static void start()
Definition main.c:542
void percpu_update(void)
Update percpu sections on the current CPU.
Definition percpu.c:99
void percpu_free(percpu_t ptr, size_t size)
Frees a percpu variable.
Definition percpu.c:63
#define PERCPU_DEFINE(type, name,...)
Macro to define a percpu variable.
Definition percpu.h:115
void percpu_init(cpu_t *cpu)
Initialize the percpu system.
Definition percpu.c:33
void percpu_section_deinit(percpu_def_t *start, percpu_def_t *end)
Unregister a percpu section and run destructors.
Definition percpu.c:159
#define PERCPU_ALIGNMENT
The PERCPU_ALIGNMENT constant defines the alignment for per-CPU variables.
Definition percpu.h:52
void percpu_section_init(percpu_def_t *start, percpu_def_t *end)
Register a percpu section and run constructors.
Definition percpu.c:130
percpu_t percpu_alloc(size_t size)
Allocates a percpu variable.
Definition percpu.c:39
#define CPU_PTR(id, ptr)
Macro to get a pointer to a percpu variable on a specific CPU.
Definition percpu.h:102
size_t percpu_t
The type that the compiler uses to store per-CPU variables.
Definition percpu.h:57
#define CPU_FOR_EACH(cpu)
Macro to iterate over all CPUs.
Definition cpu.h:214
NORETURN void panic(const interrupt_frame_t *frame, const char *format,...)
Panic the kernel, printing a message and halting.
Definition panic.c:292
#define LOCK_CREATE()
Create a lock initializer.
Definition lock.h:69
#define LOCK_SCOPE(lock)
Acquires a lock for the reminder of the current scope.
Definition lock.h:58
static void lock_release(lock_t *lock)
Releases a lock.
Definition lock.h:175
static void lock_acquire(lock_t *lock)
Acquires a lock, blocking until it is available.
Definition lock.h:96
#define CONFIG_PERCPU_SIZE
Per-CPU data size configuration.
Definition config.h:182
#define ENOMEM
Out of memory.
Definition errno.h:92
#define errno
Error number variable.
Definition errno.h:27
#define BITMAP_CREATE_ZERO(name, bits)
Define and create a zero-initialized bitmap and its buffer.
Definition bitmap.h:83
static uint64_t bitmap_find_clear_region_and_set(bitmap_t *map, uint64_t minIdx, uintptr_t maxIdx, uint64_t length, uint64_t alignment)
Find a clear region of specified length and alignment, and set it.
Definition bitmap.h:379
static void bitmap_clear_range(bitmap_t *map, uint64_t low, uint64_t high)
Clear a range of bits in the bitmap.
Definition bitmap.h:254
#define ASM(...)
Inline assembly macro.
Definition defs.h:164
#define ROUND_UP(number, multiple)
Definition math.h:21
#define NULL
Pointer error value.
Definition NULL.h:25
#define ERR
Integer error value.
Definition ERR.h:17
static uint64_t offset
Definition screen.c:19
#define PERCPU_MAX_SECTIONS
Definition percpu.c:9
static void percpu_run_ctors(percpu_section_t *section)
Definition percpu.c:77
static lock_t lock
Definition percpu.c:31
static void percpu_run_dtors(percpu_section_t *section)
Definition percpu.c:88
static size_t sectionCount
Definition percpu.c:23
static percpu_section_t sections[PERCPU_MAX_SECTIONS]
We cant do memory allocation during early boot so we have to statically allocate the percpu sections.
Definition percpu.c:22
static uint64_t globalAck
Definition percpu.c:26
static uint64_t globalGeneration
Definition percpu.c:25
static void msr_write(uint32_t msr, uint64_t value)
Definition regs.h:73
#define MSR_GS_BASE
Definition regs.h:20
#define MSR_KERNEL_GS_BASE
Definition regs.h:21
#define offsetof(type, member)
Definition stddef.h:16
__UINT64_TYPE__ uint64_t
Definition stdint.h:17
__UINTPTR_TYPE__ uintptr_t
Definition stdint.h:43
_PUBLIC void * memmove(void *_RESTRICT s1, const void *_RESTRICT s2, size_t n)
_PUBLIC void * memset(void *s, int c, size_t n)
Definition memset.c:4
CPU structure.
Definition cpu.h:84
cpu_id_t id
Definition cpu.h:86
cpu_t * self
Definition cpu.h:85
A simple ticket lock implementation.
Definition lock.h:44
Structure to define a percpu variable.
Definition percpu.h:64
percpu_def_t * start
Definition percpu.c:15
uint64_t generation
Definition percpu.c:17
percpu_def_t * end
Definition percpu.c:16