diff --git a/build/config_headers/kernel_debug_config.h b/build/config_headers/kernel_debug_config.h index 08eb49ba47..26add874c4 100644 --- a/build/config_headers/kernel_debug_config.h +++ b/build/config_headers/kernel_debug_config.h @@ -101,6 +101,9 @@ // Use the slab allocator as generic memory allocator (malloc()/free()). #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). //#define LIMIT_AVAILABLE_MEMORY 256 diff --git a/src/system/kernel/slab/MemoryManager.cpp b/src/system/kernel/slab/MemoryManager.cpp index 3a25e6f3c4..f8d682396f 100644 --- a/src/system/kernel/slab/MemoryManager.cpp +++ b/src/system/kernel/slab/MemoryManager.cpp @@ -20,6 +20,8 @@ #include #include +#include "kernel_debug_config.h" + #include "ObjectCache.h" #include "slab_private.h" @@ -31,6 +33,12 @@ # define TRACE(x...) do {} while (false) #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"; @@ -51,7 +59,6 @@ MemoryManager::AllocationEntry* MemoryManager::sAllocationEntryDontWait; bool MemoryManager::sMaintenanceNeeded; - // #pragma mark - kernel tracing @@ -991,6 +998,8 @@ MemoryManager::_GetChunks(MetaChunkList* metaChunkList, size_t chunkSize, // update the free range metaChunk->firstFreeChunk += chunkCount; + PARANOID_CHECKS_ONLY(_CheckMetaChunk(metaChunk)); + _chunk = firstChunk; _metaChunk = metaChunk; @@ -1037,6 +1046,8 @@ MemoryManager::_GetChunk(MetaChunkList* metaChunkList, size_t chunkSize, _chunk = _pop(metaChunk->freeChunks); _metaChunk = metaChunk; + _chunk->reference = 1; + // update the free range uint32 chunkIndex = _chunk - metaChunk->chunks; if (chunkIndex >= metaChunk->firstFreeChunk @@ -1048,6 +1059,8 @@ MemoryManager::_GetChunk(MetaChunkList* metaChunkList, size_t chunkSize, metaChunk->lastFreeChunk = chunkIndex - 1; } + PARANOID_CHECKS_ONLY(_CheckMetaChunk(metaChunk)); + T(AllocateChunk(chunkSize, metaChunk, _chunk)); return true; @@ -1072,6 +1085,7 @@ MemoryManager::_FreeChunk(Area* area, MetaChunk* metaChunk, Chunk* chunk, uint32 chunkIndex = chunk - metaChunk->chunks; // free the meta chunk, if it is unused now + PARANOID_CHECKS_ONLY(bool areaDeleted = false;) ASSERT(metaChunk->usedChunkCount > 0); if (--metaChunk->usedChunkCount == 0) { T(FreeMetaChunk(metaChunk)); @@ -1093,8 +1107,10 @@ MemoryManager::_FreeChunk(Area* area, MetaChunk* metaChunk, Chunk* chunk, // free the area, if it is unused now ASSERT(area->usedMetaChunkCount > 0); - if (--area->usedMetaChunkCount == 0) + if (--area->usedMetaChunkCount == 0) { _FreeArea(area, false, flags); + PARANOID_CHECKS_ONLY(areaDeleted = true;) + } } else if (metaChunk->usedChunkCount == metaChunk->chunkCount - 1) { // the meta chunk was full before -- add it back to its partial chunk // list @@ -1125,6 +1141,11 @@ MemoryManager::_FreeChunk(Area* area, MetaChunk* metaChunk, Chunk* chunk, 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->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 MemoryManager::_DumpRawAllocations(int argc, char** argv) { @@ -1585,8 +1742,14 @@ MemoryManager::_DumpMetaChunk(MetaChunk* metaChunk, bool printChunks, Chunk* chunk = metaChunk->chunks + i; // 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; + } addr_t reference = chunk->reference; if ((reference & 1) == 0) { diff --git a/src/system/kernel/slab/MemoryManager.h b/src/system/kernel/slab/MemoryManager.h index c84e74738b..035b0a3d73 100644 --- a/src/system/kernel/slab/MemoryManager.h +++ b/src/system/kernel/slab/MemoryManager.h @@ -24,7 +24,7 @@ struct VMArea; #define SLAB_CHUNK_SIZE_MEDIUM (16 * B_PAGE_SIZE) #define SLAB_CHUNK_SIZE_LARGE (128 * 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_SMALL_CHUNKS_PER_AREA (SLAB_AREA_SIZE / SLAB_CHUNK_SIZE_SMALL) @@ -164,6 +164,9 @@ private: const Chunk* chunk); static bool _IsChunkFree(const MetaChunk* metaChunk, 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 void _PrintMetaChunkTableHeader(bool printChunks);