Skip to content

Commit 9e91901

Browse files
committed
Fix MemoryContextAllocAligned's interaction with Valgrind.
Arrange that only the "aligned chunk" part of the allocated space is included in a Valgrind vchunk. This suppresses complaints about that vchunk being possibly lost because PG is retaining only pointers to the aligned chunk. Also make sure that trailing wasted space is marked NOACCESS. As a tiny performance improvement, arrange that MCXT_ALLOC_ZERO zeroes only the returned "aligned chunk", not the wasted padding space. In passing, fix GetLocalBufferStorage to use MemoryContextAllocAligned instead of rolling its own implementation, which was equally broken according to Valgrind. Author: Tom Lane <tgl@sss.pgh.pa.us> Reviewed-by: Andres Freund <andres@anarazel.de> Discussion: https://postgr.es/m/285483.1746756246@sss.pgh.pa.us
1 parent bb049a7 commit 9e91901

File tree

3 files changed

+56
-25
lines changed

3 files changed

+56
-25
lines changed

src/backend/storage/buffer/localbuf.c

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -932,10 +932,11 @@ GetLocalBufferStorage(void)
932932
num_bufs = Min(num_bufs, MaxAllocSize / BLCKSZ);
933933

934934
/* Buffers should be I/O aligned. */
935-
cur_block = (char *)
936-
TYPEALIGN(PG_IO_ALIGN_SIZE,
937-
MemoryContextAlloc(LocalBufferContext,
938-
num_bufs * BLCKSZ + PG_IO_ALIGN_SIZE));
935+
cur_block = MemoryContextAllocAligned(LocalBufferContext,
936+
num_bufs * BLCKSZ,
937+
PG_IO_ALIGN_SIZE,
938+
0);
939+
939940
next_buf_in_block = 0;
940941
num_bufs_in_block = num_bufs;
941942
}

src/backend/utils/mmgr/alignedalloc.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,15 @@ AlignedAllocFree(void *pointer)
4545
GetMemoryChunkContext(unaligned)->name, chunk);
4646
#endif
4747

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+
4857
/* Recursively pfree the unaligned chunk */
4958
pfree(unaligned);
5059
}
@@ -123,6 +132,15 @@ AlignedAllocRealloc(void *pointer, Size size, int flags)
123132
VALGRIND_MAKE_MEM_DEFINED(pointer, old_size);
124133
memcpy(newptr, pointer, Min(size, old_size));
125134

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+
126144
pfree(unaligned);
127145

128146
return newptr;

src/backend/utils/mmgr/mcxt.c

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1465,7 +1465,13 @@ MemoryContextAllocAligned(MemoryContext context,
14651465
void *unaligned;
14661466
void *aligned;
14671467

1468-
/* wouldn't make much sense to waste that much space */
1468+
/*
1469+
* Restrict alignto to ensure that it can fit into the "value" field of
1470+
* the redirection MemoryChunk, and that the distance back to the start of
1471+
* the unaligned chunk will fit into the space available for that. This
1472+
* isn't a limitation in practice, since it wouldn't make much sense to
1473+
* waste that much space.
1474+
*/
14691475
Assert(alignto < (128 * 1024 * 1024));
14701476

14711477
/* ensure alignto is a power of 2 */
@@ -1502,10 +1508,15 @@ MemoryContextAllocAligned(MemoryContext context,
15021508
alloc_size += 1;
15031509
#endif
15041510

1505-
/* perform the actual allocation */
1506-
unaligned = MemoryContextAllocExtended(context, alloc_size, flags);
1511+
/*
1512+
* Perform the actual allocation, but do not pass down MCXT_ALLOC_ZERO.
1513+
* This ensures that wasted bytes beyond the aligned chunk do not become
1514+
* DEFINED.
1515+
*/
1516+
unaligned = MemoryContextAllocExtended(context, alloc_size,
1517+
flags & ~MCXT_ALLOC_ZERO);
15071518

1508-
/* set the aligned pointer */
1519+
/* compute the aligned pointer */
15091520
aligned = (void *) TYPEALIGN(alignto, (char *) unaligned +
15101521
sizeof(MemoryChunk));
15111522

@@ -1533,12 +1544,23 @@ MemoryContextAllocAligned(MemoryContext context,
15331544
set_sentinel(aligned, size);
15341545
#endif
15351546

1536-
/* Mark the bytes before the redirection header as noaccess */
1537-
VALGRIND_MAKE_MEM_NOACCESS(unaligned,
1538-
(char *) alignedchunk - (char *) unaligned);
1547+
/*
1548+
* MemoryContextAllocExtended marked the whole unaligned chunk as a
1549+
* vchunk. Undo that, instead making just the aligned chunk be a vchunk.
1550+
* This prevents Valgrind from complaining that the vchunk is possibly
1551+
* leaked, since only pointers to the aligned chunk will exist.
1552+
*
1553+
* After these calls, the aligned chunk will be marked UNDEFINED, and all
1554+
* the rest of the unaligned chunk (the redirection chunk header, the
1555+
* padding bytes before it, and any wasted trailing bytes) will be marked
1556+
* NOACCESS, which is what we want.
1557+
*/
1558+
VALGRIND_MEMPOOL_FREE(context, unaligned);
1559+
VALGRIND_MEMPOOL_ALLOC(context, aligned, size);
15391560

1540-
/* Disallow access to the redirection chunk header. */
1541-
VALGRIND_MAKE_MEM_NOACCESS(alignedchunk, sizeof(MemoryChunk));
1561+
/* Now zero (and make DEFINED) just the aligned chunk, if requested */
1562+
if ((flags & MCXT_ALLOC_ZERO) != 0)
1563+
MemSetAligned(aligned, 0, size);
15421564

15431565
return aligned;
15441566
}
@@ -1572,16 +1594,12 @@ void
15721594
pfree(void *pointer)
15731595
{
15741596
#ifdef USE_VALGRIND
1575-
MemoryContextMethodID method = GetMemoryChunkMethodID(pointer);
15761597
MemoryContext context = GetMemoryChunkContext(pointer);
15771598
#endif
15781599

15791600
MCXT_METHOD(pointer, free_p) (pointer);
15801601

1581-
#ifdef USE_VALGRIND
1582-
if (method != MCTX_ALIGNED_REDIRECT_ID)
1583-
VALGRIND_MEMPOOL_FREE(context, pointer);
1584-
#endif
1602+
VALGRIND_MEMPOOL_FREE(context, pointer);
15851603
}
15861604

15871605
/*
@@ -1591,9 +1609,6 @@ pfree(void *pointer)
15911609
void *
15921610
repalloc(void *pointer, Size size)
15931611
{
1594-
#ifdef USE_VALGRIND
1595-
MemoryContextMethodID method = GetMemoryChunkMethodID(pointer);
1596-
#endif
15971612
#if defined(USE_ASSERT_CHECKING) || defined(USE_VALGRIND)
15981613
MemoryContext context = GetMemoryChunkContext(pointer);
15991614
#endif
@@ -1616,10 +1631,7 @@ repalloc(void *pointer, Size size)
16161631
*/
16171632
ret = MCXT_METHOD(pointer, realloc) (pointer, size, 0);
16181633

1619-
#ifdef USE_VALGRIND
1620-
if (method != MCTX_ALIGNED_REDIRECT_ID)
1621-
VALGRIND_MEMPOOL_CHANGE(context, pointer, ret, size);
1622-
#endif
1634+
VALGRIND_MEMPOOL_CHANGE(context, pointer, ret, size);
16231635

16241636
return ret;
16251637
}

0 commit comments

Comments
 (0)