Hi all, While working on resizable shared memory, I noticed an inconsistency in pg_get_shmem_pagesize(). Attached are two patches fixing that inconsistency.
0001: Fix pg_get_shmem_pagesize() on Windows When huge pages are in use, pg_get_shmem_pagesize() calls GetHugePageSize(). On Windows, however, that function is a compatibility stub returning zero. The patch uses GetLargePageMinimum() instead when large pages are actually in use. The only caller of this function today rejects unsupported platforms before reaching it. I don't think there is a current user-visible failure on Windows. But we are not restricting its usage on Windows and thus a future caller may face problems. I am proposing this for the master rather than requesting a backpatch since it's purely pro-active fix. I looked through the relevant earlier discussions (CC'ed relevant people). Commit 43c1c4f65eab77bcfc4f535a7e9ac0421e0cf2a5 introduced shared_memory_size_in_huge_pages. Its thread [1] explicitly discussed Windows reporting. An early patch used GetLargePageMinimum(), but the participants agreed that the required page count was not particularly useful on Windows and left the GUC at -1 [2]. A later exchange clarified that Windows has no equivalent of Linux's vm.nr_hugepages setting [3]. Considering those discussions, the attached patch leaves the stub and that GUC behavior unchanged. Commit 3887d0cfeb3315bc4c6219bd2eb90e89cf487fa8 renamed pg_numa_get_pagesize() to pg_get_shmem_pagesize() and moved it into shmem.c, retaining the existing lookup logic. In that thread [4], Windows portability and the requirement to call the function after shared-memory initialization were discussed [5]. I did not find an explicit discussion of the zero returned by the Windows huge-page stub. Commit 65c298f61fc70f2f960437c05649f71b862e2c48 originally introduced pg_numa_get_pagesize(), including its call to GetHugePageSize(). In the original NUMA observability thread [6], Windows portability was considered: one exchange addressed the absence of sysconf() on Windows [7], and a later review discussed consolidating the implementations and requiring shared-memory initialization before calling the function [5]. However, I could not find any discussion of the Windows GetHugePageSize() stub returning zero. It looks like the faulty use of GetHugePageSize() on Windows was never noticed or considered. 0002: Record the main shared memory segment's page size Rather than determining the page size again in pg_get_shmem_pagesize(), this patch records it in PGShmemHeader during allocation and makes pg_get_shmem_pagesize() return the recorded value. That way we eliminate any future possibility of this function returning a different size than actually used - hoping that every method of allocating shared memory will continue to record page size. This associates the value with the successful allocation, including ordinary-page fallback with huge_pages=try. It remains constant for the mapping's lifetime and is initialized afresh if shared memory is recreated, for example after a crash. It also avoids repeated page-size discovery. In particular, on Linux, the current huge-page path opens and scans /proc/meminfo on every call. I have not measured a significant performance benefit for the existing NUMA reporting caller; the additional motivation is that resizable shared-memory support will need the page size during resizing and protection changes. I could not find a proposal to retain the page size in the shared-memory header, or a rejection of that approach, in either of the above threads. The second patch supersedes the accessor implementation changed by the first, but I have kept the Windows correction separate so it can be considered independently. [1] https://www.postgresql.org/message-id/flat/F2772387-CE0F-46BF-B5F1-CC55516EB885%40amazon.com [2] https://www.postgresql.org/message-id/7ED221D0-0D75-46F8-8D6C-8AF70D8EABE7%40amazon.com [3] https://www.postgresql.org/message-id/YmXoIEaR2CWRV7KP%40paquier.xyz [4] https://www.postgresql.org/message-id/flat/CALdSSPi5fj0a7UG7Fmw2cUD1uWuckU_e8dJ%2B6x-bJEokcSXzqA%40mail.gmail.com [5] https://www.postgresql.org/message-id/tvdbhi7rkk7rvpcm22bj6jnri4ucdb4dnzefer6q7hqflpkxm7%40eq37undomnxw [6] https://postgr.es/m/CAKZiRmxh6KWo0aqRqvmcoaX2jUxZYb4kGp3N%3Dq1w%2BDiH-696Xw%40mail.gmail.com [7] https://www.postgresql.org/message-id/CAKZiRmz_ERUH0udcZgpAinBSqpWoOYeao_rNNEe9f8Z9asTihw%40mail.gmail.com -- Best Wishes, Ashutosh Bapat
From 817a64f323b058b0e0079ec2b76178370a24d82c Mon Sep 17 00:00:00 2001 From: Ashutosh Bapat <[email protected]> Date: Fri, 30 Oct 2026 15:50:36 +0530 Subject: [PATCH v20261030 1/2] pg_get_shmem_pagesize() on Windows pg_get_shmem_pagesize() calls GetHugePageSize() when huge pages are in use. On Windows, however, GetHugePageSize() is a compatibility stub that returns zero, so the accessor returns zero rather than the large-page size. Use GetLargePageMinimum() instead on Windows when large pages are in use. Leave ordinary-page handling and the GetHugePageSize() stub unchanged, preserving existing shared_memory_size_in_huge_pages reporting. The only current caller of pg_get_shmem_pagesize() is the NUMA shared memory reporting function, which rejects unsupported platforms before calling this accessor. Thus, the faulty path is not currently reached by that caller on Windows. But this may change in future, hence fixing proactively. No backporting required since the bug does not impact anything. Author: Ashutosh Bapat <[email protected]> Reviewed-by: TBD Discussion: https://postgr.es/m/<message-id-TBD> Co-authored-by: Copilot <[email protected]> --- src/backend/storage/ipc/shmem.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c index 8d81893b470..2d944e21ab8 100644 --- a/src/backend/storage/ipc/shmem.c +++ b/src/backend/storage/ipc/shmem.c @@ -1390,7 +1390,11 @@ pg_get_shmem_pagesize(void) Assert(huge_pages_status != HUGE_PAGES_UNKNOWN); if (huge_pages_status == HUGE_PAGES_ON) +#ifdef WIN32 + os_page_size = GetLargePageMinimum(); +#else GetHugePageSize(&os_page_size, NULL); +#endif return os_page_size; } base-commit: 4f89416c9dbc78da5679d32072315af56fbeb8ed -- 2.34.1
From 457a02a974f7077916e86fe0cdfc3e3a8fd26804 Mon Sep 17 00:00:00 2001 From: Ashutosh Bapat <[email protected]> Date: Fri, 30 Oct 2026 11:56:27 +0530 Subject: [PATCH v20261030 2/2] Record the main shared memory segment's page size pg_get_shmem_pagesize() currently determines the page size independently of the code that creates the main shared memory segment. This duplicates platform-specific logic and allows the two implementations to diverge. For example, before the previous commit, on Windows it returned zero when large pages are in use, because GetHugePageSize() is a stub on that platform. The only current caller of pg_get_shmem_pagesize() rejects unsupported NUMA configurations before reaching this code, so it does not encounter the problem on Windows. Record the page size in the shared memory header when creating the segment, and have pg_get_shmem_pagesize() return that value. Record the size used by the successful allocation, including ordinary-page fallback when huge_pages=try. The value remains constant for the mapping's lifetime and is initialized anew if shared memory is recreated. This also avoids repeated page-size discovery. In particular, on Linux, each call with huge pages enabled previously opened and scanned /proc/meminfo. The proposed resizable shared memory support will need the page size during shared structure resizing, using this reliable, inexpensive function more. Author: Ashutosh Bapat <[email protected]> Reviewed-by: TBD Discussion: https://postgr.es/m/<message-id-TBD> Co-authored-by: Copilot <[email protected]> --- src/backend/port/sysv_shmem.c | 12 +++++++++--- src/backend/port/win32_shmem.c | 9 +++++++++ src/backend/storage/ipc/shmem.c | 27 +++++---------------------- src/include/storage/pg_shmem.h | 2 ++ 4 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 2e3886cf9fe..0605e6e3772 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -594,10 +594,10 @@ check_huge_page_size(int *newval, void **extra, GucSource source) * * Pass the requested size in *size. This function will modify *size to the * actual size of the allocation, if it ends up allocating a segment that is - * larger than requested. + * larger than requested. The memory page size used is returned in *page_size. */ static void * -CreateAnonymousSegment(Size *size) +CreateAnonymousSegment(Size *size, Size *page_size) { Size allocsize = *size; void *ptr = MAP_FAILED; @@ -624,6 +624,8 @@ CreateAnonymousSegment(Size *size) ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, mmap_flags | huge_mmap_flags, -1, 0); mmap_errno = errno; + if (ptr != MAP_FAILED) + *page_size = hugepagesize; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m", allocsize); @@ -648,6 +650,7 @@ CreateAnonymousSegment(Size *size) ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, mmap_flags, -1, 0); mmap_errno = errno; + *page_size = sysconf(_SC_PAGESIZE); } if (ptr == MAP_FAILED) @@ -707,6 +710,7 @@ PGSharedMemoryCreate(Size size, PGShmemHeader *hdr; struct stat statbuf; Size sysvsize; + Size page_size; /* * We use the data directory's ID info (inode and device numbers) to @@ -738,7 +742,7 @@ PGSharedMemoryCreate(Size size, if (shared_memory_type == SHMEM_TYPE_MMAP) { - AnonymousShmem = CreateAnonymousSegment(&size); + AnonymousShmem = CreateAnonymousSegment(&size, &page_size); AnonymousShmemSize = size; /* Register on-exit routine to unmap the anonymous segment */ @@ -750,6 +754,7 @@ PGSharedMemoryCreate(Size size, else { sysvsize = size; + page_size = sysconf(_SC_PAGESIZE); /* huge pages are only available with mmap */ SetConfigOption("huge_pages_status", "off", @@ -855,6 +860,7 @@ PGSharedMemoryCreate(Size size, * Initialize space allocation status for segment. */ hdr->totalsize = size; + hdr->page_size = page_size; hdr->content_offset = MAXALIGN(sizeof(PGShmemHeader)); *shim = hdr; diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index 794e4fcb2ad..e842adce7e0 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -391,6 +391,15 @@ retry: hdr->totalsize = size; hdr->content_offset = MAXALIGN(sizeof(PGShmemHeader)); hdr->dsm_control = 0; + if (flProtect & SEC_LARGE_PAGES) + hdr->page_size = largePageSize; + else + { + SYSTEM_INFO sysinfo; + + GetSystemInfo(&sysinfo); + hdr->page_size = sysinfo.dwPageSize; + } /* Save info for possible future use */ UsedShmemSegAddr = memAddress; diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c index 2d944e21ab8..2b8681a7cd9 100644 --- a/src/backend/storage/ipc/shmem.c +++ b/src/backend/storage/ipc/shmem.c @@ -1367,36 +1367,19 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS) } /* - * Determine the memory page size used for the shared memory segment. + * Return the memory page size used to create the main shared memory segment. * - * If the shared segment was allocated using huge pages, returns the size of - * a huge page. Otherwise returns the size of regular memory page. + * This function avoids exposing the ShmemSegHdr structure directly. * * This should be used only after shared memory has been initialized. */ Size pg_get_shmem_pagesize(void) { - Size os_page_size; -#ifdef WIN32 - SYSTEM_INFO sysinfo; - - GetSystemInfo(&sysinfo); - os_page_size = sysinfo.dwPageSize; -#else - os_page_size = sysconf(_SC_PAGESIZE); -#endif - - Assert(huge_pages_status != HUGE_PAGES_UNKNOWN); - - if (huge_pages_status == HUGE_PAGES_ON) -#ifdef WIN32 - os_page_size = GetLargePageMinimum(); -#else - GetHugePageSize(&os_page_size, NULL); -#endif + Assert(ShmemSegHdr != NULL); + Assert(ShmemSegHdr->page_size > 0); - return os_page_size; + return ShmemSegHdr->page_size; } Datum diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 10c7b065861..ef2c87a97de 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -39,6 +39,8 @@ typedef struct PGShmemHeader /* standard header for all Postgres shmem */ dev_t device; /* device data directory is on */ ino_t inode; /* inode number of data directory */ #endif + Size page_size; /* size of each memory page backing this + * segment. */ } PGShmemHeader; /* GUC variables */ -- 2.34.1
