From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Palak Chaturvedi Date: Thu, 17 Sep 2026 00:00:00 +0000 Subject: [PATCH] shmem: limit madvise calls to resized ranges When shrinking, stop MADV_REMOVE at the current page-aligned end rather than extending into the protected reserved tail. Kernels that require the mapping to be currently writable reject the latter with EACCES, which causes buffer pool resizing to PANIC. Retain the maximum-size bound to preserve any page shared with the next structure. When growing, pass only the newly added page-aligned range to MADV_POPULATE_WRITE. This avoids prefaulting the existing range again for small increases in a large structure. Update configure with the MADV_REMOVE and MADV_POPULATE_WRITE declaration checks already present in configure.ac. Reported-by: Yuhang Qiu Discussion: https://postgr.es/m/78DD860A-DD0E-4B70-A0CA-EE5CCA3E0E60@gmail.com Discussion: https://postgr.es/m/8E7D0939-ADE1-405A-91D2-0139A6E322B3@gmail.com --- configure | 26 ++++++++++++++++++++++++++ src/backend/storage/ipc/shmem.c | 18 ++++++++++-------- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/configure b/configure index d42a7a794ff..6e61394a81d 100755 --- a/configure +++ b/configure @@ -16414,6 +16414,32 @@ cat >>confdefs.h <<_ACEOF _ACEOF +# Linux-specific madvise constants needed for resizable shared memory. See similar checks in meson.build for explanation of why these checks are here. +ac_fn_c_check_decl "$LINENO" "MADV_POPULATE_WRITE" "ac_cv_have_decl_MADV_POPULATE_WRITE" "#include +" +if test "x$ac_cv_have_decl_MADV_POPULATE_WRITE" = xyes; then : + ac_have_decl=1 +else + ac_have_decl=0 +fi + +cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_MADV_POPULATE_WRITE $ac_have_decl +_ACEOF + +ac_fn_c_check_decl "$LINENO" "MADV_REMOVE" "ac_cv_have_decl_MADV_REMOVE" "#include +" +if test "x$ac_cv_have_decl_MADV_REMOVE" = xyes; then : + ac_have_decl=1 +else + ac_have_decl=0 +fi + +cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_MADV_REMOVE $ac_have_decl +_ACEOF + + ac_fn_c_check_func "$LINENO" "explicit_bzero" "ac_cv_func_explicit_bzero" if test "x$ac_cv_func_explicit_bzero" = xyes; then : $as_echo "#define HAVE_EXPLICIT_BZERO 1" >>confdefs.h diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c index fbc05f0dc13..c4aa1660fca 100644 --- a/src/backend/storage/ipc/shmem.c +++ b/src/backend/storage/ipc/shmem.c @@ -917,15 +917,17 @@ ShmemResizeStruct(const char *name, Size new_size) * When shrinking, release memory pages beyond the new end, but not the * page containing maximal end of the structure, as it may be used by the * next structure. - * - * We do not consider the current end of the structure as it simplifies - * the calculations. Instead we rely on the underlying APIs not to touch - * the memory pages that will not be affected by the change in size. */ new_end = (char *) TYPEALIGN(page_size, (char *) result->location + new_size); if (new_size < result->size) { - char *max_end = (char *) TYPEALIGN_DOWN(page_size, (char *) result->location + result->maximum_size); + /* + * Stop at the current page-aligned end to avoid the protected tail, + * and preserve any page shared with the next structure. + */ + char *current_end = (char *) TYPEALIGN(page_size, (char *) result->location + result->size); + char *reserved_end = (char *) TYPEALIGN_DOWN(page_size, (char *) result->location + result->maximum_size); + char *max_end = Min(current_end, reserved_end); if (max_end > new_end) { @@ -938,9 +940,9 @@ ShmemResizeStruct(const char *name, Size new_size) } else if (new_size > result->size) { - char *struct_start = (char *) TYPEALIGN_DOWN(page_size, (char *) result->location); + char *old_end = (char *) TYPEALIGN(page_size, (char *) result->location + result->size); - if (new_end > struct_start) + if (new_end > old_end) { ShmemIndexEnt entry_copy = *result; @@ -951,7 +953,7 @@ ShmemResizeStruct(const char *name, Size new_size) entry_copy.size = new_size; ShmemProtectStructInternal(&entry_copy); - if (!PGSharedMemoryEnsureAllocated(struct_start, new_end - struct_start)) + if (!PGSharedMemoryEnsureAllocated(old_end, new_end - old_end)) { ShmemProtectStructInternal(result); ereport(WARNING,