Skip to content

Commit 1cc4f53

Browse files
committed
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
1 parent 5c15a54 commit 1cc4f53

File tree

4 files changed

+204
-22
lines changed

4 files changed

+204
-22
lines changed

doc/src/sgml/config.sgml

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1369,14 +1369,26 @@ include_dir 'conf.d'
13691369
</para>
13701370

13711371
<para>
1372-
At present, this feature is supported only on Linux. The setting is
1373-
ignored on other systems when set to <literal>try</literal>.
1372+
At present, this feature is supported only on Linux and Windows. The
1373+
setting is ignored on other systems when set to <literal>try</literal>.
13741374
</para>
13751375

13761376
<para>
13771377
The use of huge pages results in smaller page tables and less CPU time
1378-
spent on memory management, increasing performance. For more details,
1379-
see <xref linkend="linux-huge-pages"/>.
1378+
spent on memory management, increasing performance. For more details about
1379+
using huge pages on Linux, see <xref linkend="linux-huge-pages"/>.
1380+
</para>
1381+
1382+
<para>
1383+
Huge pages are known as large pages on Windows. To use them, you need to
1384+
assign the user right Lock Pages in Memory to the Windows user account
1385+
that runs <productname>PostgreSQL</productname>.
1386+
You can use Windows Group Policy tool (gpedit.msc) to assign the user right
1387+
Lock Pages in Memory.
1388+
To start the database server on the command prompt as a standalone process,
1389+
not as a Windows service, the command prompt must be run as an administrator
1390+
User Access Control (UAC) must be disabled. When the UAC is enabled, the normal
1391+
command prompt revokes the user right Lock Pages in Memory when started.
13801392
</para>
13811393

13821394
<para>

src/backend/port/win32_shmem.c

Lines changed: 117 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ HANDLE UsedShmemSegID = INVALID_HANDLE_VALUE;
2121
void *UsedShmemSegAddr = NULL;
2222
static Size UsedShmemSegSize = 0;
2323

24+
static bool EnableLockPagesPrivilege(int elevel);
2425
static void pgwin32_SharedMemoryDelete(int status, Datum shmId);
2526

2627
/*
@@ -103,6 +104,66 @@ PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2)
103104
return true;
104105
}
105106

107+
/*
108+
* EnableLockPagesPrivilege
109+
*
110+
* Try to acquire SeLockMemoryPrivilege so we can use large pages.
111+
*/
112+
static bool
113+
EnableLockPagesPrivilege(int elevel)
114+
{
115+
HANDLE hToken;
116+
TOKEN_PRIVILEGES tp;
117+
LUID luid;
118+
119+
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
120+
{
121+
ereport(elevel,
122+
(errmsg("could not enable Lock Pages in Memory user right: error code %lu", GetLastError()),
123+
errdetail("Failed system call was %s.", "OpenProcessToken")));
124+
return FALSE;
125+
}
126+
127+
if (!LookupPrivilegeValue(NULL, SE_LOCK_MEMORY_NAME, &luid))
128+
{
129+
ereport(elevel,
130+
(errmsg("could not enable Lock Pages in Memory user right: error code %lu", GetLastError()),
131+
errdetail("Failed system call was %s.", "LookupPrivilegeValue")));
132+
CloseHandle(hToken);
133+
return FALSE;
134+
}
135+
tp.PrivilegeCount = 1;
136+
tp.Privileges[0].Luid = luid;
137+
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
138+
139+
if (!AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL))
140+
{
141+
ereport(elevel,
142+
(errmsg("could not enable Lock Pages in Memory user right: error code %lu", GetLastError()),
143+
errdetail("Failed system call was %s.", "AdjustTokenPrivileges")));
144+
CloseHandle(hToken);
145+
return FALSE;
146+
}
147+
148+
if (GetLastError() != ERROR_SUCCESS)
149+
{
150+
if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)
151+
ereport(elevel,
152+
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
153+
errmsg("could not enable Lock Pages in Memory user right"),
154+
errhint("Assign Lock Pages in Memory user right to the Windows user account which runs PostgreSQL.")));
155+
else
156+
ereport(elevel,
157+
(errmsg("could not enable Lock Pages in Memory user right: error code %lu", GetLastError()),
158+
errdetail("Failed system call was %s.", "AdjustTokenPrivileges")));
159+
CloseHandle(hToken);
160+
return FALSE;
161+
}
162+
163+
CloseHandle(hToken);
164+
165+
return TRUE;
166+
}
106167

107168
/*
108169
* PGSharedMemoryCreate
@@ -127,11 +188,9 @@ PGSharedMemoryCreate(Size size, bool makePrivate, int port,
127188
int i;
128189
DWORD size_high;
129190
DWORD size_low;
130-
131-
if (huge_pages == HUGE_PAGES_ON)
132-
ereport(ERROR,
133-
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
134-
errmsg("huge pages not supported on this platform")));
191+
SIZE_T largePageSize = 0;
192+
Size orig_size = size;
193+
DWORD flProtect = PAGE_READWRITE;
135194

136195
/* Room for a header? */
137196
Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
@@ -140,6 +199,35 @@ PGSharedMemoryCreate(Size size, bool makePrivate, int port,
140199

141200
UsedShmemSegAddr = NULL;
142201

202+
if (huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY)
203+
{
204+
/* Does the processor support large pages? */
205+
largePageSize = GetLargePageMinimum();
206+
if (largePageSize == 0)
207+
{
208+
ereport(huge_pages == HUGE_PAGES_ON ? FATAL : DEBUG1,
209+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
210+
errmsg("the processor does not support large pages")));
211+
ereport(DEBUG1,
212+
(errmsg("disabling huge pages")));
213+
}
214+
else if (!EnableLockPagesPrivilege(huge_pages == HUGE_PAGES_ON ? FATAL : DEBUG1))
215+
{
216+
ereport(DEBUG1,
217+
(errmsg("disabling huge pages")));
218+
}
219+
else
220+
{
221+
/* Huge pages available and privilege enabled, so turn on */
222+
flProtect = PAGE_READWRITE | SEC_COMMIT | SEC_LARGE_PAGES;
223+
224+
/* Round size up as appropriate. */
225+
if (size % largePageSize != 0)
226+
size += largePageSize - (size % largePageSize);
227+
}
228+
}
229+
230+
retry:
143231
#ifdef _WIN64
144232
size_high = size >> 32;
145233
#else
@@ -163,16 +251,35 @@ PGSharedMemoryCreate(Size size, bool makePrivate, int port,
163251

164252
hmap = CreateFileMapping(INVALID_HANDLE_VALUE, /* Use the pagefile */
165253
NULL, /* Default security attrs */
166-
PAGE_READWRITE, /* Memory is Read/Write */
254+
flProtect,
167255
size_high, /* Size Upper 32 Bits */
168256
size_low, /* Size Lower 32 bits */
169257
szShareMem);
170258

171259
if (!hmap)
172-
ereport(FATAL,
173-
(errmsg("could not create shared memory segment: error code %lu", GetLastError()),
174-
errdetail("Failed system call was CreateFileMapping(size=%zu, name=%s).",
175-
size, szShareMem)));
260+
{
261+
if (GetLastError() == ERROR_NO_SYSTEM_RESOURCES &&
262+
huge_pages == HUGE_PAGES_TRY &&
263+
(flProtect & SEC_LARGE_PAGES) != 0)
264+
{
265+
elog(DEBUG1, "CreateFileMapping(%zu) with SEC_LARGE_PAGES failed, "
266+
"huge pages disabled",
267+
size);
268+
269+
/*
270+
* Use the original size, not the rounded-up value, when falling back
271+
* to non-huge pages.
272+
*/
273+
size = orig_size;
274+
flProtect = PAGE_READWRITE;
275+
goto retry;
276+
}
277+
else
278+
ereport(FATAL,
279+
(errmsg("could not create shared memory segment: error code %lu", GetLastError()),
280+
errdetail("Failed system call was CreateFileMapping(size=%zu, name=%s).",
281+
size, szShareMem)));
282+
}
176283

177284
/*
178285
* If the segment already existed, CreateFileMapping() will return a

src/backend/utils/misc/guc.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3913,7 +3913,7 @@ static struct config_enum ConfigureNamesEnum[] =
39133913

39143914
{
39153915
{"huge_pages", PGC_POSTMASTER, RESOURCES_MEM,
3916-
gettext_noop("Use of huge pages on Linux."),
3916+
gettext_noop("Use of huge pages on Linux or Windows."),
39173917
NULL
39183918
},
39193919
&huge_pages,

src/bin/pg_ctl/pg_ctl.c

Lines changed: 70 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ static void WINAPI pgwin32_ServiceHandler(DWORD);
144144
static void WINAPI pgwin32_ServiceMain(DWORD, LPTSTR *);
145145
static void pgwin32_doRunAsService(void);
146146
static int CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION *processInfo, bool as_service);
147+
static PTOKEN_PRIVILEGES GetPrivilegesToDelete(HANDLE hToken);
147148
#endif
148149

149150
static pgpid_t get_pgpid(bool is_status_request);
@@ -1623,11 +1624,6 @@ typedef BOOL (WINAPI * __SetInformationJobObject) (HANDLE, JOBOBJECTINFOCLASS, L
16231624
typedef BOOL (WINAPI * __AssignProcessToJobObject) (HANDLE, HANDLE);
16241625
typedef BOOL (WINAPI * __QueryInformationJobObject) (HANDLE, JOBOBJECTINFOCLASS, LPVOID, DWORD, LPDWORD);
16251626

1626-
/* Windows API define missing from some versions of MingW headers */
1627-
#ifndef DISABLE_MAX_PRIVILEGE
1628-
#define DISABLE_MAX_PRIVILEGE 0x1
1629-
#endif
1630-
16311627
/*
16321628
* Create a restricted token, a job object sandbox, and execute the specified
16331629
* process with it.
@@ -1650,6 +1646,7 @@ CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION *processInfo, bool as_ser
16501646
HANDLE restrictedToken;
16511647
SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY};
16521648
SID_AND_ATTRIBUTES dropSids[2];
1649+
PTOKEN_PRIVILEGES delPrivs;
16531650

16541651
/* Functions loaded dynamically */
16551652
__CreateRestrictedToken _CreateRestrictedToken = NULL;
@@ -1708,14 +1705,21 @@ CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION *processInfo, bool as_ser
17081705
return 0;
17091706
}
17101707

1708+
/* Get list of privileges to remove */
1709+
delPrivs = GetPrivilegesToDelete(origToken);
1710+
if (delPrivs == NULL)
1711+
/* Error message already printed */
1712+
return 0;
1713+
17111714
b = _CreateRestrictedToken(origToken,
1712-
DISABLE_MAX_PRIVILEGE,
1715+
0,
17131716
sizeof(dropSids) / sizeof(dropSids[0]),
17141717
dropSids,
1715-
0, NULL,
1718+
delPrivs->PrivilegeCount, delPrivs->Privileges,
17161719
0, NULL,
17171720
&restrictedToken);
17181721

1722+
free(delPrivs);
17191723
FreeSid(dropSids[1].Sid);
17201724
FreeSid(dropSids[0].Sid);
17211725
CloseHandle(origToken);
@@ -1832,6 +1836,65 @@ CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION *processInfo, bool as_ser
18321836
*/
18331837
return r;
18341838
}
1839+
1840+
/*
1841+
* Get a list of privileges to delete from the access token. We delete all privileges
1842+
* except SeLockMemoryPrivilege which is needed to use large pages, and
1843+
* SeChangeNotifyPrivilege which is enabled by default in DISABLE_MAX_PRIVILEGE.
1844+
*/
1845+
static PTOKEN_PRIVILEGES
1846+
GetPrivilegesToDelete(HANDLE hToken)
1847+
{
1848+
int i, j;
1849+
DWORD length;
1850+
PTOKEN_PRIVILEGES tokenPrivs;
1851+
LUID luidLockPages;
1852+
LUID luidChangeNotify;
1853+
1854+
if (!LookupPrivilegeValue(NULL, SE_LOCK_MEMORY_NAME, &luidLockPages) ||
1855+
!LookupPrivilegeValue(NULL, SE_CHANGE_NOTIFY_NAME, &luidChangeNotify))
1856+
{
1857+
write_stderr(_("%s: could not get LUIDs for privileges: error code %lu\n"),
1858+
progname, (unsigned long) GetLastError());
1859+
return NULL;
1860+
}
1861+
1862+
if (!GetTokenInformation(hToken, TokenPrivileges, NULL, 0, &length) &&
1863+
GetLastError() != ERROR_INSUFFICIENT_BUFFER)
1864+
{
1865+
write_stderr(_("%s: could not get token information: error code %lu\n"),
1866+
progname, (unsigned long) GetLastError());
1867+
return NULL;
1868+
}
1869+
1870+
tokenPrivs = (PTOKEN_PRIVILEGES) malloc(length);
1871+
if (tokenPrivs == NULL)
1872+
{
1873+
write_stderr(_("%s: out of memory\n"), progname);
1874+
return NULL;
1875+
}
1876+
1877+
if (!GetTokenInformation(hToken, TokenPrivileges, tokenPrivs, length, &length))
1878+
{
1879+
write_stderr(_("%s: could not get token information: error code %lu\n"),
1880+
progname, (unsigned long) GetLastError());
1881+
free(tokenPrivs);
1882+
return NULL;
1883+
}
1884+
1885+
for (i = 0; i < tokenPrivs->PrivilegeCount; i++)
1886+
{
1887+
if (memcmp(&tokenPrivs->Privileges[i].Luid, &luidLockPages, sizeof(LUID)) == 0 ||
1888+
memcmp(&tokenPrivs->Privileges[i].Luid, &luidChangeNotify, sizeof(LUID)) == 0)
1889+
{
1890+
for (j = i; j < tokenPrivs->PrivilegeCount - 1; j++)
1891+
tokenPrivs->Privileges[j] = tokenPrivs->Privileges[j + 1];
1892+
tokenPrivs->PrivilegeCount--;
1893+
}
1894+
}
1895+
1896+
return tokenPrivs;
1897+
}
18351898
#endif /* WIN32 */
18361899

18371900
static void

0 commit comments

Comments
 (0)