* Added optional paranoid checking of the slab memory manager meta chunks

after each chunk allocation/deallocation.
* The commands that dump chunks also verify, whether chunks that look free
  are in the free list.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@37360 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold
2010-07-02 23:33:55 +00:00
parent b0eaa06c26
commit 05951b9b6a
3 changed files with 173 additions and 4 deletions
@@ -101,6 +101,9 @@
// Use the slab allocator as generic memory allocator (malloc()/free()). // Use the slab allocator as generic memory allocator (malloc()/free()).
#define USE_SLAB_ALLOCATOR_FOR_MALLOC 1 #define USE_SLAB_ALLOCATOR_FOR_MALLOC 1
// Enables additional sanity checks in the slab allocator's memory manager.
#define DEBUG_SLAB_MEMORY_MANAGER_PARANOID_CHECKS 0
// When set limits the amount of available RAM (in MB). // When set limits the amount of available RAM (in MB).
//#define LIMIT_AVAILABLE_MEMORY 256 //#define LIMIT_AVAILABLE_MEMORY 256
+166 -3
View File
@@ -20,6 +20,8 @@
#include <vm/VMCache.h> #include <vm/VMCache.h>
#include <vm/VMTranslationMap.h> #include <vm/VMTranslationMap.h>
#include "kernel_debug_config.h"
#include "ObjectCache.h" #include "ObjectCache.h"
#include "slab_private.h" #include "slab_private.h"
@@ -31,6 +33,12 @@
# define TRACE(x...) do {} while (false) # define TRACE(x...) do {} while (false)
#endif #endif
#if DEBUG_SLAB_MEMORY_MANAGER_PARANOID_CHECKS
# define PARANOID_CHECKS_ONLY(x) x
#else
# define PARANOID_CHECKS_ONLY(x)
#endif
static const char* const kSlabAreaName = "slab area"; static const char* const kSlabAreaName = "slab area";
@@ -51,7 +59,6 @@ MemoryManager::AllocationEntry* MemoryManager::sAllocationEntryDontWait;
bool MemoryManager::sMaintenanceNeeded; bool MemoryManager::sMaintenanceNeeded;
// #pragma mark - kernel tracing // #pragma mark - kernel tracing
@@ -991,6 +998,8 @@ MemoryManager::_GetChunks(MetaChunkList* metaChunkList, size_t chunkSize,
// update the free range // update the free range
metaChunk->firstFreeChunk += chunkCount; metaChunk->firstFreeChunk += chunkCount;
PARANOID_CHECKS_ONLY(_CheckMetaChunk(metaChunk));
_chunk = firstChunk; _chunk = firstChunk;
_metaChunk = metaChunk; _metaChunk = metaChunk;
@@ -1037,6 +1046,8 @@ MemoryManager::_GetChunk(MetaChunkList* metaChunkList, size_t chunkSize,
_chunk = _pop(metaChunk->freeChunks); _chunk = _pop(metaChunk->freeChunks);
_metaChunk = metaChunk; _metaChunk = metaChunk;
_chunk->reference = 1;
// update the free range // update the free range
uint32 chunkIndex = _chunk - metaChunk->chunks; uint32 chunkIndex = _chunk - metaChunk->chunks;
if (chunkIndex >= metaChunk->firstFreeChunk if (chunkIndex >= metaChunk->firstFreeChunk
@@ -1048,6 +1059,8 @@ MemoryManager::_GetChunk(MetaChunkList* metaChunkList, size_t chunkSize,
metaChunk->lastFreeChunk = chunkIndex - 1; metaChunk->lastFreeChunk = chunkIndex - 1;
} }
PARANOID_CHECKS_ONLY(_CheckMetaChunk(metaChunk));
T(AllocateChunk(chunkSize, metaChunk, _chunk)); T(AllocateChunk(chunkSize, metaChunk, _chunk));
return true; return true;
@@ -1072,6 +1085,7 @@ MemoryManager::_FreeChunk(Area* area, MetaChunk* metaChunk, Chunk* chunk,
uint32 chunkIndex = chunk - metaChunk->chunks; uint32 chunkIndex = chunk - metaChunk->chunks;
// free the meta chunk, if it is unused now // free the meta chunk, if it is unused now
PARANOID_CHECKS_ONLY(bool areaDeleted = false;)
ASSERT(metaChunk->usedChunkCount > 0); ASSERT(metaChunk->usedChunkCount > 0);
if (--metaChunk->usedChunkCount == 0) { if (--metaChunk->usedChunkCount == 0) {
T(FreeMetaChunk(metaChunk)); T(FreeMetaChunk(metaChunk));
@@ -1093,8 +1107,10 @@ MemoryManager::_FreeChunk(Area* area, MetaChunk* metaChunk, Chunk* chunk,
// free the area, if it is unused now // free the area, if it is unused now
ASSERT(area->usedMetaChunkCount > 0); ASSERT(area->usedMetaChunkCount > 0);
if (--area->usedMetaChunkCount == 0) if (--area->usedMetaChunkCount == 0) {
_FreeArea(area, false, flags); _FreeArea(area, false, flags);
PARANOID_CHECKS_ONLY(areaDeleted = true;)
}
} else if (metaChunk->usedChunkCount == metaChunk->chunkCount - 1) { } else if (metaChunk->usedChunkCount == metaChunk->chunkCount - 1) {
// the meta chunk was full before -- add it back to its partial chunk // the meta chunk was full before -- add it back to its partial chunk
// list // list
@@ -1125,6 +1141,11 @@ MemoryManager::_FreeChunk(Area* area, MetaChunk* metaChunk, Chunk* chunk,
metaChunk->lastFreeChunk = lastFree; metaChunk->lastFreeChunk = lastFree;
} }
} }
PARANOID_CHECKS_ONLY(
if (!areaDeleted)
_CheckMetaChunk(metaChunk);
)
} }
@@ -1150,6 +1171,8 @@ MemoryManager::_PrepareMetaChunk(MetaChunk* metaChunk, size_t chunkSize)
metaChunk->firstFreeChunk = 0; metaChunk->firstFreeChunk = 0;
metaChunk->lastFreeChunk = metaChunk->chunkCount - 1; metaChunk->lastFreeChunk = metaChunk->chunkCount - 1;
PARANOID_CHECKS_ONLY(_CheckMetaChunk(metaChunk));
} }
@@ -1495,6 +1518,140 @@ MemoryManager::_RequestMaintenance()
} }
/*static*/ bool
MemoryManager::_IsChunkInFreeList(const MetaChunk* metaChunk,
const Chunk* chunk)
{
Chunk* freeChunk = metaChunk->freeChunks;
while (freeChunk != NULL) {
if (freeChunk == chunk)
return true;
freeChunk = freeChunk->next;
}
return false;
}
#if DEBUG_SLAB_MEMORY_MANAGER_PARANOID_CHECKS
/*static*/ void
MemoryManager::_CheckMetaChunk(MetaChunk* metaChunk)
{
Area* area = metaChunk->GetArea();
int32 metaChunkIndex = metaChunk - area->metaChunks;
if (metaChunkIndex < 0 || metaChunkIndex >= SLAB_META_CHUNKS_PER_AREA) {
panic("invalid meta chunk %p!", metaChunk);
return;
}
switch (metaChunk->chunkSize) {
case 0:
// unused
return;
case SLAB_CHUNK_SIZE_SMALL:
case SLAB_CHUNK_SIZE_MEDIUM:
case SLAB_CHUNK_SIZE_LARGE:
break;
default:
panic("meta chunk %p has invalid chunk size: %" B_PRIuSIZE,
metaChunk, metaChunk->chunkSize);
return;
}
if (metaChunk->totalSize > SLAB_CHUNK_SIZE_LARGE) {
panic("meta chunk %p has invalid total size: %" B_PRIuSIZE,
metaChunk, metaChunk->totalSize);
return;
}
addr_t expectedBase = (addr_t)area + metaChunkIndex * SLAB_CHUNK_SIZE_LARGE;
if (metaChunk->chunkBase < expectedBase
|| metaChunk->chunkBase - expectedBase + metaChunk->totalSize
> SLAB_CHUNK_SIZE_LARGE) {
panic("meta chunk %p has invalid base address: %" B_PRIxADDR, metaChunk,
metaChunk->chunkBase);
return;
}
if (metaChunk->chunkCount != metaChunk->totalSize / metaChunk->chunkSize) {
panic("meta chunk %p has invalid chunk count: %u", metaChunk,
metaChunk->chunkCount);
return;
}
if (metaChunk->usedChunkCount > metaChunk->chunkCount) {
panic("meta chunk %p has invalid unused chunk count: %u", metaChunk,
metaChunk->usedChunkCount);
return;
}
if (metaChunk->firstFreeChunk > metaChunk->chunkCount) {
panic("meta chunk %p has invalid first free chunk: %u", metaChunk,
metaChunk->firstFreeChunk);
return;
}
if (metaChunk->lastFreeChunk >= metaChunk->chunkCount) {
panic("meta chunk %p has invalid last free chunk: %u", metaChunk,
metaChunk->lastFreeChunk);
return;
}
// check free list for structural sanity
uint32 freeChunks = 0;
for (Chunk* chunk = metaChunk->freeChunks; chunk != NULL;
chunk = chunk->next) {
if ((addr_t)chunk % sizeof(Chunk) != 0 || chunk < metaChunk->chunks
|| chunk >= metaChunk->chunks + metaChunk->chunkCount) {
panic("meta chunk %p has invalid element in free list, chunk: %p",
metaChunk, chunk);
return;
}
if (++freeChunks > metaChunk->chunkCount) {
panic("meta chunk %p has cyclic free list", metaChunk);
return;
}
}
if (freeChunks + metaChunk->usedChunkCount > metaChunk->chunkCount) {
panic("meta chunk %p has mismatching free/used chunk counts: total: "
"%u, used: %u, free: %" B_PRIu32, metaChunk, metaChunk->chunkCount,
metaChunk->usedChunkCount, freeChunks);
return;
}
// count used chunks by looking at their reference/next field
uint32 usedChunks = 0;
for (uint32 i = 0; i < metaChunk->chunkCount; i++) {
if (!_IsChunkFree(metaChunk, metaChunk->chunks + i))
usedChunks++;
}
if (usedChunks != metaChunk->usedChunkCount) {
panic("meta chunk %p has used chunks that appear free: total: "
"%u, used: %u, appearing used: %" B_PRIu32, metaChunk,
metaChunk->chunkCount, metaChunk->usedChunkCount, usedChunks);
return;
}
// check free range
for (uint32 i = metaChunk->firstFreeChunk; i < metaChunk->lastFreeChunk;
i++) {
if (!_IsChunkFree(metaChunk, metaChunk->chunks + i)) {
panic("meta chunk %p has used chunk in free range, chunk: %p (%"
B_PRIu32 ", free range: %u - %u)", metaChunk,
metaChunk->chunks + i, i, metaChunk->firstFreeChunk,
metaChunk->lastFreeChunk);
return;
}
}
}
#endif // DEBUG_SLAB_MEMORY_MANAGER_PARANOID_CHECKS
/*static*/ int /*static*/ int
MemoryManager::_DumpRawAllocations(int argc, char** argv) MemoryManager::_DumpRawAllocations(int argc, char** argv)
{ {
@@ -1585,8 +1742,14 @@ MemoryManager::_DumpMetaChunk(MetaChunk* metaChunk, bool printChunks,
Chunk* chunk = metaChunk->chunks + i; Chunk* chunk = metaChunk->chunks + i;
// skip free chunks // skip free chunks
if (_IsChunkFree(metaChunk, chunk)) if (_IsChunkFree(metaChunk, chunk)) {
if (!_IsChunkInFreeList(metaChunk, chunk)) {
kprintf("%5" B_PRIu32 " %p appears free, but isn't in free "
"list!\n", i, (void*)_ChunkAddress(metaChunk, chunk));
}
continue; continue;
}
addr_t reference = chunk->reference; addr_t reference = chunk->reference;
if ((reference & 1) == 0) { if ((reference & 1) == 0) {
+4 -1
View File
@@ -24,7 +24,7 @@ struct VMArea;
#define SLAB_CHUNK_SIZE_MEDIUM (16 * B_PAGE_SIZE) #define SLAB_CHUNK_SIZE_MEDIUM (16 * B_PAGE_SIZE)
#define SLAB_CHUNK_SIZE_LARGE (128 * B_PAGE_SIZE) #define SLAB_CHUNK_SIZE_LARGE (128 * B_PAGE_SIZE)
#define SLAB_AREA_SIZE (2048 * B_PAGE_SIZE) #define SLAB_AREA_SIZE (2048 * B_PAGE_SIZE)
// TODO: These sizes have been chosen with 4 KB pages is mind. // TODO: These sizes have been chosen with 4 KB pages in mind.
#define SLAB_META_CHUNKS_PER_AREA (SLAB_AREA_SIZE / SLAB_CHUNK_SIZE_LARGE) #define SLAB_META_CHUNKS_PER_AREA (SLAB_AREA_SIZE / SLAB_CHUNK_SIZE_LARGE)
#define SLAB_SMALL_CHUNKS_PER_AREA (SLAB_AREA_SIZE / SLAB_CHUNK_SIZE_SMALL) #define SLAB_SMALL_CHUNKS_PER_AREA (SLAB_AREA_SIZE / SLAB_CHUNK_SIZE_SMALL)
@@ -164,6 +164,9 @@ private:
const Chunk* chunk); const Chunk* chunk);
static bool _IsChunkFree(const MetaChunk* metaChunk, static bool _IsChunkFree(const MetaChunk* metaChunk,
const Chunk* chunk); const Chunk* chunk);
static bool _IsChunkInFreeList(const MetaChunk* metaChunk,
const Chunk* chunk);
static void _CheckMetaChunk(MetaChunk* metaChunk);
static int _DumpRawAllocations(int argc, char** argv); static int _DumpRawAllocations(int argc, char** argv);
static void _PrintMetaChunkTableHeader(bool printChunks); static void _PrintMetaChunkTableHeader(bool printChunks);