155 lines
4.3 KiB
C
155 lines
4.3 KiB
C
#include "ktest.h"
|
|
#include "slab.h"
|
|
#include "pmm.h"
|
|
#include "string.h"
|
|
#include "heap.h"
|
|
|
|
KTEST(slabInitRejectsOversizedObject)
|
|
{
|
|
KmemCache cache;
|
|
KT_FALSE(t, kmemCacheInit(&cache, "toobig", frameSize));
|
|
KT_EQ(t, kmemCacheCapacity(&cache), 0);
|
|
KT_NULL(t, kmemCacheAlloc(&cache));
|
|
}
|
|
|
|
KTEST(slabAllocDistinctNonOverlapping)
|
|
{
|
|
KmemCache cache;
|
|
KT_TRUE(t, kmemCacheInit(&cache, "small32", 32));
|
|
KT_GT(t, kmemCacheObjsPerSlab(&cache), 0);
|
|
KT_EQ(t, kmemCacheCapacity(&cache), 0); /* nothing grown yet */
|
|
|
|
void* a = kmemCacheAlloc(&cache);
|
|
void* b = kmemCacheAlloc(&cache);
|
|
void* c = kmemCacheAlloc(&cache);
|
|
KT_NOTNULL(t, a);
|
|
KT_NOTNULL(t, b);
|
|
KT_NOTNULL(t, c);
|
|
KT_NE(t, a, b);
|
|
KT_NE(t, b, c);
|
|
KT_NE(t, a, c);
|
|
KT_EQ(t, kmemCacheUsed(&cache), 3);
|
|
|
|
/* write a distinct pattern into each object and check none of them
|
|
stomped on a neighbor */
|
|
memset(a, 0xAA, 32);
|
|
memset(b, 0xBB, 32);
|
|
memset(c, 0xCC, 32);
|
|
uint8 probe[32];
|
|
memset(probe, 0xAA, 32);
|
|
KT_EQ(t, memcmp(a, probe, 32), 0);
|
|
memset(probe, 0xBB, 32);
|
|
KT_EQ(t, memcmp(b, probe, 32), 0);
|
|
memset(probe, 0xCC, 32);
|
|
KT_EQ(t, memcmp(c, probe, 32), 0);
|
|
|
|
kmemCacheFree(&cache, a);
|
|
kmemCacheFree(&cache, b);
|
|
kmemCacheFree(&cache, c);
|
|
KT_EQ(t, kmemCacheUsed(&cache), 0);
|
|
|
|
kmemCacheDestroy(&cache);
|
|
}
|
|
|
|
KTEST(slabFreeReusesLifo)
|
|
{
|
|
KmemCache cache;
|
|
kmemCacheInit(&cache, "reuse", 16);
|
|
|
|
void* a = kmemCacheAlloc(&cache);
|
|
kmemCacheFree(&cache, a);
|
|
void* b = kmemCacheAlloc(&cache);
|
|
KT_EQP(t, a, b); /* single-slot free list, must come back the same */
|
|
|
|
kmemCacheFree(&cache, b);
|
|
kmemCacheDestroy(&cache);
|
|
}
|
|
|
|
KTEST(slabGrowsAcrossMultipleSlabs)
|
|
{
|
|
KmemCache cache;
|
|
kmemCacheInit(&cache, "grow64", 64);
|
|
usize perSlab = kmemCacheObjsPerSlab(&cache);
|
|
KT_GT(t, perSlab, 0);
|
|
|
|
uint64 framesBefore = pmmFreeFrames();
|
|
|
|
/* fill exactly the first slab */
|
|
void* first = nullptr;
|
|
for (usize i = 0; i < perSlab; i++) {
|
|
void* p = kmemCacheAlloc(&cache);
|
|
KT_NOTNULL(t, p);
|
|
if (i == 0)
|
|
first = p;
|
|
}
|
|
KT_EQ(t, kmemCacheSlabCount(&cache), 1);
|
|
KT_EQ(t, kmemCacheUsed(&cache), perSlab);
|
|
|
|
/* one more forces a second slab (a second frame from pmm) */
|
|
void* overflow = kmemCacheAlloc(&cache);
|
|
KT_NOTNULL(t, overflow);
|
|
KT_EQ(t, kmemCacheSlabCount(&cache), 2);
|
|
KT_EQ(t, kmemCacheCapacity(&cache), perSlab * 2);
|
|
KT_EQ(t, pmmFreeFrames(), framesBefore - 2);
|
|
KT_NE(t, overflow, first);
|
|
|
|
kmemCacheDestroy(&cache);
|
|
/* destroying the cache must return every frame it ever grew into */
|
|
KT_EQ(t, pmmFreeFrames(), framesBefore);
|
|
}
|
|
|
|
#define slabStressCount 800
|
|
|
|
KTEST(slabStressAllocFreePattern)
|
|
{
|
|
KmemCache cache;
|
|
kmemCacheInit(&cache, "stress", 40);
|
|
|
|
void** slots = (void**)kmalloc(sizeof(void*) * slabStressCount);
|
|
KT_NOTNULL(t, slots);
|
|
if (!slots) {
|
|
kmemCacheDestroy(&cache);
|
|
return;
|
|
}
|
|
|
|
for (int i = 0; i < slabStressCount; i++) {
|
|
slots[i] = kmemCacheAlloc(&cache);
|
|
KT_NOTNULL(t, slots[i]);
|
|
if (slots[i])
|
|
*(int32*)slots[i] = i;
|
|
}
|
|
KT_EQ(t, kmemCacheUsed(&cache), slabStressCount);
|
|
|
|
/* every slot still holds the value we stamped, none aliased */
|
|
for (int i = 0; i < slabStressCount; i++) {
|
|
if (slots[i])
|
|
KT_EQ(t, *(int32*)slots[i], i);
|
|
}
|
|
|
|
/* free every third object, then re-allocate that many and confirm the
|
|
cache handed back exactly that many reclaimed slots (no capacity
|
|
growth needed to satisfy them) */
|
|
usize freedCount = 0;
|
|
for (int i = 0; i < slabStressCount; i += 3) {
|
|
kmemCacheFree(&cache, slots[i]);
|
|
slots[i] = nullptr;
|
|
freedCount++;
|
|
}
|
|
KT_EQ(t, kmemCacheUsed(&cache), slabStressCount - freedCount);
|
|
|
|
usize capacityBeforeRefill = kmemCacheCapacity(&cache);
|
|
for (int i = 0; i < slabStressCount; i += 3) {
|
|
slots[i] = kmemCacheAlloc(&cache);
|
|
KT_NOTNULL(t, slots[i]);
|
|
}
|
|
KT_EQ(t, kmemCacheCapacity(&cache), capacityBeforeRefill);
|
|
KT_EQ(t, kmemCacheUsed(&cache), slabStressCount);
|
|
|
|
for (int i = 0; i < slabStressCount; i++)
|
|
kmemCacheFree(&cache, slots[i]);
|
|
KT_EQ(t, kmemCacheUsed(&cache), 0);
|
|
|
|
kfree(slots);
|
|
kmemCacheDestroy(&cache);
|
|
}
|