* 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:
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user