1 /*
2  * Copyright (c) 2013-2023 Ali Mashtizadeh
3  * All rights reserved.
4  */
5 
6 #include <stdbool.h>
7 #include <stdint.h>
8 #include <string.h>
9 
10 #include <sys/cdefs.h>
11 #include <sys/kassert.h>
12 #include <sys/kdebug.h>
13 #include <sys/queue.h>
14 #include <sys/kmem.h>
15 
16 #include <machine/pmap.h>
17 
18 LIST_HEAD(SlabListHead, Slab) slabList = LIST_HEAD_INITIALIZER(slabList);
19 
20 /**
21  * Slab_Init --
22  *
23  *	Create's a slab for a single type of object in the kernel.
24  *
25  *	@param [in] slab Slab that the object belongs to.
26  *	@param [in] name Developer friendly name for debugging purposes.
27  *	@param [in] objsz Size of the object in bytes.
28  *	@param [in] align Alignment of the object in bytes.
29  */
30 void
Slab_Init(Slab * slab,const char * name,uintptr_t objsz,uintptr_t align)31 Slab_Init(Slab *slab, const char *name, uintptr_t objsz, uintptr_t align)
32 {
33     ASSERT(objsz >= sizeof(SlabElement));
34 
35     slab->objsz = objsz;
36     slab->align = align;
37     slab->xmem = XMem_New();
38     slab->objs = 0;
39     slab->freeObjs = 0;
40     slab->allocs = 0;
41     slab->frees = 0;
42     LIST_INIT(&slab->freeList);
43 
44     ASSERT(slab->xmem != NULL);
45 
46     strncpy(&slab->name[0], name, SLAB_NAMELEN);
47 
48     Spinlock_Init(&slab->lock, name, SPINLOCK_TYPE_NORMAL);
49 
50     LIST_INSERT_HEAD(&slabList, slab, slabList);
51 }
52 
53 /**
54  * SlabExtend --
55  *
56  *	Grow the slab to allocate new objects.
57  *
58  *	@param [in] slab Slab that we want to expand.
59  *	@retval -1 Failed to expand the slab.
60  *	@retval 0 Success.
61  */
62 int
SlabExtend(Slab * slab)63 SlabExtend(Slab *slab)
64 {
65     uintptr_t base = XMem_GetBase(slab->xmem);
66     uintptr_t len = XMem_GetLength(slab->xmem);
67     uintptr_t inc;
68     uintptr_t realObjSz = ROUNDUP(slab->objsz, slab->align);
69 
70     inc = ROUNDUP(realObjSz * 64, PGSIZE);
71     if (inc < 4 * PGSIZE) {
72 	inc = 4 * PGSIZE;
73     }
74 
75     if (!XMem_Allocate(slab->xmem, len + inc)) {
76 	kprintf("Slab: Cannot grow XMem region!\n");
77 	return -1;
78     }
79 
80     // Add empty objects to linked list
81     uintptr_t i;
82     uintptr_t objs = inc / realObjSz;
83     for (i = 0; i < objs; i++) {
84 	SlabElement *elem = (SlabElement *)(base + len + i * realObjSz);
85 
86 	LIST_INSERT_HEAD(&slab->freeList, elem, free);
87     }
88 
89     slab->objs += objs;
90     slab->freeObjs += objs;
91 
92     return 0;
93 }
94 
95 /**
96  * Slab_Alloc --
97  *
98  *	Free a slab object.
99  *
100  *	@param [in] slab Slab that the object belongs to.
101  *	@retval NULL Could not allocate an object.
102  *	@return Pointer to the allocated object.
103  */
104 void *
Slab_Alloc(Slab * slab)105 Slab_Alloc(Slab *slab)
106 {
107     SlabElement *elem;
108 
109     Spinlock_Lock(&slab->lock);
110 
111     if (slab->freeObjs == 0)
112 	SlabExtend(slab);
113 
114     elem = LIST_FIRST(&slab->freeList);
115     if (elem != NULL) {
116 	LIST_REMOVE(elem, free);
117 	slab->allocs++;
118 	slab->freeObjs--;
119     }
120 
121     Spinlock_Unlock(&slab->lock);
122 
123     return (void *)elem;
124 }
125 
126 /**
127  * Slab_Free --
128  *
129  *	Free a slab object.
130  *
131  *	@param [in] slab Slab that the object belongs to.
132  *	@param [in] region Object to free.
133  */
134 void
Slab_Free(Slab * slab,void * region)135 Slab_Free(Slab *slab, void *region)
136 {
137     Spinlock_Lock(&slab->lock);
138 
139     SlabElement *elem = (SlabElement *)region;
140     LIST_INSERT_HEAD(&slab->freeList, elem, free);
141     slab->frees++;
142     slab->freeObjs++;
143 
144     Spinlock_Unlock(&slab->lock);
145 }
146 
147 static void
Debug_Slabs(int argc,const char * argv[])148 Debug_Slabs(int argc, const char *argv[])
149 {
150     Slab *slab;
151 
152     kprintf("%-36s %-10s %-10s %-10s\n", "Slab Name", "Alloc", "Free", "Total");
153     LIST_FOREACH(slab, &slabList, slabList) {
154 	kprintf("%-36s %-10lld %-10lld %-10lld\n", slab->name,
155 		slab->objs - slab->freeObjs, slab->freeObjs, slab->objs);
156     }
157 }
158 
159 REGISTER_DBGCMD(slabs, "Display list of slabs", Debug_Slabs);
160 
161