diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index 8d7b3bf..e1c3c66 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -1335,17 +1335,23 @@ 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 . + This feature uses the large-page support on Windows. To use the large-page + support, you need to assign Lock page in memory user right to the Windows + user account which runs PostgreSQL. + + + 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 0ff2c7e..88b195a 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,61 @@ 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"), + errdetail("Failed system call was %s, error code %lu", "OpenProcessToken", GetLastError()))); + return FALSE; + } + + if (!LookupPrivilegeValue(NULL, SE_LOCK_MEMORY_NAME, &luid)) + { + CloseHandle(hToken); + ereport(elevel, + (errmsg("could not enable Lock pages in memory user right"), + errdetail("Failed system call was %s, error code %lu", "LookupPrivilegeValue", GetLastError()))); + 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"), + errdetail("Failed system call was %s, error code %lu", "AdjustTokenPrivileges", GetLastError()))); + CloseHandle(hToken); + return FALSE; + } + + 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."))); + CloseHandle(hToken); + return FALSE; + } + + CloseHandle(hToken); + + return TRUE; +} /* * PGSharedMemoryCreate @@ -127,11 +183,8 @@ 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; + DWORD flProtect = PAGE_READWRITE; /* Room for a header? */ Assert(size > MAXALIGN(sizeof(PGShmemHeader))); @@ -140,6 +193,34 @@ 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); + } + } + #ifdef _WIN64 size_high = size >> 32; #else @@ -163,7 +244,7 @@ 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); diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 946ba9e..9659000 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -3796,7 +3796,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,