root / Whitix / branches / hybrid / memory / slab.c

Revision 541, 10.2 kB (checked in by mwhitworth, 3 months ago)

Update build process.

Line 
1/*  This file is part of Whitix.
2 *
3 *  Whitix is free software; you can redistribute it and/or modify
4 *  it under the terms of the GNU General Public License as published by
5 *  the Free Software Foundation; either version 2 of the License, or
6 *  (at your option) any later version.
7 *
8 *  Whitix is distributed in the hope that it will be useful,
9 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 *  GNU General Public License for more details.
12 *
13 *  You should have received a copy of the GNU General Public License
14 *  along with Whitix; if not, write to the Free Software
15 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
16 *
17 */
18
19#include <bitmap.h>
20#include <slab.h>
21#include <module.h>
22#include <error.h>
23#include <i386/i386.h>
24#include <imports.h>
25
26/*
27 * This is the beginnings of a decent slab allocator. Inspired by the paper on
28 * the Sun caching memory allocator.
29 *
30 * Each slab type has a bitmap, which has a size of PAGE_SIZE/MINIMUM_OBJECT_SIZE
31 * /8. It sounds memory inefficent when you first think about it, but other
32 * methods of pushing addresses into a linked list could use a lot more memory.
33 * However, the bitmap method requires more CPU resources.
34 *
35 */
36
37LIST_HEAD(cacheList);
38
39/* General slab limits */
40#define SLAB_START 0xC0000000
41#define SLAB_END   0xD0000000
42
43#define SLAB_TOTAL_BYTES(cache) \
44        (((cache)->numPages*PAGE_SIZE) - (cache)->offset)
45
46/* Gets over the bootstrapping problem, and from there a slab cache is allocated */
47static struct Cache cacheCache={
48        .list=LIST_HEAD_INIT(cacheCache.list),
49        .name="Cache Cache",
50        .size=sizeof(struct Cache),
51        .numPages = 1,
52        .numObjs = (PAGE_SIZE-sizeof(struct Slab))/sizeof(struct Cache),
53        .offset = sizeof(struct Slab),
54};
55
56struct CacheSize
57{
58        int size;
59        struct Cache* cache;
60};
61
62/* malloc just cycles through these */
63
64static struct CacheSize cacheSizes[]={
65        {16,NULL},
66        {32,NULL},
67        {64,NULL},
68        {128,NULL},
69        {256,NULL},
70        {512,NULL},
71        {1024,NULL},
72        {2048,NULL},
73        {4096,NULL},
74        {8192,NULL},
75        {16384,NULL},
76        {32768,NULL},
77        {65536,NULL}, /* 64k */
78        {131072,NULL},
79};
80
81/* Used globally if caller wishes to store slab info off slab */
82struct Cache* slabCache;
83
84void* malloc(size_t size)
85{
86        /* Should be serialized? */
87        DWORD flags;
88        int i;
89        void* obj;
90
91        IrqSaveFlags(flags);
92
93        for (i=0; i<count(cacheSizes); i++)
94                if (size <= cacheSizes[i].size)
95                        /* Should be ok to use */
96                        break;
97
98        if (i == count(cacheSizes)) /* Too big */
99        {
100                KePrint("malloc(%u) failed\n",size);
101                IrqRestoreFlags(flags);
102                return NULL;
103        }
104
105//      printf("malloc(%u) from %#X\n",size,__builtin_return_address(0));
106
107        obj=MemCacheAlloc(cacheSizes[i].cache);
108
109        IrqRestoreFlags(flags);
110        return obj;
111}
112
113SYMBOL_EXPORT(malloc);
114
115/***********************************************************************
116 *
117 * FUNCTION:    SlabFree
118 *
119 * DESCRIPTION: Mark a block of memory as free.
120 *
121 * PARAMETERS:  cache - the cache that contains the memory block.
122 *              slab - the slab that contains the memory block.
123 *              p - pointer to the memory block.
124 *
125 * RETURNS:     Nothing.
126 *
127 ***********************************************************************/
128
129static void SlabFree(struct Cache* cache,struct Slab* slab,void* p)
130{
131        /* Just clear the bit in the bitmap */
132        int i=((DWORD)p-(DWORD)(slab->page+cache->offset))/cache->size;
133
134        /* And zero out the memory concerned */
135        BmapSetBit(slab->bitmap,i,false);
136        slab->isFull=0; /* At least 1 piece is free now */
137
138        if (cache->ctor)
139                cache->ctor(p);
140        else
141                /* Make sure it's zeroed for later use */
142                ZeroMemory(p,cache->size);
143}
144
145/* If this is called to free a non-malloc'ed piece of memory, it's your fault (tm) */
146void free(void* p)
147{
148        int i;
149        struct Cache* currCache;
150        DWORD flags;
151
152        if (!p)
153                return;
154
155        IrqSaveFlags(flags);
156
157        for (i=0; i<count(cacheSizes); i++)
158        {
159                currCache=cacheSizes[i].cache;
160                if (!MemCacheFree(currCache,p))
161                {
162                        IrqRestoreFlags(flags);
163                        return;
164                }
165        }
166
167/* FIXME! */
168        KePrint("free did not free %#X (from %#X)\n",p,__builtin_return_address(0));
169        cli(); hlt();
170}
171
172SYMBOL_EXPORT(free);
173
174/***********************************************************************
175 *
176 * FUNCTION: MemCacheAllocSlab
177 *
178 * DESCRIPTION: Allocate a slab (well, the memory for it).
179 *
180 * PARAMETERS: cache - the cache to allocate a slab to.
181 *
182 * RETURNS:     The uninitialized slab structure.
183 *
184 ***********************************************************************/
185
186static inline struct Slab* MemCacheAllocSlab(struct Cache* cache)
187{
188        DWORD virtAddr;
189        struct Slab* slab;
190
191        /* Allocate multiple pages. Don't want to get in the way of quick code though */
192        if (cache->size > PAGE_SIZE)
193                virtAddr=VirtMapPhysRange(SLAB_START,SLAB_END,PAGE_ALIGN_UP(cache->size) >> PAGE_SHIFT,PAGE_KERNEL | PAGE_RW | PAGE_PRESENT);
194        else
195                virtAddr=VirtMapPhysPage(SLAB_START,SLAB_END,PAGE_KERNEL | PAGE_RW | PAGE_PRESENT);
196
197        /* Failed to allocate the memory for the slab */
198        if (!virtAddr)
199                return NULL;
200
201        if (cache->options & SLAB_OFFSLAB)
202        {
203                /* Allocate from a slab cache, which does keep it's info on slab */
204                slab=(struct Slab*)MemCacheAlloc(slabCache);
205        }else
206                slab=(struct Slab*)virtAddr;
207
208        /* Slab setup is performed here. ZeroMemory call also zeros the bitmap. */
209        ZeroMemory(slab,sizeof(struct Slab));
210        slab->page=virtAddr;
211
212        return slab;
213}
214
215static inline void MemCacheConstructSlab(struct Cache* cache,struct Slab* slab)
216{
217        int i;
218        void* obj;
219
220        /* If the slab has a constructor, call it */
221        if (cache->ctor)
222        {
223                for (i=0; i<cache->numObjs; i++)
224                {
225                        obj=(void*)(slab->page+(i*cache->size)+cache->offset);
226                        cache->ctor(obj);
227                }
228        }else
229                /* Zero out the slab */
230                ZeroMemory(slab->page+cache->offset,(cache->numPages*PAGE_SIZE)-cache->offset);
231
232        if (!cache->slabs)
233        {
234                /* This is now the first slab in the list */
235                INIT_LIST_HEAD(&slab->list);
236                cache->slabs=slab;
237        }else
238                /* Adds to the head of the list = less time traversing for a free slab */
239                ListAdd(&slab->list,&cache->slabs->list);
240}
241
242/***********************************************************************
243 *
244 * FUNCTION: MemCacheAddSlab
245 *
246 * DESCRIPTION: Add a slab of memory to a cache.
247 *
248 * PARAMETERS: cache - the cache to add a slab to.
249 *
250 * RETURNS: Usual error codes.
251 *
252 ***********************************************************************/
253
254/* Note: In future, calculate the amount of memory needed for the bitmap */
255
256static int MemCacheAddSlab(struct Cache* cache)
257{
258        struct Slab* slab;
259
260        slab=MemCacheAllocSlab(cache);
261
262        if (!slab)
263                return -ENOMEM;
264
265        MemCacheConstructSlab(cache,slab);
266        return 0;
267}
268
269/***********************************************************************
270 *
271 * FUNCTION:    MemCacheAlloc
272 *
273 * DESCRIPTION: Allocate a block of memory from a cache.
274 *
275 * PARAMETERS:  cache - the cache to allocate memory from.
276 *
277 * RETURNS:             Pointer to the block of memory allocated if it is a
278 *                              success, NULL otherwise.
279 *
280 ***********************************************************************/
281
282void* MemCacheAlloc(struct Cache* cache)
283{
284        DWORD flags;
285        struct Slab* currSlab;
286        int i;
287
288        if (!cache)
289                return NULL;
290
291        IrqSaveFlags(flags);
292
293        /* Go through all the slabs */
294        if (!cache->slabs)
295                /* If MemCacheAddSlab fails, it'll fail straight through to the
296                the bottom of this function, as currSlab will equal NULL. */
297                MemCacheAddSlab(cache);
298
299        currSlab=cache->slabs;
300
301        /* Now iterate through each slab, seeing if any are full, and if not, allocate from them */
302
303        while (currSlab)
304        {
305                if (!currSlab->isFull)
306                {
307                        /* Search the bitmap */
308                        for (i=0; i<cache->numObjs; i++)
309                        {
310                                if (!BmapGetBit(currSlab->bitmap,i))
311                                {
312                                        BmapSetBit(currSlab->bitmap,i,true);
313                                        if (i == cache->numObjs-1)
314                                                currSlab->isFull=1;
315
316                                        IrqRestoreFlags(flags);
317                                        return (void*)(currSlab->page+cache->offset+(i*cache->size));
318                                }
319                        }
320                }
321               
322                /* Only one in the list */
323                if (currSlab->list.prev == &cache->slabs->list)
324                        MemCacheAddSlab(cache);
325
326                currSlab=ListEntry(currSlab->list.prev,struct Slab,list);
327        }
328
329        KePrint("MemCacheAlloc(%#X) - failed to allocate memory\n",cache);
330        IrqRestoreFlags(flags);
331        /* Shouldn't get here, as all memory requests should be satifisted by adding slabs */
332        return NULL;
333}
334
335SYMBOL_EXPORT(MemCacheAlloc);
336
337int MemCacheFree(struct Cache* cache,void* obj)
338{
339        DWORD flags;
340        DWORD addr;
341        struct Slab* currSlab;
342        int ret=-EFAULT;
343
344        if (!obj)
345                return -EFAULT;
346
347        IrqSaveFlags(flags);
348
349        addr=(DWORD)obj;
350        currSlab=cache->slabs;
351
352        while (currSlab)
353        {
354                if (addr >= (currSlab->page+cache->offset) && addr < currSlab->page+(cache->numPages*PAGE_SIZE))
355                {
356                        /* In this slab then */
357                        SlabFree(cache,currSlab,obj);
358                        ret=0;
359                        goto out;
360                }
361               
362                /* End of list really */
363                if (currSlab->list.prev == &cache->slabs->list)
364                        goto out;
365
366                currSlab=ListEntry(currSlab->list.prev,struct Slab,list);
367        }
368
369out:
370//      printf("Done, %d\n",ret);
371        IrqRestoreFlags(flags);
372        return ret;
373}
374
375SYMBOL_EXPORT(MemCacheFree);
376
377struct Cache* MemCacheCreate(const char* name,size_t size,Constructor ctor,Destructor dtor,int options)
378{
379        struct Cache* retVal;
380
381        if (!size)
382                return NULL;
383
384        if (size < SLAB_MIN_OBJECT_SIZE)
385                size=SLAB_MIN_OBJECT_SIZE;
386
387        /* Allocate descriptor from cacheCache */
388        retVal=(struct Cache*)MemCacheAlloc(&cacheCache);
389
390        /* 4 byte align the size */
391        size&=~0x3;
392
393        retVal->size=size;
394
395        if (size > PAGE_SIZE)
396                retVal->numPages=size >> PAGE_SHIFT;
397        else
398                retVal->numPages=1;
399
400        retVal->name=name;
401        retVal->ctor=ctor;
402        retVal->dtor=dtor;
403
404        if (size >= PAGE_SIZE/4)
405                options |= SLAB_OFFSLAB;
406
407        retVal->offset=(options & SLAB_OFFSLAB) ? 0 : (sizeof(struct Slab));
408        retVal->numObjs=SLAB_TOTAL_BYTES(retVal)/retVal->size;
409        retVal->options=options;
410
411        return retVal;
412}
413
414SYMBOL_EXPORT(MemCacheCreate);
415
416DWORD functions[]=
417{
418        (DWORD)malloc,
419        (DWORD)free,
420        (DWORD)MemCacheCreate,
421        (DWORD)MemCacheAlloc,
422        (DWORD)MemCacheFree,
423};
424
425int SlabInit()
426{
427        int i;
428
429        /* Set up the cache cache */
430        ListAdd(&cacheList, &cacheCache.list);
431
432        /* Now start allocating caches */
433
434        /* Set up a slab cache if the caller wishes to store slab information off slab */
435        slabCache=MemCacheCreate("Slab cache",sizeof(struct Slab),NULL,NULL,0);
436
437        for (i=0; i<count(cacheSizes); i++)
438        {
439                cacheSizes[i].cache=MemCacheCreate("malloc",cacheSizes[i].size,NULL,NULL,0);
440                if (!cacheSizes[i].cache)
441                {
442                        KePrint("Malloc slab allocation failed!\n");
443                        return -ENOMEM;
444                }
445        }
446
447        ResolveSetMemFunctions(functions);
448
449        return 0;
450}
451
452ModuleInit(SlabInit);
Note: See TracBrowser for help on using the browser.