Line data Source code
1 : /*------------------------------------------------------------------------- 2 : * 3 : * alignedalloc.c 4 : * Allocator functions to implement palloc_aligned 5 : * 6 : * This is not a fully-fledged MemoryContext type as there is no means to 7 : * create a MemoryContext of this type. The code here only serves to allow 8 : * operations such as pfree() and repalloc() to work correctly on a memory 9 : * chunk that was allocated by palloc_aligned(). 10 : * 11 : * Portions Copyright (c) 2022-2025, PostgreSQL Global Development Group 12 : * 13 : * IDENTIFICATION 14 : * src/backend/utils/mmgr/alignedalloc.c 15 : * 16 : *------------------------------------------------------------------------- 17 : */ 18 : 19 : #include "postgres.h" 20 : 21 : #include "utils/memdebug.h" 22 : #include "utils/memutils_memorychunk.h" 23 : 24 : /* 25 : * AlignedAllocFree 26 : * Frees allocated memory; memory is removed from its owning context. 27 : */ 28 : void 29 329138 : AlignedAllocFree(void *pointer) 30 : { 31 329138 : MemoryChunk *chunk = PointerGetMemoryChunk(pointer); 32 : void *unaligned; 33 : 34 : VALGRIND_MAKE_MEM_DEFINED(chunk, sizeof(MemoryChunk)); 35 : 36 : Assert(!MemoryChunkIsExternal(chunk)); 37 : 38 : /* obtain the original (unaligned) allocated pointer */ 39 329138 : unaligned = MemoryChunkGetBlock(chunk); 40 : 41 : #ifdef MEMORY_CONTEXT_CHECKING 42 : /* Test for someone scribbling on unused space in chunk */ 43 : if (!sentinel_ok(pointer, chunk->requested_size)) 44 : elog(WARNING, "detected write past chunk end in %s %p", 45 : GetMemoryChunkContext(unaligned)->name, chunk); 46 : #endif 47 : 48 : /* 49 : * Create a dummy vchunk covering the start of the unaligned chunk, but 50 : * not overlapping the aligned chunk. This will be freed while pfree'ing 51 : * the unaligned chunk, keeping Valgrind happy. Then when we return to 52 : * the outer pfree, that will clean up the vchunk for the aligned chunk. 53 : */ 54 : VALGRIND_MEMPOOL_ALLOC(GetMemoryChunkContext(unaligned), unaligned, 55 : (char *) pointer - (char *) unaligned); 56 : 57 : /* Recursively pfree the unaligned chunk */ 58 329138 : pfree(unaligned); 59 329138 : } 60 : 61 : /* 62 : * AlignedAllocRealloc 63 : * Change the allocated size of a chunk and return possibly a different 64 : * pointer to a memory address aligned to the same boundary as the 65 : * originally requested alignment. The contents of 'pointer' will be 66 : * copied into the returned pointer up until 'size'. Any additional 67 : * memory will be uninitialized. 68 : */ 69 : void * 70 0 : AlignedAllocRealloc(void *pointer, Size size, int flags) 71 : { 72 0 : MemoryChunk *redirchunk = PointerGetMemoryChunk(pointer); 73 : Size alignto; 74 : void *unaligned; 75 : MemoryContext ctx; 76 : Size old_size; 77 : void *newptr; 78 : 79 : VALGRIND_MAKE_MEM_DEFINED(redirchunk, sizeof(MemoryChunk)); 80 : 81 0 : alignto = MemoryChunkGetValue(redirchunk); 82 0 : unaligned = MemoryChunkGetBlock(redirchunk); 83 : 84 : /* sanity check this is a power of 2 value */ 85 : Assert((alignto & (alignto - 1)) == 0); 86 : 87 : /* 88 : * Determine the size of the original allocation. We can't determine this 89 : * exactly as GetMemoryChunkSpace() returns the total space used for the 90 : * allocation, which for contexts like aset includes rounding up to the 91 : * next power of 2. However, this value is just used to memcpy() the old 92 : * data into the new allocation, so we only need to concern ourselves with 93 : * not reading beyond the end of the original allocation's memory. The 94 : * drawback here is that we may copy more bytes than we need to, which 95 : * only amounts to wasted effort. We can safely subtract the extra bytes 96 : * that we requested to allow us to align the pointer. We must also 97 : * subtract the space for the unaligned pointer's MemoryChunk since 98 : * GetMemoryChunkSpace should have included that. This does assume that 99 : * all context types use MemoryChunk as a chunk header. 100 : */ 101 0 : old_size = GetMemoryChunkSpace(unaligned) - 102 : PallocAlignedExtraBytes(alignto) - sizeof(MemoryChunk); 103 : 104 : #ifdef MEMORY_CONTEXT_CHECKING 105 : /* check that GetMemoryChunkSpace returned something realistic */ 106 : Assert(old_size >= redirchunk->requested_size); 107 : #endif 108 : 109 : /* 110 : * To keep things simple, we always allocate a new aligned chunk and copy 111 : * data into it. Because of the above inaccuracy, this may end in copying 112 : * more data than was in the original allocation request size, but that 113 : * should be OK. 114 : */ 115 0 : ctx = GetMemoryChunkContext(unaligned); 116 0 : newptr = MemoryContextAllocAligned(ctx, size, alignto, flags); 117 : 118 : /* Cope cleanly with OOM */ 119 0 : if (unlikely(newptr == NULL)) 120 : { 121 : VALGRIND_MAKE_MEM_NOACCESS(redirchunk, sizeof(MemoryChunk)); 122 0 : return MemoryContextAllocationFailure(ctx, size, flags); 123 : } 124 : 125 : /* 126 : * We may memcpy more than the original allocation request size, which 127 : * would result in trying to copy trailing bytes that the original 128 : * MemoryContextAllocAligned call marked NOACCESS. So we must mark the 129 : * entire old_size as defined. That's slightly annoying, but probably not 130 : * worth improving. 131 : */ 132 : VALGRIND_MAKE_MEM_DEFINED(pointer, old_size); 133 0 : memcpy(newptr, pointer, Min(size, old_size)); 134 : 135 : /* 136 : * Create a dummy vchunk covering the start of the old unaligned chunk, 137 : * but not overlapping the aligned chunk. This will be freed while 138 : * pfree'ing the old unaligned chunk, keeping Valgrind happy. Then when 139 : * we return to repalloc, it will move the vchunk for the aligned chunk. 140 : */ 141 : VALGRIND_MEMPOOL_ALLOC(ctx, unaligned, 142 : (char *) pointer - (char *) unaligned); 143 : 144 0 : pfree(unaligned); 145 : 146 0 : return newptr; 147 : } 148 : 149 : /* 150 : * AlignedAllocGetChunkContext 151 : * Return the MemoryContext that 'pointer' belongs to. 152 : */ 153 : MemoryContext 154 0 : AlignedAllocGetChunkContext(void *pointer) 155 : { 156 0 : MemoryChunk *redirchunk = PointerGetMemoryChunk(pointer); 157 : MemoryContext cxt; 158 : 159 : VALGRIND_MAKE_MEM_DEFINED(redirchunk, sizeof(MemoryChunk)); 160 : 161 : Assert(!MemoryChunkIsExternal(redirchunk)); 162 : 163 0 : cxt = GetMemoryChunkContext(MemoryChunkGetBlock(redirchunk)); 164 : 165 : VALGRIND_MAKE_MEM_NOACCESS(redirchunk, sizeof(MemoryChunk)); 166 : 167 0 : return cxt; 168 : } 169 : 170 : /* 171 : * AlignedAllocGetChunkSpace 172 : * Given a currently-allocated chunk, determine the total space 173 : * it occupies (including all memory-allocation overhead). 174 : */ 175 : Size 176 0 : AlignedAllocGetChunkSpace(void *pointer) 177 : { 178 0 : MemoryChunk *redirchunk = PointerGetMemoryChunk(pointer); 179 : void *unaligned; 180 : Size space; 181 : 182 : VALGRIND_MAKE_MEM_DEFINED(redirchunk, sizeof(MemoryChunk)); 183 : 184 0 : unaligned = MemoryChunkGetBlock(redirchunk); 185 0 : space = GetMemoryChunkSpace(unaligned); 186 : 187 : VALGRIND_MAKE_MEM_NOACCESS(redirchunk, sizeof(MemoryChunk)); 188 : 189 0 : return space; 190 : }