summaryrefslogtreecommitdiffstats
path: root/mem/slab.c
blob: 75b83027540755e76ae84a12c7b68da942b30d04 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
#include "slab.h"
#include "memory.h"
#include "lib/format.h"
#include "lib/string.h"
#include "lib/jove.h"
#include "io/log.h"

extern void *_kernel_end;

static uintptr_t s_addr_next_free;

#define GENERIC_CACHEC 8
static struct SlabCache s_generic_caches[GENERIC_CACHEC];

static uintptr_t
s_next_free(size_t width)
{
    uintptr_t ret = s_addr_next_free;
    s_addr_next_free += width;
    mem_ensure_range(ret, s_addr_next_free, true, false);
    return ret;
}

static int
s_get_free_listw(size_t slabw, size_t objw)
{
    int freelistc = 1;
    while(freelistc < 256) {
        int maxobjc = (slabw - (freelistc * sizeof(uintptr_t))) / objw;
        if(maxobjc <= freelistc) return maxobjc;
        freelistc++;
    }
    return freelistc;
}

static struct SlabDescriptor
*s_slab_new(struct SlabCache *cache, struct SlabDescriptor *last)
{
    size_t slab_width = (cache->slab_pages * PAGESIZE);
    uintptr_t descr_base = s_next_free(slab_width);
    struct SlabDescriptor *descr = (struct SlabDescriptor*)descr_base;

    size_t free_listc = s_get_free_listw(
            slab_width - sizeof(struct SlabDescriptor),
            cache->obj_size);
    size_t descriptor_width = sizeof(struct SlabDescriptor)
        + (free_listc * sizeof(uintptr_t));
    uintptr_t obj_base = descr_base + descriptor_width;

    if(free_listc < 8) {
        free_listc = ((slab_width - sizeof(struct SlabDescriptor)) / cache->obj_size);
        descr = mem_alloc(sizeof(struct SlabDescriptor) + (free_listc * sizeof(uintptr_t)));
        obj_base = descr_base;
    }

    *descr = (struct SlabDescriptor) {
        .prev = last,
        .next = (last == NULL ? NULL : last->next),
        .slab_base = (void*)descr_base,
        .obj_base = (void*)obj_base,
        .free_count = free_listc,
        .free_index = free_listc - 1
    };
    for(size_t i = 0; i < free_listc; i++) {
        descr->free[i] = obj_base + (i * cache->obj_size);
    }

    return descr;
}

void
mem_slabcache_new(struct SlabCache *cache, char *name, size_t objsize)
{
    if(objsize % 8 > 0) objsize += (8 - (objsize % 8));
    size_t pages = objsize > 512 ? (objsize >> 9) : 1;
    *cache = (struct SlabCache){
        .obj_size = objsize,
        .slab_pages = pages,
        .list_free = NULL,
        .list_partial = NULL,
        .list_full = NULL
    };
    size_t namelen = strlen(name);
    namelen = namelen > 32 ? 32 : namelen;
    memcpy(cache->name, name, namelen);

    //Allocate the first slab
    cache->list_free = s_slab_new(cache, NULL);
}

void*
mem_slab_alloc(struct SlabCache *cache)
{
    // Get a free slab
    struct SlabDescriptor *slab = NULL;
    if(cache->list_partial != NULL) slab = cache->list_partial;
    if(slab == NULL && cache->list_free != NULL) {
        slab = cache->list_free;
        cache->list_free = slab->next;
    }
    if(slab == NULL) slab = s_slab_new(cache, cache->list_free);
    cache->list_partial = slab;

    // Take an object from the slab.
    uintptr_t objaddr = slab->free[slab->free_index];
    slab->free_index -= 1;

    if(slab->free_index < 0) {
        slab->next = cache->list_full;
        cache->list_full = slab;
    }
    return (void*)objaddr;
}

void
mem_slab_free(struct SlabCache *cache, void *ptr)
{
    uintptr_t addr = (uintptr_t)ptr;
    //Look for the pointer in the bounds of every slab
    for(struct SlabDescriptor *slab = cache->list_full; 
            slab != NULL; slab = slab->next)
    {
        uintptr_t base = (uintptr_t)slab->obj_base;
        uintptr_t limit = ((uintptr_t)slab->slab_base) 
            + (cache->slab_pages * PAGESIZE);
        if(addr > limit || addr < base) continue;
        if((addr - base) % cache->obj_size != 0) {
            klogf("Tried to free offset pointer %#016X in slab %s\n",
                    addr, cache->name);
            return;
        }
        slab->free_index++;
        slab->free[slab->free_index] = addr;

        cache->list_full = slab->next;
        slab->next = cache->list_partial;
        cache->list_partial = slab;
        return;
    }
    for(struct SlabDescriptor *slab = cache->list_partial; 
            slab != NULL; slab = slab->next)
    {
        uintptr_t base = (uintptr_t)slab->obj_base;
        uintptr_t limit = ((uintptr_t)slab->slab_base) 
            + (cache->slab_pages * PAGESIZE);
        if(addr > limit || addr < base) continue;
        if((addr - base) % cache->obj_size != 0) {
            klogf("Tried to free offset pointer %#016X in slab %s\n",
                    addr, cache->name);
            return;
        }
        slab->free_index++;
        slab->free[slab->free_index] = addr;

        if(slab->free_index == (slab->free_count - 1)) {
            cache->list_partial = slab->next;
            slab->next = cache->list_free;
            cache->list_free = slab;
        }
        return;
    }
}

void*
mem_alloc(size_t width)
{
    size_t width_log2 = (__builtin_clz(width) ^ 31) + 1;
    if(width_log2 < 6) width_log2 = 6;
    width_log2 -= 6;
    if(width_log2 >= GENERIC_CACHEC) {
        klogf("Allocation size %i too big for generic caches!\n", width);
        return NULL;
    }

    struct SlabCache *generic_cache = &s_generic_caches[width_log2];
    return mem_slab_alloc(generic_cache);
}

void
mem_free(void *ptr)
{
    for(int i = 0; i < GENERIC_CACHEC; i++) {
        mem_slab_free(&s_generic_caches[i], ptr);
    }
}

void
mem_slab_setup(void)
{
    s_addr_next_free = (uintptr_t)&_kernel_end;
    s_addr_next_free = ((s_addr_next_free >> 12) + 1) << 12;
    s_get_free_listw(PAGESIZE - sizeof(struct SlabDescriptor), 32);

    for(int i = 0; i < GENERIC_CACHEC; i++)
    {
        size_t objsize = 1 << (i + 6);
        char slab_name[SLABCACHE_NAME_LIMIT];
        sfmt(slab_name, SLABCACHE_NAME_LIMIT, "generic_%i", 1 << (i + 6));
        mem_slabcache_new(&s_generic_caches[i], slab_name, objsize);
    }
}