LCOV - code coverage report
Current view: top level - src/backend/utils/mmgr - slab.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 0 186 0.0 %
Date: 2017-09-29 13:40:31 Functions: 0 11 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * slab.c
       4             :  *    SLAB allocator definitions.
       5             :  *
       6             :  * SLAB is a MemoryContext implementation designed for cases where large
       7             :  * numbers of equally-sized objects are allocated (and freed).
       8             :  *
       9             :  *
      10             :  * Portions Copyright (c) 2017, PostgreSQL Global Development Group
      11             :  *
      12             :  * IDENTIFICATION
      13             :  *    src/backend/utils/mmgr/slab.c
      14             :  *
      15             :  *
      16             :  * NOTE:
      17             :  *  The constant allocation size allows significant simplification and various
      18             :  *  optimizations over more general purpose allocators. The blocks are carved
      19             :  *  into chunks of exactly the right size (plus alignment), not wasting any
      20             :  *  memory.
      21             :  *
      22             :  *  The information about free chunks is maintained both at the block level and
      23             :  *  global (context) level. This is possible as the chunk size (and thus also
      24             :  *  the number of chunks per block) is fixed.
      25             :  *
      26             :  *  On each block, free chunks are tracked in a simple linked list. Contents
      27             :  *  of free chunks is replaced with an index of the next free chunk, forming
      28             :  *  a very simple linked list. Each block also contains a counter of free
      29             :  *  chunks. Combined with the local block-level freelist, it makes it trivial
      30             :  *  to eventually free the whole block.
      31             :  *
      32             :  *  At the context level, we use 'freelist' to track blocks ordered by number
      33             :  *  of free chunks, starting with blocks having a single allocated chunk, and
      34             :  *  with completely full blocks on the tail.
      35             :  *
      36             :  *  This also allows various optimizations - for example when searching for
      37             :  *  free chunk, the allocator reuses space from the fullest blocks first, in
      38             :  *  the hope that some of the less full blocks will get completely empty (and
      39             :  *  returned back to the OS).
      40             :  *
      41             :  *  For each block, we maintain pointer to the first free chunk - this is quite
      42             :  *  cheap and allows us to skip all the preceding used chunks, eliminating
      43             :  *  a significant number of lookups in many common usage patters. In the worst
      44             :  *  case this performs as if the pointer was not maintained.
      45             :  *
      46             :  *  We cache the freelist index for the blocks with the fewest free chunks
      47             :  *  (minFreeChunks), so that we don't have to search the freelist on every
      48             :  *  SlabAlloc() call, which is quite expensive.
      49             :  *
      50             :  *-------------------------------------------------------------------------
      51             :  */
      52             : 
      53             : #include "postgres.h"
      54             : 
      55             : #include "utils/memdebug.h"
      56             : #include "utils/memutils.h"
      57             : #include "lib/ilist.h"
      58             : 
      59             : 
      60             : /*
      61             :  * SlabContext is a specialized implementation of MemoryContext.
      62             :  */
      63             : typedef struct SlabContext
      64             : {
      65             :     MemoryContextData header;   /* Standard memory-context fields */
      66             :     /* Allocation parameters for this context: */
      67             :     Size        chunkSize;      /* chunk size */
      68             :     Size        fullChunkSize;  /* chunk size including header and alignment */
      69             :     Size        blockSize;      /* block size */
      70             :     int         chunksPerBlock; /* number of chunks per block */
      71             :     int         minFreeChunks;  /* min number of free chunks in any block */
      72             :     int         nblocks;        /* number of blocks allocated */
      73             :     /* blocks with free space, grouped by number of free chunks: */
      74             :     dlist_head  freelist[FLEXIBLE_ARRAY_MEMBER];
      75             : } SlabContext;
      76             : 
      77             : /*
      78             :  * SlabBlock
      79             :  *      Structure of a single block in SLAB allocator.
      80             :  *
      81             :  * node: doubly-linked list of blocks in global freelist
      82             :  * nfree: number of free chunks in this block
      83             :  * firstFreeChunk: index of the first free chunk
      84             :  */
      85             : typedef struct SlabBlock
      86             : {
      87             :     dlist_node  node;           /* doubly-linked list */
      88             :     int         nfree;          /* number of free chunks */
      89             :     int         firstFreeChunk; /* index of the first free chunk in the block */
      90             : } SlabBlock;
      91             : 
      92             : /*
      93             :  * SlabChunk
      94             :  *      The prefix of each piece of memory in an SlabBlock
      95             :  */
      96             : typedef struct SlabChunk
      97             : {
      98             :     /* block owning this chunk */
      99             :     void       *block;
     100             :     SlabContext *slab;          /* owning context */
     101             :     /* there must not be any padding to reach a MAXALIGN boundary here! */
     102             : } SlabChunk;
     103             : 
     104             : 
     105             : #define SlabPointerGetChunk(ptr)    \
     106             :     ((SlabChunk *)(((char *)(ptr)) - sizeof(SlabChunk)))
     107             : #define SlabChunkGetPointer(chk)    \
     108             :     ((void *)(((char *)(chk)) + sizeof(SlabChunk)))
     109             : #define SlabBlockGetChunk(slab, block, idx) \
     110             :     ((SlabChunk *) ((char *) (block) + sizeof(SlabBlock)    \
     111             :                     + (idx * slab->fullChunkSize)))
     112             : #define SlabBlockStart(block)   \
     113             :     ((char *) block + sizeof(SlabBlock))
     114             : #define SlabChunkIndex(slab, block, chunk)  \
     115             :     (((char *) chunk - SlabBlockStart(block)) / slab->fullChunkSize)
     116             : 
     117             : /*
     118             :  * These functions implement the MemoryContext API for Slab contexts.
     119             :  */
     120             : static void *SlabAlloc(MemoryContext context, Size size);
     121             : static void SlabFree(MemoryContext context, void *pointer);
     122             : static void *SlabRealloc(MemoryContext context, void *pointer, Size size);
     123             : static void SlabInit(MemoryContext context);
     124             : static void SlabReset(MemoryContext context);
     125             : static void SlabDelete(MemoryContext context);
     126             : static Size SlabGetChunkSpace(MemoryContext context, void *pointer);
     127             : static bool SlabIsEmpty(MemoryContext context);
     128             : static void SlabStats(MemoryContext context, int level, bool print,
     129             :           MemoryContextCounters *totals);
     130             : #ifdef MEMORY_CONTEXT_CHECKING
     131             : static void SlabCheck(MemoryContext context);
     132             : #endif
     133             : 
     134             : /*
     135             :  * This is the virtual function table for Slab contexts.
     136             :  */
     137             : static MemoryContextMethods SlabMethods = {
     138             :     SlabAlloc,
     139             :     SlabFree,
     140             :     SlabRealloc,
     141             :     SlabInit,
     142             :     SlabReset,
     143             :     SlabDelete,
     144             :     SlabGetChunkSpace,
     145             :     SlabIsEmpty,
     146             :     SlabStats
     147             : #ifdef MEMORY_CONTEXT_CHECKING
     148             :     ,SlabCheck
     149             : #endif
     150             : };
     151             : 
     152             : /* ----------
     153             :  * Debug macros
     154             :  * ----------
     155             :  */
     156             : #ifdef HAVE_ALLOCINFO
     157             : #define SlabFreeInfo(_cxt, _chunk) \
     158             :             fprintf(stderr, "SlabFree: %s: %p, %zu\n", \
     159             :                 (_cxt)->header.name, (_chunk), (_chunk)->header.size)
     160             : #define SlabAllocInfo(_cxt, _chunk) \
     161             :             fprintf(stderr, "SlabAlloc: %s: %p, %zu\n", \
     162             :                 (_cxt)->header.name, (_chunk), (_chunk)->header.size)
     163             : #else
     164             : #define SlabFreeInfo(_cxt, _chunk)
     165             : #define SlabAllocInfo(_cxt, _chunk)
     166             : #endif
     167             : 
     168             : 
     169             : /*
     170             :  * SlabContextCreate
     171             :  *      Create a new Slab context.
     172             :  *
     173             :  * parent: parent context, or NULL if top-level context
     174             :  * name: name of context (for debugging --- string will be copied)
     175             :  * blockSize: allocation block size
     176             :  * chunkSize: allocation chunk size
     177             :  *
     178             :  * The chunkSize may not exceed:
     179             :  *      MAXALIGN_DOWN(SIZE_MAX) - MAXALIGN(sizeof(SlabBlock)) - SLAB_CHUNKHDRSZ
     180             :  *
     181             :  */
     182             : MemoryContext
     183           0 : SlabContextCreate(MemoryContext parent,
     184             :                   const char *name,
     185             :                   Size blockSize,
     186             :                   Size chunkSize)
     187             : {
     188             :     int         chunksPerBlock;
     189             :     Size        fullChunkSize;
     190             :     Size        freelistSize;
     191             :     SlabContext *slab;
     192             : 
     193             :     StaticAssertStmt(offsetof(SlabChunk, slab) + sizeof(MemoryContext) ==
     194             :                      MAXALIGN(sizeof(SlabChunk)),
     195             :                      "padding calculation in SlabChunk is wrong");
     196             : 
     197             :     /* Make sure the linked list node fits inside a freed chunk */
     198           0 :     if (chunkSize < sizeof(int))
     199           0 :         chunkSize = sizeof(int);
     200             : 
     201             :     /* chunk, including SLAB header (both addresses nicely aligned) */
     202           0 :     fullChunkSize = MAXALIGN(sizeof(SlabChunk) + MAXALIGN(chunkSize));
     203             : 
     204             :     /* Make sure the block can store at least one chunk. */
     205           0 :     if (blockSize - sizeof(SlabBlock) < fullChunkSize)
     206           0 :         elog(ERROR, "block size %zu for slab is too small for %zu chunks",
     207             :              blockSize, chunkSize);
     208             : 
     209             :     /* Compute maximum number of chunks per block */
     210           0 :     chunksPerBlock = (blockSize - sizeof(SlabBlock)) / fullChunkSize;
     211             : 
     212             :     /* The freelist starts with 0, ends with chunksPerBlock. */
     213           0 :     freelistSize = sizeof(dlist_head) * (chunksPerBlock + 1);
     214             : 
     215             :     /* if we can't fit at least one chunk into the block, we're hosed */
     216           0 :     Assert(chunksPerBlock > 0);
     217             : 
     218             :     /* make sure the chunks actually fit on the block   */
     219           0 :     Assert((fullChunkSize * chunksPerBlock) + sizeof(SlabBlock) <= blockSize);
     220             : 
     221             :     /* Do the type-independent part of context creation */
     222           0 :     slab = (SlabContext *)
     223           0 :         MemoryContextCreate(T_SlabContext,
     224             :                             (offsetof(SlabContext, freelist) + freelistSize),
     225             :                             &SlabMethods,
     226             :                             parent,
     227             :                             name);
     228             : 
     229           0 :     slab->blockSize = blockSize;
     230           0 :     slab->chunkSize = chunkSize;
     231           0 :     slab->fullChunkSize = fullChunkSize;
     232           0 :     slab->chunksPerBlock = chunksPerBlock;
     233           0 :     slab->nblocks = 0;
     234           0 :     slab->minFreeChunks = 0;
     235             : 
     236           0 :     return (MemoryContext) slab;
     237             : }
     238             : 
     239             : /*
     240             :  * SlabInit
     241             :  *      Context-type-specific initialization routine.
     242             :  */
     243             : static void
     244           0 : SlabInit(MemoryContext context)
     245             : {
     246             :     int         i;
     247           0 :     SlabContext *slab = castNode(SlabContext, context);
     248             : 
     249           0 :     Assert(slab);
     250             : 
     251             :     /* initialize the freelist slots */
     252           0 :     for (i = 0; i < (slab->chunksPerBlock + 1); i++)
     253           0 :         dlist_init(&slab->freelist[i]);
     254           0 : }
     255             : 
     256             : /*
     257             :  * SlabReset
     258             :  *      Frees all memory which is allocated in the given set.
     259             :  *
     260             :  * The code simply frees all the blocks in the context - we don't keep any
     261             :  * keeper blocks or anything like that.
     262             :  */
     263             : static void
     264           0 : SlabReset(MemoryContext context)
     265             : {
     266             :     int         i;
     267           0 :     SlabContext *slab = castNode(SlabContext, context);
     268             : 
     269           0 :     Assert(slab);
     270             : 
     271             : #ifdef MEMORY_CONTEXT_CHECKING
     272             :     /* Check for corruption and leaks before freeing */
     273           0 :     SlabCheck(context);
     274             : #endif
     275             : 
     276             :     /* walk over freelists and free the blocks */
     277           0 :     for (i = 0; i <= slab->chunksPerBlock; i++)
     278             :     {
     279             :         dlist_mutable_iter miter;
     280             : 
     281           0 :         dlist_foreach_modify(miter, &slab->freelist[i])
     282             :         {
     283           0 :             SlabBlock  *block = dlist_container(SlabBlock, node, miter.cur);
     284             : 
     285           0 :             dlist_delete(miter.cur);
     286             : 
     287             : #ifdef CLOBBER_FREED_MEMORY
     288           0 :             wipe_mem(block, slab->blockSize);
     289             : #endif
     290           0 :             free(block);
     291           0 :             slab->nblocks--;
     292             :         }
     293             :     }
     294             : 
     295           0 :     slab->minFreeChunks = 0;
     296             : 
     297           0 :     Assert(slab->nblocks == 0);
     298           0 : }
     299             : 
     300             : /*
     301             :  * SlabDelete
     302             :  *      Frees all memory which is allocated in the given slab, in preparation
     303             :  *      for deletion of the slab. We simply call SlabReset().
     304             :  */
     305             : static void
     306           0 : SlabDelete(MemoryContext context)
     307             : {
     308             :     /* just reset the context */
     309           0 :     SlabReset(context);
     310           0 : }
     311             : 
     312             : /*
     313             :  * SlabAlloc
     314             :  *      Returns pointer to allocated memory of given size or NULL if
     315             :  *      request could not be completed; memory is added to the slab.
     316             :  */
     317             : static void *
     318           0 : SlabAlloc(MemoryContext context, Size size)
     319             : {
     320           0 :     SlabContext *slab = castNode(SlabContext, context);
     321             :     SlabBlock  *block;
     322             :     SlabChunk  *chunk;
     323             :     int         idx;
     324             : 
     325           0 :     Assert(slab);
     326             : 
     327           0 :     Assert((slab->minFreeChunks >= 0) &&
     328             :            (slab->minFreeChunks < slab->chunksPerBlock));
     329             : 
     330             :     /* make sure we only allow correct request size */
     331           0 :     if (size != slab->chunkSize)
     332           0 :         elog(ERROR, "unexpected alloc chunk size %zu (expected %zu)",
     333             :              size, slab->chunkSize);
     334             : 
     335             :     /*
     336             :      * If there are no free chunks in any existing block, create a new block
     337             :      * and put it to the last freelist bucket.
     338             :      *
     339             :      * slab->minFreeChunks == 0 means there are no blocks with free chunks,
     340             :      * thanks to how minFreeChunks is updated at the end of SlabAlloc().
     341             :      */
     342           0 :     if (slab->minFreeChunks == 0)
     343             :     {
     344           0 :         block = (SlabBlock *) malloc(slab->blockSize);
     345             : 
     346           0 :         if (block == NULL)
     347           0 :             return NULL;
     348             : 
     349           0 :         block->nfree = slab->chunksPerBlock;
     350           0 :         block->firstFreeChunk = 0;
     351             : 
     352             :         /*
     353             :          * Put all the chunks on a freelist. Walk the chunks and point each
     354             :          * one to the next one.
     355             :          */
     356           0 :         for (idx = 0; idx < slab->chunksPerBlock; idx++)
     357             :         {
     358           0 :             chunk = SlabBlockGetChunk(slab, block, idx);
     359           0 :             *(int32 *) SlabChunkGetPointer(chunk) = (idx + 1);
     360             :         }
     361             : 
     362             :         /*
     363             :          * And add it to the last freelist with all chunks empty.
     364             :          *
     365             :          * We know there are no blocks in the freelist, otherwise we wouldn't
     366             :          * need a new block.
     367             :          */
     368           0 :         Assert(dlist_is_empty(&slab->freelist[slab->chunksPerBlock]));
     369             : 
     370           0 :         dlist_push_head(&slab->freelist[slab->chunksPerBlock], &block->node);
     371             : 
     372           0 :         slab->minFreeChunks = slab->chunksPerBlock;
     373           0 :         slab->nblocks += 1;
     374             :     }
     375             : 
     376             :     /* grab the block from the freelist (even the new block is there) */
     377           0 :     block = dlist_head_element(SlabBlock, node,
     378             :                                &slab->freelist[slab->minFreeChunks]);
     379             : 
     380             :     /* make sure we actually got a valid block, with matching nfree */
     381           0 :     Assert(block != NULL);
     382           0 :     Assert(slab->minFreeChunks == block->nfree);
     383           0 :     Assert(block->nfree > 0);
     384             : 
     385             :     /* we know index of the first free chunk in the block */
     386           0 :     idx = block->firstFreeChunk;
     387             : 
     388             :     /* make sure the chunk index is valid, and that it's marked as empty */
     389           0 :     Assert((idx >= 0) && (idx < slab->chunksPerBlock));
     390             : 
     391             :     /* compute the chunk location block start (after the block header) */
     392           0 :     chunk = SlabBlockGetChunk(slab, block, idx);
     393             : 
     394             :     /*
     395             :      * Update the block nfree count, and also the minFreeChunks as we've
     396             :      * decreased nfree for a block with the minimum number of free chunks
     397             :      * (because that's how we chose the block).
     398             :      */
     399           0 :     block->nfree--;
     400           0 :     slab->minFreeChunks = block->nfree;
     401             : 
     402             :     /*
     403             :      * Remove the chunk from the freelist head. The index of the next free
     404             :      * chunk is stored in the chunk itself.
     405             :      */
     406             :     VALGRIND_MAKE_MEM_DEFINED(SlabChunkGetPointer(chunk), sizeof(int32));
     407           0 :     block->firstFreeChunk = *(int32 *) SlabChunkGetPointer(chunk);
     408             : 
     409           0 :     Assert(block->firstFreeChunk >= 0);
     410           0 :     Assert(block->firstFreeChunk <= slab->chunksPerBlock);
     411             : 
     412           0 :     Assert((block->nfree != 0 &&
     413             :             block->firstFreeChunk < slab->chunksPerBlock) ||
     414             :            (block->nfree == 0 &&
     415             :             block->firstFreeChunk == slab->chunksPerBlock));
     416             : 
     417             :     /* move the whole block to the right place in the freelist */
     418           0 :     dlist_delete(&block->node);
     419           0 :     dlist_push_head(&slab->freelist[block->nfree], &block->node);
     420             : 
     421             :     /*
     422             :      * And finally update minFreeChunks, i.e. the index to the block with the
     423             :      * lowest number of free chunks. We only need to do that when the block
     424             :      * got full (otherwise we know the current block is the right one). We'll
     425             :      * simply walk the freelist until we find a non-empty entry.
     426             :      */
     427           0 :     if (slab->minFreeChunks == 0)
     428             :     {
     429           0 :         for (idx = 1; idx <= slab->chunksPerBlock; idx++)
     430             :         {
     431           0 :             if (dlist_is_empty(&slab->freelist[idx]))
     432           0 :                 continue;
     433             : 
     434             :             /* found a non-empty freelist */
     435           0 :             slab->minFreeChunks = idx;
     436           0 :             break;
     437             :         }
     438             :     }
     439             : 
     440           0 :     if (slab->minFreeChunks == slab->chunksPerBlock)
     441           0 :         slab->minFreeChunks = 0;
     442             : 
     443             :     /* Prepare to initialize the chunk header. */
     444             :     VALGRIND_MAKE_MEM_UNDEFINED(chunk, sizeof(SlabChunk));
     445             : 
     446           0 :     chunk->block = (void *) block;
     447           0 :     chunk->slab = slab;
     448             : 
     449             : #ifdef MEMORY_CONTEXT_CHECKING
     450             :     /* slab mark to catch clobber of "unused" space */
     451           0 :     if (slab->chunkSize < (slab->fullChunkSize - sizeof(SlabChunk)))
     452             :     {
     453           0 :         set_sentinel(SlabChunkGetPointer(chunk), size);
     454             :         VALGRIND_MAKE_MEM_NOACCESS(((char *) chunk) +
     455             :                                    sizeof(SlabChunk) + slab->chunkSize,
     456             :                                    slab->fullChunkSize -
     457             :                                    (slab->chunkSize + sizeof(SlabChunk)));
     458             :     }
     459             : #endif
     460             : #ifdef RANDOMIZE_ALLOCATED_MEMORY
     461             :     /* fill the allocated space with junk */
     462             :     randomize_mem((char *) SlabChunkGetPointer(chunk), size);
     463             : #endif
     464             : 
     465             :     SlabAllocInfo(slab, chunk);
     466           0 :     return SlabChunkGetPointer(chunk);
     467             : }
     468             : 
     469             : /*
     470             :  * SlabFree
     471             :  *      Frees allocated memory; memory is removed from the slab.
     472             :  */
     473             : static void
     474           0 : SlabFree(MemoryContext context, void *pointer)
     475             : {
     476             :     int         idx;
     477           0 :     SlabContext *slab = castNode(SlabContext, context);
     478           0 :     SlabChunk  *chunk = SlabPointerGetChunk(pointer);
     479           0 :     SlabBlock  *block = chunk->block;
     480             : 
     481             :     SlabFreeInfo(slab, chunk);
     482             : 
     483             : #ifdef MEMORY_CONTEXT_CHECKING
     484             :     /* Test for someone scribbling on unused space in chunk */
     485           0 :     if (slab->chunkSize < (slab->fullChunkSize - sizeof(SlabChunk)))
     486           0 :         if (!sentinel_ok(pointer, slab->chunkSize))
     487           0 :             elog(WARNING, "detected write past chunk end in %s %p",
     488             :                  slab->header.name, chunk);
     489             : #endif
     490             : 
     491             :     /* compute index of the chunk with respect to block start */
     492           0 :     idx = SlabChunkIndex(slab, block, chunk);
     493             : 
     494             :     /* add chunk to freelist, and update block nfree count */
     495           0 :     *(int32 *) pointer = block->firstFreeChunk;
     496           0 :     block->firstFreeChunk = idx;
     497           0 :     block->nfree++;
     498             : 
     499           0 :     Assert(block->nfree > 0);
     500           0 :     Assert(block->nfree <= slab->chunksPerBlock);
     501             : 
     502             : #ifdef CLOBBER_FREED_MEMORY
     503             :     /* XXX don't wipe the int32 index, used for block-level freelist */
     504           0 :     wipe_mem((char *) pointer + sizeof(int32),
     505           0 :              slab->chunkSize - sizeof(int32));
     506             : #endif
     507             : 
     508             :     /* remove the block from a freelist */
     509           0 :     dlist_delete(&block->node);
     510             : 
     511             :     /*
     512             :      * See if we need to update the minFreeChunks field for the slab - we only
     513             :      * need to do that if there the block had that number of free chunks
     514             :      * before we freed one. In that case, we check if there still are blocks
     515             :      * in the original freelist and we either keep the current value (if there
     516             :      * still are blocks) or increment it by one (the new block is still the
     517             :      * one with minimum free chunks).
     518             :      *
     519             :      * The one exception is when the block will get completely free - in that
     520             :      * case we will free it, se we can't use it for minFreeChunks. It however
     521             :      * means there are no more blocks with free chunks.
     522             :      */
     523           0 :     if (slab->minFreeChunks == (block->nfree - 1))
     524             :     {
     525             :         /* Have we removed the last chunk from the freelist? */
     526           0 :         if (dlist_is_empty(&slab->freelist[slab->minFreeChunks]))
     527             :         {
     528             :             /* but if we made the block entirely free, we'll free it */
     529           0 :             if (block->nfree == slab->chunksPerBlock)
     530           0 :                 slab->minFreeChunks = 0;
     531             :             else
     532           0 :                 slab->minFreeChunks++;
     533             :         }
     534             :     }
     535             : 
     536             :     /* If the block is now completely empty, free it. */
     537           0 :     if (block->nfree == slab->chunksPerBlock)
     538             :     {
     539           0 :         free(block);
     540           0 :         slab->nblocks--;
     541             :     }
     542             :     else
     543           0 :         dlist_push_head(&slab->freelist[block->nfree], &block->node);
     544             : 
     545           0 :     Assert(slab->nblocks >= 0);
     546           0 : }
     547             : 
     548             : /*
     549             :  * SlabRealloc
     550             :  *      Change the allocated size of a chunk.
     551             :  *
     552             :  * As Slab is designed for allocating equally-sized chunks of memory, it can't
     553             :  * do an actual chunk size change.  We try to be gentle and allow calls with
     554             :  * exactly the same size, as in that case we can simply return the same
     555             :  * chunk.  When the size differs, we throw an error.
     556             :  *
     557             :  * We could also allow requests with size < chunkSize.  That however seems
     558             :  * rather pointless - Slab is meant for chunks of constant size, and moreover
     559             :  * realloc is usually used to enlarge the chunk.
     560             :  */
     561             : static void *
     562           0 : SlabRealloc(MemoryContext context, void *pointer, Size size)
     563             : {
     564           0 :     SlabContext *slab = castNode(SlabContext, context);
     565             : 
     566           0 :     Assert(slab);
     567             : 
     568             :     /* can't do actual realloc with slab, but let's try to be gentle */
     569           0 :     if (size == slab->chunkSize)
     570           0 :         return pointer;
     571             : 
     572           0 :     elog(ERROR, "slab allocator does not support realloc()");
     573             :     return NULL;                /* keep compiler quiet */
     574             : }
     575             : 
     576             : /*
     577             :  * SlabGetChunkSpace
     578             :  *      Given a currently-allocated chunk, determine the total space
     579             :  *      it occupies (including all memory-allocation overhead).
     580             :  */
     581             : static Size
     582           0 : SlabGetChunkSpace(MemoryContext context, void *pointer)
     583             : {
     584           0 :     SlabContext *slab = castNode(SlabContext, context);
     585             : 
     586           0 :     Assert(slab);
     587             : 
     588           0 :     return slab->fullChunkSize;
     589             : }
     590             : 
     591             : /*
     592             :  * SlabIsEmpty
     593             :  *      Is an Slab empty of any allocated space?
     594             :  */
     595             : static bool
     596           0 : SlabIsEmpty(MemoryContext context)
     597             : {
     598           0 :     SlabContext *slab = castNode(SlabContext, context);
     599             : 
     600           0 :     Assert(slab);
     601             : 
     602           0 :     return (slab->nblocks == 0);
     603             : }
     604             : 
     605             : /*
     606             :  * SlabStats
     607             :  *      Compute stats about memory consumption of an Slab.
     608             :  *
     609             :  * level: recursion level (0 at top level); used for print indentation.
     610             :  * print: true to print stats to stderr.
     611             :  * totals: if not NULL, add stats about this Slab into *totals.
     612             :  */
     613             : static void
     614           0 : SlabStats(MemoryContext context, int level, bool print,
     615             :           MemoryContextCounters *totals)
     616             : {
     617           0 :     SlabContext *slab = castNode(SlabContext, context);
     618           0 :     Size        nblocks = 0;
     619           0 :     Size        freechunks = 0;
     620           0 :     Size        totalspace = 0;
     621           0 :     Size        freespace = 0;
     622             :     int         i;
     623             : 
     624           0 :     Assert(slab);
     625             : 
     626           0 :     for (i = 0; i <= slab->chunksPerBlock; i++)
     627             :     {
     628             :         dlist_iter  iter;
     629             : 
     630           0 :         dlist_foreach(iter, &slab->freelist[i])
     631             :         {
     632           0 :             SlabBlock  *block = dlist_container(SlabBlock, node, iter.cur);
     633             : 
     634           0 :             nblocks++;
     635           0 :             totalspace += slab->blockSize;
     636           0 :             freespace += slab->fullChunkSize * block->nfree;
     637           0 :             freechunks += block->nfree;
     638             :         }
     639             :     }
     640             : 
     641           0 :     if (print)
     642             :     {
     643           0 :         for (i = 0; i < level; i++)
     644           0 :             fprintf(stderr, "  ");
     645           0 :         fprintf(stderr,
     646             :                 "Slab: %s: %zu total in %zd blocks; %zu free (%zd chunks); %zu used\n",
     647             :                 slab->header.name, totalspace, nblocks, freespace, freechunks,
     648             :                 totalspace - freespace);
     649             :     }
     650             : 
     651           0 :     if (totals)
     652             :     {
     653           0 :         totals->nblocks += nblocks;
     654           0 :         totals->freechunks += freechunks;
     655           0 :         totals->totalspace += totalspace;
     656           0 :         totals->freespace += freespace;
     657             :     }
     658           0 : }
     659             : 
     660             : 
     661             : #ifdef MEMORY_CONTEXT_CHECKING
     662             : 
     663             : /*
     664             :  * SlabCheck
     665             :  *      Walk through chunks and check consistency of memory.
     666             :  *
     667             :  * NOTE: report errors as WARNING, *not* ERROR or FATAL.  Otherwise you'll
     668             :  * find yourself in an infinite loop when trouble occurs, because this
     669             :  * routine will be entered again when elog cleanup tries to release memory!
     670             :  */
     671             : static void
     672           0 : SlabCheck(MemoryContext context)
     673             : {
     674             :     int         i;
     675           0 :     SlabContext *slab = castNode(SlabContext, context);
     676           0 :     char       *name = slab->header.name;
     677             :     char       *freechunks;
     678             : 
     679           0 :     Assert(slab);
     680           0 :     Assert(slab->chunksPerBlock > 0);
     681             : 
     682             :     /* bitmap of free chunks on a block */
     683           0 :     freechunks = palloc(slab->chunksPerBlock * sizeof(bool));
     684             : 
     685             :     /* walk all the freelists */
     686           0 :     for (i = 0; i <= slab->chunksPerBlock; i++)
     687             :     {
     688             :         int         j,
     689             :                     nfree;
     690             :         dlist_iter  iter;
     691             : 
     692             :         /* walk all blocks on this freelist */
     693           0 :         dlist_foreach(iter, &slab->freelist[i])
     694             :         {
     695             :             int         idx;
     696           0 :             SlabBlock  *block = dlist_container(SlabBlock, node, iter.cur);
     697             : 
     698             :             /*
     699             :              * Make sure the number of free chunks (in the block header)
     700             :              * matches position in the freelist.
     701             :              */
     702           0 :             if (block->nfree != i)
     703           0 :                 elog(WARNING, "problem in slab %s: number of free chunks %d in block %p does not match freelist %d",
     704             :                      name, block->nfree, block, i);
     705             : 
     706             :             /* reset the bitmap of free chunks for this block */
     707           0 :             memset(freechunks, 0, (slab->chunksPerBlock * sizeof(bool)));
     708           0 :             idx = block->firstFreeChunk;
     709             : 
     710             :             /*
     711             :              * Now walk through the chunks, count the free ones and also
     712             :              * perform some additional checks for the used ones. As the chunk
     713             :              * freelist is stored within the chunks themselves, we have to
     714             :              * walk through the chunks and construct our own bitmap.
     715             :              */
     716             : 
     717           0 :             nfree = 0;
     718           0 :             while (idx < slab->chunksPerBlock)
     719             :             {
     720             :                 SlabChunk  *chunk;
     721             : 
     722             :                 /* count the chunk as free, add it to the bitmap */
     723           0 :                 nfree++;
     724           0 :                 freechunks[idx] = true;
     725             : 
     726             :                 /* read index of the next free chunk */
     727           0 :                 chunk = SlabBlockGetChunk(slab, block, idx);
     728             :                 VALGRIND_MAKE_MEM_DEFINED(SlabChunkGetPointer(chunk), sizeof(int32));
     729           0 :                 idx = *(int32 *) SlabChunkGetPointer(chunk);
     730             :             }
     731             : 
     732           0 :             for (j = 0; j < slab->chunksPerBlock; j++)
     733             :             {
     734             :                 /* non-zero bit in the bitmap means chunk the chunk is used */
     735           0 :                 if (!freechunks[j])
     736             :                 {
     737           0 :                     SlabChunk  *chunk = SlabBlockGetChunk(slab, block, j);
     738             : 
     739             :                     /* chunks have both block and slab pointers, so check both */
     740           0 :                     if (chunk->block != block)
     741           0 :                         elog(WARNING, "problem in slab %s: bogus block link in block %p, chunk %p",
     742             :                              name, block, chunk);
     743             : 
     744           0 :                     if (chunk->slab != slab)
     745           0 :                         elog(WARNING, "problem in slab %s: bogus slab link in block %p, chunk %p",
     746             :                              name, block, chunk);
     747             : 
     748             :                     /* there might be sentinel (thanks to alignment) */
     749           0 :                     if (slab->chunkSize < (slab->fullChunkSize - sizeof(SlabChunk)))
     750           0 :                         if (!sentinel_ok(chunk, slab->chunkSize))
     751           0 :                             elog(WARNING, "problem in slab %s: detected write past chunk end in block %p, chunk %p",
     752             :                                  name, block, chunk);
     753             :                 }
     754             :             }
     755             : 
     756             :             /*
     757             :              * Make sure we got the expected number of free chunks (as tracked
     758             :              * in the block header).
     759             :              */
     760           0 :             if (nfree != block->nfree)
     761           0 :                 elog(WARNING, "problem in slab %s: number of free chunks %d in block %p does not match bitmap %d",
     762             :                      name, block->nfree, block, nfree);
     763             :         }
     764             :     }
     765           0 : }
     766             : 
     767             : #endif                          /* MEMORY_CONTEXT_CHECKING */

Generated by: LCOV version 1.11