diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index ac339fb..18fb3a4 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -1350,17 +1350,29 @@ include_dir 'conf.d'
- At present, this feature is supported only on Linux. The setting is
- ignored on other systems when set to try.
+ At present, this feature is supported only on Linux and Windows. The
+ setting is ignored on other systems when set to try.
The use of huge pages results in smaller page tables and less CPU time
- spent on memory management, increasing performance. For more details,
+ spent on memory management, increasing performance. For more details on Linux,
see .
+ Huge pages are known as large pages on Windows. To use them, you need to
+ assign the user right Lock Pages in Memory to the Windows user account
+ that runs PostgreSQL.
+ You can use Windows Group Policy tool (gpedit.msc) to assign the user right
+ Lock Pages in Memory.
+ To start the database server on the command prompt as a standalone process,
+ not as a Windows service, run the command prompt as an administrator or
+ disable the User Access Control (UAC). When the UAC is enabled, the normal
+ command prompt revokes the user right Lock Pages in Memory.
+
+
+
With huge_pages set to try,
the server will try to use huge pages, but fall back to using
normal allocation if that fails. With on, failure
diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c
index 31489fc..b022dc3 100644
--- a/src/backend/port/win32_shmem.c
+++ b/src/backend/port/win32_shmem.c
@@ -21,6 +21,7 @@ HANDLE UsedShmemSegID = INVALID_HANDLE_VALUE;
void *UsedShmemSegAddr = NULL;
static Size UsedShmemSegSize = 0;
+static bool EnableLockPagesPrivilege(int elevel);
static void pgwin32_SharedMemoryDelete(int status, Datum shmId);
/*
@@ -103,6 +104,66 @@ PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2)
return true;
}
+/*
+ * EnableLockPagesPrivilege
+ *
+ * Try to acquire SeLockMemoryPrivilege so we can use large pages.
+ */
+static bool
+EnableLockPagesPrivilege(int elevel)
+{
+ HANDLE hToken;
+ TOKEN_PRIVILEGES tp;
+ LUID luid;
+
+ if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
+ {
+ ereport(elevel,
+ (errmsg("could not enable Lock Pages in Memory user right: error code %lu", GetLastError()),
+ errdetail("Failed system call was %s.", "OpenProcessToken")));
+ return FALSE;
+ }
+
+ if (!LookupPrivilegeValue(NULL, SE_LOCK_MEMORY_NAME, &luid))
+ {
+ ereport(elevel,
+ (errmsg("could not enable Lock Pages in Memory user right: error code %lu", GetLastError()),
+ errdetail("Failed system call was %s.", "LookupPrivilegeValue")));
+ CloseHandle(hToken);
+ return FALSE;
+ }
+ tp.PrivilegeCount = 1;
+ tp.Privileges[0].Luid = luid;
+ tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+
+ if (!AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL))
+ {
+ ereport(elevel,
+ (errmsg("could not enable Lock Pages in Memory user right: error code %lu", GetLastError()),
+ errdetail("Failed system call was %s.", "AdjustTokenPrivileges")));
+ CloseHandle(hToken);
+ return FALSE;
+ }
+
+ if (GetLastError() != ERROR_SUCCESS)
+ {
+ if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)
+ ereport(elevel,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("could not enable Lock Pages in Memory user right"),
+ errhint("Assign Lock Pages in Memory user right to the Windows user account which runs PostgreSQL.")));
+ else
+ ereport(elevel,
+ (errmsg("could not enable Lock Pages in Memory user right: error code %lu", GetLastError()),
+ errdetail("Failed system call was %s.", "AdjustTokenPrivileges")));
+ CloseHandle(hToken);
+ return FALSE;
+ }
+
+ CloseHandle(hToken);
+
+ return TRUE;
+}
/*
* PGSharedMemoryCreate
@@ -127,11 +188,9 @@ PGSharedMemoryCreate(Size size, bool makePrivate, int port,
int i;
DWORD size_high;
DWORD size_low;
-
- if (huge_pages == HUGE_PAGES_ON)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("huge pages not supported on this platform")));
+ SIZE_T largePageSize = 0;
+ Size orig_size = size;
+ DWORD flProtect = PAGE_READWRITE;
/* Room for a header? */
Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
@@ -140,6 +199,35 @@ PGSharedMemoryCreate(Size size, bool makePrivate, int port,
UsedShmemSegAddr = NULL;
+ if (huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY)
+ {
+ /* Does the processor support large pages? */
+ largePageSize = GetLargePageMinimum();
+ if (largePageSize == 0)
+ {
+ ereport(huge_pages == HUGE_PAGES_ON ? FATAL : DEBUG1,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("the processor does not support large pages")));
+ ereport(DEBUG1,
+ (errmsg("disabling huge pages")));
+ }
+ else if (!EnableLockPagesPrivilege(huge_pages == HUGE_PAGES_ON ? FATAL : DEBUG1))
+ {
+ ereport(DEBUG1,
+ (errmsg("disabling huge pages")));
+ }
+ else
+ {
+ /* Huge pages available and privilege enabled, so turn on */
+ flProtect = PAGE_READWRITE | SEC_COMMIT | SEC_LARGE_PAGES;
+
+ /* Round size up as appropriate. */
+ if (size % largePageSize != 0)
+ size += largePageSize - (size % largePageSize);
+ }
+ }
+
+retry:
#ifdef _WIN64
size_high = size >> 32;
#else
@@ -163,16 +251,35 @@ PGSharedMemoryCreate(Size size, bool makePrivate, int port,
hmap = CreateFileMapping(INVALID_HANDLE_VALUE, /* Use the pagefile */
NULL, /* Default security attrs */
- PAGE_READWRITE, /* Memory is Read/Write */
+ flProtect,
size_high, /* Size Upper 32 Bits */
size_low, /* Size Lower 32 bits */
szShareMem);
if (!hmap)
- ereport(FATAL,
- (errmsg("could not create shared memory segment: error code %lu", GetLastError()),
- errdetail("Failed system call was CreateFileMapping(size=%zu, name=%s).",
- size, szShareMem)));
+ {
+ if (GetLastError() == ERROR_NO_SYSTEM_RESOURCES &&
+ huge_pages == HUGE_PAGES_TRY &&
+ (flProtect & SEC_LARGE_PAGES) != 0)
+ {
+ elog(DEBUG1, "CreateFileMapping(%llu) with SEC_LARGE_PAGES failed "
+ "due to insufficient large pages, huge pages disabled",
+ size);
+
+ /*
+ * Use the original size, not the rounded-up value, when falling back
+ * to non-huge pages.
+ */
+ size = orig_size;
+ flProtect = PAGE_READWRITE;
+ goto retry;
+ }
+ else
+ ereport(FATAL,
+ (errmsg("could not create shared memory segment: error code %lu", GetLastError()),
+ errdetail("Failed system call was CreateFileMapping(size=%zu, name=%s).",
+ size, szShareMem)));
+ }
/*
* If the segment already existed, CreateFileMapping() will return a
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 8b5f064..b871679 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -3857,7 +3857,7 @@ static struct config_enum ConfigureNamesEnum[] =
{
{"huge_pages", PGC_POSTMASTER, RESOURCES_MEM,
- gettext_noop("Use of huge pages on Linux."),
+ gettext_noop("Use of huge pages on Linux/Windows."),
NULL
},
&huge_pages,
diff --git a/src/bin/pg_ctl/pg_ctl.c b/src/bin/pg_ctl/pg_ctl.c
index c63819b..2358ed0 100644
--- a/src/bin/pg_ctl/pg_ctl.c
+++ b/src/bin/pg_ctl/pg_ctl.c
@@ -1708,11 +1708,6 @@ typedef BOOL (WINAPI * __SetInformationJobObject) (HANDLE, JOBOBJECTINFOCLASS, L
typedef BOOL (WINAPI * __AssignProcessToJobObject) (HANDLE, HANDLE);
typedef BOOL (WINAPI * __QueryInformationJobObject) (HANDLE, JOBOBJECTINFOCLASS, LPVOID, DWORD, LPDWORD);
-/* Windows API define missing from some versions of MingW headers */
-#ifndef DISABLE_MAX_PRIVILEGE
-#define DISABLE_MAX_PRIVILEGE 0x1
-#endif
-
/*
* Create a restricted token, a job object sandbox, and execute the specified
* process with it.
@@ -1794,7 +1789,7 @@ CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION *processInfo, bool as_ser
}
b = _CreateRestrictedToken(origToken,
- DISABLE_MAX_PRIVILEGE,
+ 0,
sizeof(dropSids) / sizeof(dropSids[0]),
dropSids,
0, NULL,