Age Owner Branch data TLA 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
989 drowley@postgresql.o 29 :CBC 164191 : AlignedAllocFree(void *pointer)
30 : : {
31 : 164191 : MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
32 : : void *unaligned;
33 : :
34 : : VALGRIND_MAKE_MEM_DEFINED(chunk, sizeof(MemoryChunk));
35 : :
36 [ - + ]: 164191 : Assert(!MemoryChunkIsExternal(chunk));
37 : :
38 : : /* obtain the original (unaligned) allocated pointer */
39 : 164191 : unaligned = MemoryChunkGetBlock(chunk);
40 : :
41 : : #ifdef MEMORY_CONTEXT_CHECKING
42 : : /* Test for someone scribbling on unused space in chunk */
43 [ - + ]: 164191 : if (!sentinel_ok(pointer, chunk->requested_size))
989 drowley@postgresql.o 44 [ # # ]:UBC 0 : 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 */
989 drowley@postgresql.o 58 :CBC 164191 : pfree(unaligned);
59 : 164191 : }
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 *
557 drowley@postgresql.o 70 :UBC 0 : AlignedAllocRealloc(void *pointer, Size size, int flags)
71 : : {
989 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 : :
875 81 : 0 : alignto = MemoryChunkGetValue(redirchunk);
82 : 0 : unaligned = MemoryChunkGetBlock(redirchunk);
83 : :
84 : : /* sanity check this is a power of 2 value */
989 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 : : PallocAlignedExtraBytes(alignto) - sizeof(MemoryChunk);
103 : :
104 : : #ifdef MEMORY_CONTEXT_CHECKING
105 : : /* check that GetMemoryChunkSpace returned something realistic */
106 [ # # ]: 0 : 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);
557 116 : 0 : newptr = MemoryContextAllocAligned(ctx, size, alignto, flags);
117 : :
118 : : /* Cope cleanly with OOM */
106 tgl@sss.pgh.pa.us 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 : :
989 drowley@postgresql.o 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 : : {
875 156 : 0 : MemoryChunk *redirchunk = PointerGetMemoryChunk(pointer);
157 : : MemoryContext cxt;
158 : :
159 : : VALGRIND_MAKE_MEM_DEFINED(redirchunk, sizeof(MemoryChunk));
160 : :
161 [ # # ]: 0 : 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
989 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 : :
875 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 : : }
|