Skip to content

Commit a9b2a38

Browse files
committed
slab: integrate slab experiment, replace spalloc
1 parent dd08b38 commit a9b2a38

File tree

7 files changed

+190
-126
lines changed

7 files changed

+190
-126
lines changed

kernel/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ add_executable(nightingale_kernel
6060
random.c
6161
ringbuf.c
6262
signal.c
63-
spalloc.c
63+
slab.c
6464
spin.c
6565
string.c
6666
sync_testbed.c

kernel/include/ng/slab.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#pragma once
2+
3+
#include <list.h>
4+
5+
struct slab_cache {
6+
list slabs;
7+
size_t object_size;
8+
size_t requested_size;
9+
};
10+
11+
void slab_cache_init(struct slab_cache *cache, size_t requested_size);
12+
13+
void *slab_alloc(struct slab_cache *cache);
14+
void slab_free(struct slab_cache *cache, void *ptr);
15+
void validate_page_sizes();

kernel/include/ng/spalloc.h

Lines changed: 0 additions & 33 deletions
This file was deleted.

kernel/slab.c

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
#include <assert.h>
2+
#include <stdio.h>
3+
#include <stdlib.h>
4+
#include <string.h>
5+
6+
#include <list.h>
7+
#include <ng/slab.h>
8+
#include <ng/vmm.h>
9+
10+
constexpr size_t page_size = 0x1000;
11+
constexpr size_t waste_percentage = 10;
12+
13+
void *alloc_pages(size_t count) {
14+
return vmm_reserve(count * 0x1000);
15+
}
16+
17+
struct slab_header {
18+
struct slab_cache *cache;
19+
20+
list_node siblings;
21+
22+
void *base;
23+
size_t size;
24+
25+
void *first_free;
26+
27+
size_t object_count;
28+
size_t object_slots;
29+
};
30+
31+
// objects absolutely must be stored as 8 bytes or larger for the free list
32+
// anything larger than 8 bytes will store as a multiple of 16 for alignment
33+
static size_t stored_size(size_t object_size) {
34+
if (object_size <= 8)
35+
return 8;
36+
else
37+
return (object_size + 15) & ~15;
38+
}
39+
40+
// figure out how many pages we need per slab in order to not waste more than
41+
// `waste_percentage` % of the allocated pages
42+
static size_t pages_for_size(size_t object_size) {
43+
for (size_t pages = 1;; pages++) {
44+
size_t region_size = pages * page_size;
45+
size_t available_size = region_size - sizeof(struct slab_header);
46+
if ((available_size % object_size) * 100 / region_size
47+
< waste_percentage)
48+
return pages;
49+
}
50+
}
51+
52+
static size_t objects_per_slab(size_t object_size, size_t pages) {
53+
return (pages * page_size - sizeof(struct slab_header)) / object_size;
54+
}
55+
56+
static void *slab_object_ptr(struct slab_header *slab, size_t n) {
57+
size_t object_size = slab->cache->object_size;
58+
size_t object_count = slab->object_slots;
59+
60+
if (n >= object_count)
61+
return nullptr;
62+
63+
return slab->base + object_size * n;
64+
}
65+
66+
static bool slab_contains_ptr(struct slab_header *slab, void *ptr) {
67+
return ptr >= slab->base && ptr < (void *)slab;
68+
}
69+
70+
static struct slab_header *alloc_slab(struct slab_cache *cache) {
71+
size_t pages = pages_for_size(cache->object_size);
72+
size_t objects = objects_per_slab(cache->object_size, pages);
73+
74+
void *slab = alloc_pages(pages);
75+
if (!slab)
76+
return nullptr;
77+
78+
// header is placed at the _end_ of the allocated region
79+
struct slab_header *header
80+
= slab + (pages * page_size) - sizeof(struct slab_header);
81+
memset(header, 0, sizeof(struct slab_header));
82+
83+
header->base = slab;
84+
header->size = pages * page_size;
85+
header->object_slots = objects;
86+
header->cache = cache;
87+
list_append(&cache->slabs, &header->siblings);
88+
89+
// set up object free list
90+
for (size_t o = 0; o < objects - 1; o++) {
91+
*(void **)slab_object_ptr(header, o) = slab_object_ptr(header, o + 1);
92+
}
93+
*(void **)slab_object_ptr(header, objects - 1) = nullptr;
94+
95+
header->first_free = slab;
96+
97+
return header;
98+
}
99+
100+
static void *alloc_object(struct slab_header *slab) {
101+
if (slab->object_count == slab->object_slots) {
102+
return nullptr;
103+
}
104+
105+
void *object = slab->first_free;
106+
slab->first_free = *(void **)slab->first_free;
107+
slab->object_count += 1;
108+
return object;
109+
}
110+
111+
static void free_object(struct slab_header *slab, void *ptr) {
112+
assert(slab_contains_ptr(slab, ptr));
113+
114+
*(void **)ptr = slab->first_free;
115+
slab->first_free = ptr;
116+
slab->object_count -= 1;
117+
}
118+
119+
void *slab_alloc(struct slab_cache *cache) {
120+
void *object = nullptr;
121+
122+
list_for_each (&cache->slabs) {
123+
struct slab_header *slab
124+
= container_of(struct slab_header, siblings, it);
125+
if ((object = alloc_object(slab)))
126+
return object;
127+
}
128+
129+
struct slab_header *new_slab = alloc_slab(cache);
130+
if (new_slab)
131+
return alloc_object(new_slab);
132+
else
133+
return nullptr;
134+
}
135+
136+
void slab_free(struct slab_cache *cache, void *ptr) {
137+
list_for_each (&cache->slabs) {
138+
struct slab_header *slab
139+
= container_of(struct slab_header, siblings, it);
140+
if (slab_contains_ptr(slab, ptr)) {
141+
free_object(slab, ptr);
142+
return;
143+
}
144+
}
145+
146+
assert(0);
147+
}
148+
149+
void slab_cache_init(struct slab_cache *cache, size_t requested_size) {
150+
cache->requested_size = requested_size;
151+
cache->object_size = stored_size(requested_size);
152+
list_init(&cache->slabs);
153+
}
154+
155+
void validate_page_sizes() {
156+
printf("size\tpages\tobjects\n");
157+
for (size_t i = 3; i < 15; i++) {
158+
size_t o = (1 << i);
159+
size_t p = pages_for_size(o);
160+
size_t n = objects_per_slab(o, p);
161+
printf("%zu\t%zu\t%zu\n", o, p, n);
162+
163+
o = (1 << i) + (1 << (i - 1));
164+
p = pages_for_size(o);
165+
n = objects_per_slab(o, p);
166+
printf("%zu\t%zu\t%zu\n", o, p, n);
167+
}
168+
}

kernel/spalloc.c

Lines changed: 0 additions & 54 deletions
This file was deleted.

kernel/tests.c

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
#include <assert.h>
22
#include <ng/cpu.h>
3-
#include <ng/spalloc.h>
43
#include <ng/tests.h>
54
#include <ng/thread.h>
65

@@ -12,37 +11,6 @@ void run_sync_tests();
1211
kthread_exit();
1312
}
1413

15-
void run_spalloc_test() {
16-
// validate spalloc working
17-
struct testing {
18-
int a, b, c, d, e, f, g, h;
19-
};
20-
struct spalloc foobar;
21-
sp_init(&foobar, struct testing);
22-
23-
struct testing *first = sp_alloc(&foobar);
24-
assert(first == foobar.region);
25-
first->a = 10;
26-
27-
struct testing *second = sp_alloc(&foobar);
28-
assert(second == sp_at(&foobar, 1));
29-
second->a = 11;
30-
31-
assert(first->a == 10);
32-
33-
first->g = 1;
34-
sp_free(&foobar, first);
35-
assert(first->g != 1); // poison
36-
assert(second->a == 11);
37-
38-
struct testing *re_first = sp_alloc(&foobar);
39-
assert(re_first == first);
40-
41-
assert(foobar.capacity == 0x10000);
42-
assert(foobar.count == 2);
43-
}
44-
4514
void run_all_tests() {
46-
run_spalloc_test();
4715
kthread_create(test_kernel_thread, "get a cat");
4816
}

kernel/timer.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#include <assert.h>
22
#include <ng/fs.h>
33
#include <ng/irq.h>
4-
#include <ng/spalloc.h>
4+
#include <ng/slab.h>
55
#include <ng/sync.h>
66
#include <ng/timer.h>
77
#include <ng/x86/pit.h>
@@ -13,7 +13,7 @@
1313
uint64_t kernel_timer = 0;
1414
static long long last_tsc;
1515
static long long tsc_delta;
16-
struct spalloc timer_pool;
16+
struct slab_cache timer_pool;
1717
list timer_q = LIST_INIT(timer_q);
1818
spinlock_t timer_q_lock;
1919

@@ -41,7 +41,7 @@ struct timer_event {
4141
};
4242

4343
void timer_init() {
44-
sp_init(&timer_pool, struct timer_event);
44+
slab_cache_init(&timer_pool, sizeof(struct timer_event));
4545
irq_install(0, timer_handler, nullptr);
4646
}
4747

@@ -51,7 +51,7 @@ void assert_consistency(struct timer_event *t) {
5151

5252
struct timer_event *insert_timer_event(uint64_t delta_t, void (*fn)(void *),
5353
const char *inserter_name, void *data) {
54-
struct timer_event *q = sp_alloc(&timer_pool);
54+
struct timer_event *q = slab_alloc(&timer_pool);
5555
q->at = kernel_timer + delta_t;
5656
q->flags = 0;
5757
q->fn = fn;
@@ -84,7 +84,7 @@ void drop_timer_event(struct timer_event *te) {
8484
spin_lock(&timer_q_lock);
8585
list_remove(&te->node);
8686
spin_unlock(&timer_q_lock);
87-
sp_free(&timer_pool, te);
87+
slab_free(&timer_pool, te);
8888
}
8989

9090
void timer_procfile(struct file *ofd, void *) {
@@ -118,7 +118,7 @@ void timer_handler(interrupt_frame *r, void *impl) {
118118

119119
spin_unlock(&timer_q_lock);
120120
timer_head->fn(timer_head->data);
121-
sp_free(&timer_pool, timer_head);
121+
slab_free(&timer_pool, timer_head);
122122
spin_lock(&timer_q_lock);
123123
}
124124
spin_unlock(&timer_q_lock);

0 commit comments

Comments
 (0)