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

Reply via email to