From 1abfc0e4f4d1e8b44a4f49e13e97c6023ee9c02e Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@enterprisedb.com>
Date: Wed, 26 Dec 2018 12:04:00 +1300
Subject: [PATCH] Add huge page support for AIX.

When using shared_memory_type=sysv, provide support for huge pages on
AIX.

WIP: Not tested (I don't have AIX).  Does it work?
Is the retry logic right?  Is the documentation good enough?
Does AIX care about non-page-aligned sized?  You could support
sysv + huge page on Linux too (SHM_HUGETBL) but there doesn't
appear to be any point, should we for completeness?

Author: Thomas Munro, based on sketch code provided by Tony Reix
Reported-by: Tony Reix
Discussion: https://postgr.es/m/HE1PR0202MB28126DB4E0B6621CC6A1A91286D90%40HE1PR0202MB2812.eurprd02.prod.outlook.com
---
 doc/src/sgml/config.sgml      | 14 +++++++++--
 src/backend/port/sysv_shmem.c | 45 +++++++++++++++++++++++++++++++----
 2 files changed, 52 insertions(+), 7 deletions(-)

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 7f9ce8fcba..457eca33e6 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -1542,9 +1542,12 @@ include_dir 'conf.d'
        </para>
 
        <para>
-        At present, this setting is supported only on Linux and Windows. The
+        At present, this setting is supported only on Linux, AIX and Windows. The
         setting is ignored on other systems when set to
-        <literal>try</literal>.
+        <literal>try</literal>.  On Linux, huge pages are only available when
+        <varname>shared_memory_type</varname> is set to <literal>mmap</literal>
+        (the default).  On AIX, they are only available when
+        <varname>shared_memory_type</varname> is set to <literal>sysv</literal>.
        </para>
 
        <para>
@@ -1565,6 +1568,13 @@ include_dir 'conf.d'
         command prompt revokes the user right Lock Pages in Memory when started.
        </para>
 
+       <para>
+        Huge pages are known as large page on AIX.  To use them, the user account
+        that runs <productname>PostgreSQL</productname> must have the
+        <literal>CAP_BYPASS_RAC_VMM</literal> and <literal>CAP_PROPAGATE</literal>
+        capabilities.
+       </para>
+
        <para>
         Note that this setting only affects the main shared memory area.
         Operating systems such as Linux, FreeBSD, and Illumos can also use
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index dab2920ad6..a48721ec83 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -125,6 +125,7 @@ InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size)
 	IpcMemoryId shmid;
 	void	   *requestedAddress = NULL;
 	void	   *memAddress;
+	int			shmget_extra_flags = 0;
 
 	/*
 	 * Normally we just pass requestedAddress = NULL to shmat(), allowing the
@@ -145,7 +146,28 @@ InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size)
 	}
 #endif
 
-	shmid = shmget(memKey, size, IPC_CREAT | IPC_EXCL | IPCProtection);
+	if (huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY)
+	{
+#if defined(SHM_LGPAGE)
+		/* AIX */
+		shmget_extra_flags = SHM_LGPAGE | SHM_PIN;
+#endif
+	}
+
+	shmid = shmget(memKey, size,
+				   IPC_CREAT | IPC_EXCL | IPCProtection | shmget_extra_flags);
+
+	/*
+	 * If we tried and failed to allocate huge pages, try again without the extra
+	 * flags so that we ask for the default page size.
+	 */
+	if (huge_pages == HUGE_PAGES_TRY && shmget_extra_flags > 0 && shmid < 0 &&
+		errno != EEXIST)
+	{
+		elog(DEBUG1, "shmget(%zu) with huge pages failed, huge pages disabled: %m",
+			 size);
+		shmid = shmget(memKey, size, IPC_CREAT | IPC_EXCL | IPCProtection);
+	}
 
 	if (shmid < 0)
 	{
@@ -641,12 +663,25 @@ PGSharedMemoryCreate(Size size,
 						DataDir)));
 
 	/* Complain if hugepages demanded but we can't possibly support them */
-#if !defined(MAP_HUGETLB)
 	if (huge_pages == HUGE_PAGES_ON)
-		ereport(ERROR,
-				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-				 errmsg("huge pages not supported on this platform")));
+	{
+		if (shared_memory_type == SHMEM_TYPE_MMAP)
+		{
+#if !defined(MAP_HUGETLB)
+			ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					 errmsg("huge pages not supported on this platform with this shared_memory_type")));
 #endif
+		}
+		else if (shared_memory_type == SHMEM_TYPE_SYSV)
+		{
+#if !defined(SHM_LGPAGE)
+			ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					 errmsg("huge pages not supported on this platform with this shared_memory_type")));
+#endif
+		}
+	}
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
-- 
2.22.0

