mirror of
				https://github.com/postgres/postgres.git
				synced 2025-11-04 00:02:52 -05:00 
			
		
		
		
	Support huge pages on Windows
Add support for huge pages (called large pages on Windows) to the Windows build. This (probably) breaks compatibility with Windows versions prior to Windows 2003 or Windows Vista. Authors: Takayuki Tsunakawa and Thomas Munro Reviewed by: Magnus Hagander, Amit Kapila
This commit is contained in:
		
							parent
							
								
									5c15a54e85
								
							
						
					
					
						commit
						1cc4f536ef
					
				@ -1369,14 +1369,26 @@ include_dir 'conf.d'
 | 
			
		||||
       </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>
 | 
			
		||||
 | 
			
		||||
@ -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(%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
 | 
			
		||||
 | 
			
		||||
@ -3913,7 +3913,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 or Windows."),
 | 
			
		||||
			NULL
 | 
			
		||||
		},
 | 
			
		||||
		&huge_pages,
 | 
			
		||||
 | 
			
		||||
@ -144,6 +144,7 @@ static void WINAPI pgwin32_ServiceHandler(DWORD);
 | 
			
		||||
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);
 | 
			
		||||
@ -1623,11 +1624,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.
 | 
			
		||||
@ -1650,6 +1646,7 @@ CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION *processInfo, bool as_ser
 | 
			
		||||
	HANDLE		restrictedToken;
 | 
			
		||||
	SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY};
 | 
			
		||||
	SID_AND_ATTRIBUTES dropSids[2];
 | 
			
		||||
	PTOKEN_PRIVILEGES delPrivs;
 | 
			
		||||
 | 
			
		||||
	/* Functions loaded dynamically */
 | 
			
		||||
	__CreateRestrictedToken _CreateRestrictedToken = NULL;
 | 
			
		||||
@ -1708,14 +1705,21 @@ CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION *processInfo, bool as_ser
 | 
			
		||||
		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);
 | 
			
		||||
@ -1832,6 +1836,65 @@ CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION *processInfo, bool as_ser
 | 
			
		||||
	 */
 | 
			
		||||
	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
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user