From de31b31eb48f1c918a266b1a902ca6ab553a5675 Mon Sep 17 00:00:00 2001
From: Nathan Bossart <bossartn@amazon.com>
Date: Thu, 16 Sep 2021 21:23:04 +0000
Subject: [PATCH v12 1/1] Introduce shared_memory_size_in_huge_pages GUC.

This runtime-computed GUC shows the number of huge pages required
for the server's main shared memory area.
---
 doc/src/sgml/config.sgml           | 20 ++++++++++++++++++++
 doc/src/sgml/ref/postgres-ref.sgml |  3 ++-
 doc/src/sgml/runtime.sgml          | 30 ++++++++++--------------------
 src/backend/port/sysv_shmem.c      | 16 +++++++++++-----
 src/backend/port/win32_shmem.c     | 14 ++++++++++++++
 src/backend/storage/ipc/ipci.c     | 14 ++++++++++++++
 src/backend/utils/misc/guc.c       | 12 ++++++++++++
 src/include/storage/pg_shmem.h     |  1 +
 8 files changed, 84 insertions(+), 26 deletions(-)

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index ef0e2a7746..6e8839cd1b 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -10289,6 +10289,26 @@ dynamic_library_path = 'C:\tools\postgresql;H:\my_project\lib;$libdir'
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-shared-memory-size-in-huge-pages" xreflabel="shared_memory_size_in_huge_pages">
+      <term><varname>shared_memory_size_in_huge_pages</varname> (<type>integer</type>)
+      <indexterm>
+       <primary><varname>shared_memory_size_in_huge_pages</varname> configuration parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+        Reports the number of huge pages that are needed for the main shared
+        memory area based on the specified <xref linkend="guc-huge-page-size"/>.
+        If huge pages are not supported, this will be <literal>-1</literal>.
+       </para>
+       <para>
+        This setting is supported only on Linux.  It is always set to
+        <literal>-1</literal> on other platforms.  For more details about using
+        huge pages on Linux, see <xref linkend="linux-huge-pages"/>.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="guc-ssl-library" xreflabel="ssl_library">
       <term><varname>ssl_library</varname> (<type>string</type>)
       <indexterm>
diff --git a/doc/src/sgml/ref/postgres-ref.sgml b/doc/src/sgml/ref/postgres-ref.sgml
index f72c3b04e4..55a3f6c69d 100644
--- a/doc/src/sgml/ref/postgres-ref.sgml
+++ b/doc/src/sgml/ref/postgres-ref.sgml
@@ -143,7 +143,8 @@ PostgreSQL documentation
        <para>
         This can be used on a running server for most parameters.  However,
         the server must be shut down for some runtime-computed parameters
-        (e.g., <xref linkend="guc-shared-memory-size"/> and
+        (e.g., <xref linkend="guc-shared-memory-size"/>,
+        <xref linkend="guc-shared-memory-size-in-huge-pages"/>, and
         <xref linkend="guc-wal-segment-size"/>).
        </para>
 
diff --git a/doc/src/sgml/runtime.sgml b/doc/src/sgml/runtime.sgml
index f1cbc1d9e9..9310859506 100644
--- a/doc/src/sgml/runtime.sgml
+++ b/doc/src/sgml/runtime.sgml
@@ -1442,31 +1442,21 @@ export PG_OOM_ADJUST_VALUE=0
     with <varname>CONFIG_HUGETLBFS=y</varname> and
     <varname>CONFIG_HUGETLB_PAGE=y</varname>. You will also have to configure
     the operating system to provide enough huge pages of the desired size.
-    To estimate the number of huge pages needed, start
-    <productname>PostgreSQL</productname> without huge pages enabled and check
-    the postmaster's anonymous shared memory segment size, as well as the
-    system's default and supported huge page sizes, using the
-    <filename>/proc</filename> and <filename>/sys</filename> file systems.
+    To determine the number of huge pages needed, use the
+    <command>postgres</command> command to see the value of
+    <xref linkend="guc-shared-memory-size-in-huge-pages"/>.
     This might look like:
 <programlisting>
-$ <userinput>head -1 $PGDATA/postmaster.pid</userinput>
-4170
-$ <userinput>pmap 4170 | awk '/rw-s/ &amp;&amp; /zero/ {print $2}'</userinput>
-6490428K
-$ <userinput>grep ^Hugepagesize /proc/meminfo</userinput>
-Hugepagesize:       2048 kB
-$ <userinput>ls /sys/kernel/mm/hugepages</userinput>
-hugepages-1048576kB  hugepages-2048kB
+$ <userinput>postgres -D $PGDATA -C shared_memory_size_in_huge_pages</userinput>
+3170
 </programlisting>
 
-     In this example the default is 2MB, but you can also explicitly request
-     either 2MB or 1GB with <xref linkend="guc-huge-page-size"/>.
+     Note that you can explicitly request either 2MB or 1GB huge pages with
+     <xref linkend="guc-huge-page-size"/>.
 
-     Assuming <literal>2MB</literal> huge pages,
-     <literal>6490428</literal> / <literal>2048</literal> gives approximately
-     <literal>3169.154</literal>, so in this example we need at
-     least <literal>3170</literal> huge pages.  A larger setting would be
-     appropriate if other programs on the machine also need huge pages.
+     While we need at least <literal>3170</literal> huge pages in this example,
+     a larger setting would be appropriate if other programs on the machine
+     also need huge pages.
      We can set this with:
 <programlisting>
 # <userinput>sysctl -w vm.nr_hugepages=3170</userinput>
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 9de96edf6a..bc15e2649c 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -456,8 +456,6 @@ PGSharedMemoryAttach(IpcMemoryId shmId,
 	return shmStat.shm_nattch == 0 ? SHMSTATE_UNATTACHED : SHMSTATE_ATTACHED;
 }
 
-#ifdef MAP_HUGETLB
-
 /*
  * Identify the huge page size to use, and compute the related mmap flags.
  *
@@ -476,11 +474,19 @@ PGSharedMemoryAttach(IpcMemoryId shmId,
  * such as increasing shared_buffers to absorb the extra space.
  *
  * Returns the (real, assumed or config provided) page size into *hugepagesize,
- * and the hugepage-related mmap flags to use into *mmap_flags.
+ * and the hugepage-related mmap flags to use into *mmap_flags.  If huge pages
+ * are not supported, *hugepagesize and *mmap_flags will be set to 0.
  */
-static void
+void
 GetHugePageSize(Size *hugepagesize, int *mmap_flags)
 {
+#ifndef MAP_HUGETLB
+
+	*hugepagesize = 0;
+	*mmap_flags = 0;
+
+#else
+
 	Size		default_hugepagesize = 0;
 
 	/*
@@ -553,9 +559,9 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags)
 		*mmap_flags |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT;
 	}
 #endif
-}
 
 #endif							/* MAP_HUGETLB */
+}
 
 /*
  * Creates an anonymous mmap()ed shared memory segment.
diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c
index d7a71992d8..90de2ab4e1 100644
--- a/src/backend/port/win32_shmem.c
+++ b/src/backend/port/win32_shmem.c
@@ -605,3 +605,17 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild)
 
 	return true;
 }
+
+/*
+ * This function is provided for consistency with sysv_shmem.c and does not
+ * provide any useful information for Windows.  To obtain the large page size,
+ * use GetLargePageMinimum() instead.
+ *
+ * This always sets *hugepagesize and *mmap_flags to 0.
+ */
+void
+GetHugePageSize(Size *hugepagesize, int *mmap_flags)
+{
+	*hugepagesize = 0;
+	*mmap_flags = 0;
+}
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 13f3926ff6..805d807906 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -326,6 +326,9 @@ InitializeShmemGUCs(void)
 	char		buf[64];
 	Size		size_b;
 	Size		size_mb;
+	Size		hp_size;
+	Size		hp_required;
+	int			unused;
 
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
@@ -334,4 +337,15 @@ InitializeShmemGUCs(void)
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf, PGC_INTERNAL, PGC_S_OVERRIDE);
+
+	/*
+	 * Calculate the number of huge pages required.
+	 */
+	GetHugePageSize(&hp_size, &unused);
+	if (hp_size != 0)
+	{
+		hp_required = add_size(size_b / hp_size, 1);
+		sprintf(buf, "%zu", hp_required);
+		SetConfigOption("shared_memory_size_in_huge_pages", buf, PGC_INTERNAL, PGC_S_OVERRIDE);
+	}
 }
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index a6e4fcc24e..d2ce4a8450 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -665,6 +665,7 @@ static int	max_identifier_length;
 static int	block_size;
 static int	segment_size;
 static int	shared_memory_size_mb;
+static int	shared_memory_size_in_huge_pages;
 static int	wal_block_size;
 static bool data_checksums;
 static bool integer_datetimes;
@@ -2349,6 +2350,17 @@ static struct config_int ConfigureNamesInt[] =
 		NULL, NULL, NULL
 	},
 
+	{
+		{"shared_memory_size_in_huge_pages", PGC_INTERNAL, PRESET_OPTIONS,
+			gettext_noop("Shows the number of huge pages needed for the main shared memory area."),
+			gettext_noop("-1 indicates that the value could not be determined."),
+			GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE | GUC_RUNTIME_COMPUTED
+		},
+		&shared_memory_size_in_huge_pages,
+		-1, -1, INT_MAX,
+		NULL, NULL, NULL
+	},
+
 	{
 		{"temp_buffers", PGC_USERSET, RESOURCES_MEM,
 			gettext_noop("Sets the maximum number of temporary buffers used by each session."),
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 059df1b72c..518eb86065 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -87,5 +87,6 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size,
 										   PGShmemHeader **shim);
 extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
+extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
 #endif							/* PG_SHMEM_H */
-- 
2.16.6

