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-2026, 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 166287 : AlignedAllocFree(void *pointer)
30 : {
31 166287 : 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 166287 : 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 166287 : pfree(unaligned);
59 166287 : }
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 : }
|