</para>
<para>
- At present, this feature is supported only on Linux. The setting is
- ignored on other systems when set to <literal>try</literal>.
+ At present, this feature is supported only on Linux and Windows. The
+ setting is ignored on other systems when set to <literal>try</literal>.
</para>
<para>
The use of huge pages results in smaller page tables and less CPU time
- spent on memory management, increasing performance. For more details,
- see <xref linkend="linux-huge-pages"/>.
+ spent on memory management, increasing performance. For more details about
+ using huge pages on Linux, see <xref linkend="linux-huge-pages"/>.
+ </para>
+
+ <para>
+ 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 <productname>PostgreSQL</productname>.
+ 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, the command prompt must be run as an administrator
+ User Access Control (UAC) must be disabled. When the UAC is enabled, the normal
+ command prompt revokes the user right Lock Pages in Memory when started.
</para>
<para>
void *UsedShmemSegAddr = NULL;
static Size UsedShmemSegSize = 0;
+static bool EnableLockPagesPrivilege(int elevel);
static void pgwin32_SharedMemoryDelete(int status, Datum shmId);
/*
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
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)));
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
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(%zu) with SEC_LARGE_PAGES failed, "
+ "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
{
{"huge_pages", PGC_POSTMASTER, RESOURCES_MEM,
- gettext_noop("Use of huge pages on Linux."),
+ gettext_noop("Use of huge pages on Linux or Windows."),
NULL
},
&huge_pages,
static void WINAPI pgwin32_ServiceMain(DWORD, LPTSTR *);
static void pgwin32_doRunAsService(void);
static int CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION *processInfo, bool as_service);
+static PTOKEN_PRIVILEGES GetPrivilegesToDelete(HANDLE hToken);
#endif
static pgpid_t get_pgpid(bool is_status_request);
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.
HANDLE restrictedToken;
SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY};
SID_AND_ATTRIBUTES dropSids[2];
+ PTOKEN_PRIVILEGES delPrivs;
/* Functions loaded dynamically */
__CreateRestrictedToken _CreateRestrictedToken = NULL;
return 0;
}
+ /* Get list of privileges to remove */
+ delPrivs = GetPrivilegesToDelete(origToken);
+ if (delPrivs == NULL)
+ /* Error message already printed */
+ return 0;
+
b = _CreateRestrictedToken(origToken,
- DISABLE_MAX_PRIVILEGE,
+ 0,
sizeof(dropSids) / sizeof(dropSids[0]),
dropSids,
- 0, NULL,
+ delPrivs->PrivilegeCount, delPrivs->Privileges,
0, NULL,
&restrictedToken);
+ free(delPrivs);
FreeSid(dropSids[1].Sid);
FreeSid(dropSids[0].Sid);
CloseHandle(origToken);
*/
return r;
}
+
+/*
+ * Get a list of privileges to delete from the access token. We delete all privileges
+ * except SeLockMemoryPrivilege which is needed to use large pages, and
+ * SeChangeNotifyPrivilege which is enabled by default in DISABLE_MAX_PRIVILEGE.
+ */
+static PTOKEN_PRIVILEGES
+GetPrivilegesToDelete(HANDLE hToken)
+{
+ int i, j;
+ DWORD length;
+ PTOKEN_PRIVILEGES tokenPrivs;
+ LUID luidLockPages;
+ LUID luidChangeNotify;
+
+ if (!LookupPrivilegeValue(NULL, SE_LOCK_MEMORY_NAME, &luidLockPages) ||
+ !LookupPrivilegeValue(NULL, SE_CHANGE_NOTIFY_NAME, &luidChangeNotify))
+ {
+ write_stderr(_("%s: could not get LUIDs for privileges: error code %lu\n"),
+ progname, (unsigned long) GetLastError());
+ return NULL;
+ }
+
+ if (!GetTokenInformation(hToken, TokenPrivileges, NULL, 0, &length) &&
+ GetLastError() != ERROR_INSUFFICIENT_BUFFER)
+ {
+ write_stderr(_("%s: could not get token information: error code %lu\n"),
+ progname, (unsigned long) GetLastError());
+ return NULL;
+ }
+
+ tokenPrivs = (PTOKEN_PRIVILEGES) malloc(length);
+ if (tokenPrivs == NULL)
+ {
+ write_stderr(_("%s: out of memory\n"), progname);
+ return NULL;
+ }
+
+ if (!GetTokenInformation(hToken, TokenPrivileges, tokenPrivs, length, &length))
+ {
+ write_stderr(_("%s: could not get token information: error code %lu\n"),
+ progname, (unsigned long) GetLastError());
+ free(tokenPrivs);
+ return NULL;
+ }
+
+ for (i = 0; i < tokenPrivs->PrivilegeCount; i++)
+ {
+ if (memcmp(&tokenPrivs->Privileges[i].Luid, &luidLockPages, sizeof(LUID)) == 0 ||
+ memcmp(&tokenPrivs->Privileges[i].Luid, &luidChangeNotify, sizeof(LUID)) == 0)
+ {
+ for (j = i; j < tokenPrivs->PrivilegeCount - 1; j++)
+ tokenPrivs->Privileges[j] = tokenPrivs->Privileges[j + 1];
+ tokenPrivs->PrivilegeCount--;
+ }
+ }
+
+ return tokenPrivs;
+}
#endif /* WIN32 */
static void