summaryrefslogtreecommitdiffstats
path: root/memory/slab.c
blob: dc8aeda362b06de4a51a830601450711e9a05d42 (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
#include "slab.h"
#include "bump.h"
#include "memory.h"
#include "string.h"
#include "jove.h"
#include "print.h"
#include "klib/format.h"

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 slab_t
*s_slab_new(slab_cache_t *cache, slab_t *last)
{
    size_t slab_width = (cache->slab_pages << PAGE_SHIFT);
    uintptr_t descr_base = (uintptr_t)bump_alloc(slab_width);
    slab_t *descr = (slab_t*)descr_base;

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

    if(free_listc < 8) {
        free_listc = ((slab_width - sizeof(slab_t)) / cache->obj_size);
        descr = kmalloc(sizeof(slab_t) + (free_listc * sizeof(uintptr_t)));
        obj_base = descr_base;
    }
    cache->obj_capacity += free_listc;

    *descr = (slab_t) {
        .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
slabcache_new(slab_cache_t *cache, char *name, size_t objsize)
{
    if(objsize % 8 > 0) objsize += (8 - (objsize % 8));
    size_t pages = objsize > 512 ? (objsize >> 9) : 1;
    *cache = (slab_cache_t){
        .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*
slab_alloc(slab_cache_t *cache)
{
    // Get a free slab
    slab_t *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
slab_free(slab_cache_t *cache, void *ptr)
{
    uintptr_t addr = (uintptr_t)ptr;
    //Look for the pointer in the bounds of every slab
    for(slab_t *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 << PAGE_SHIFT);
        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(slab_t *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 << PAGE_SHIFT);
        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 == ((int)slab->free_count - 1)) {
            cache->list_partial = slab->next;
            slab->next = cache->list_free;
            cache->list_free = slab;
        }
        return;
    }
}