/src/postgres/src/backend/utils/mmgr/alignedalloc.c
Line | Count | Source |
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 | | AlignedAllocFree(void *pointer) |
30 | 0 | { |
31 | 0 | MemoryChunk *chunk = PointerGetMemoryChunk(pointer); |
32 | 0 | void *unaligned; |
33 | |
|
34 | 0 | VALGRIND_MAKE_MEM_DEFINED(chunk, sizeof(MemoryChunk)); |
35 | |
|
36 | 0 | Assert(!MemoryChunkIsExternal(chunk)); |
37 | | |
38 | | /* obtain the original (unaligned) allocated pointer */ |
39 | 0 | 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 | 0 | VALGRIND_MEMPOOL_ALLOC(GetMemoryChunkContext(unaligned), unaligned, |
55 | 0 | (char *) pointer - (char *) unaligned); |
56 | | |
57 | | /* Recursively pfree the unaligned chunk */ |
58 | 0 | pfree(unaligned); |
59 | 0 | } |
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 | | AlignedAllocRealloc(void *pointer, Size size, int flags) |
71 | 0 | { |
72 | 0 | MemoryChunk *redirchunk = PointerGetMemoryChunk(pointer); |
73 | 0 | Size alignto; |
74 | 0 | void *unaligned; |
75 | 0 | MemoryContext ctx; |
76 | 0 | Size old_size; |
77 | 0 | void *newptr; |
78 | |
|
79 | 0 | 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 | 0 | 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 | 0 | 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 | 0 | { |
121 | 0 | VALGRIND_MAKE_MEM_NOACCESS(redirchunk, sizeof(MemoryChunk)); |
122 | 0 | return MemoryContextAllocationFailure(ctx, size, flags); |
123 | 0 | } |
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 | 0 | 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 | 0 | VALGRIND_MEMPOOL_ALLOC(ctx, unaligned, |
142 | 0 | (char *) pointer - (char *) unaligned); |
143 | |
|
144 | 0 | pfree(unaligned); |
145 | |
|
146 | 0 | return newptr; |
147 | 0 | } |
148 | | |
149 | | /* |
150 | | * AlignedAllocGetChunkContext |
151 | | * Return the MemoryContext that 'pointer' belongs to. |
152 | | */ |
153 | | MemoryContext |
154 | | AlignedAllocGetChunkContext(void *pointer) |
155 | 0 | { |
156 | 0 | MemoryChunk *redirchunk = PointerGetMemoryChunk(pointer); |
157 | 0 | MemoryContext cxt; |
158 | |
|
159 | 0 | VALGRIND_MAKE_MEM_DEFINED(redirchunk, sizeof(MemoryChunk)); |
160 | |
|
161 | 0 | Assert(!MemoryChunkIsExternal(redirchunk)); |
162 | |
|
163 | 0 | cxt = GetMemoryChunkContext(MemoryChunkGetBlock(redirchunk)); |
164 | |
|
165 | 0 | VALGRIND_MAKE_MEM_NOACCESS(redirchunk, sizeof(MemoryChunk)); |
166 | |
|
167 | 0 | return cxt; |
168 | 0 | } |
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 | | AlignedAllocGetChunkSpace(void *pointer) |
177 | 0 | { |
178 | 0 | MemoryChunk *redirchunk = PointerGetMemoryChunk(pointer); |
179 | 0 | void *unaligned; |
180 | 0 | Size space; |
181 | |
|
182 | 0 | VALGRIND_MAKE_MEM_DEFINED(redirchunk, sizeof(MemoryChunk)); |
183 | |
|
184 | 0 | unaligned = MemoryChunkGetBlock(redirchunk); |
185 | 0 | space = GetMemoryChunkSpace(unaligned); |
186 | |
|
187 | 0 | VALGRIND_MAKE_MEM_NOACCESS(redirchunk, sizeof(MemoryChunk)); |
188 | |
|
189 | 0 | return space; |
190 | 0 | } |