Better page file creation that allows for system managed setting. A lot of thanks...
[reactos.git] / reactos / subsys / smss / initpage.c
1 /* $Id$
2 *
3 * initpage.c -
4 *
5 * ReactOS Operating System
6 *
7 * --------------------------------------------------------------------
8 *
9 * This software is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation; either version 2 of the
12 * License, or (at your option) any later version.
13 *
14 * This software is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this software; see the file COPYING.LIB. If not, write
21 * to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
22 * MA 02139, USA.
23 *
24 * --------------------------------------------------------------------
25 */
26 #include "smss.h"
27
28 #define NDEBUG
29 #include <debug.h>
30
31 #define GIGABYTE (1024 * 1024 * 1024) /* One Gigabyte */
32
33 static NTSTATUS STDCALL
34 SmpPagingFilesQueryRoutine(PWSTR ValueName,
35 ULONG ValueType,
36 PVOID ValueData,
37 ULONG ValueLength,
38 PVOID Context,
39 PVOID EntryContext)
40 {
41 UNICODE_STRING FileName;
42 LARGE_INTEGER InitialSize = {{0, 0}};
43 LARGE_INTEGER MaximumSize = {{0, 0}};
44 NTSTATUS Status = STATUS_SUCCESS;
45 PWSTR p, ValueString = (PWSTR)ValueData;
46 WCHAR RootDriveLetter[5] = {0};
47
48 if (ValueLength > 3 * sizeof(WCHAR) &&
49 (ValueLength % sizeof(WCHAR) != 0 ||
50 ValueString[(ValueLength / sizeof(WCHAR)) - 1] != L'\0'))
51 {
52 return STATUS_INVALID_PARAMETER;
53 }
54
55 if (ValueType != REG_SZ)
56 {
57 return STATUS_INVALID_PARAMETER_2;
58 }
59
60 /*
61 * Format: "<path>[ <initial_size>[ <maximum_size>]]"
62 */
63 if ((p = wcschr(ValueString, L' ')) != NULL)
64 {
65 *p = L'\0';
66 InitialSize.QuadPart = wcstoul(p + 1, &p, 0) * 256 * 4096;
67 if (*p == ' ')
68 {
69 MaximumSize.QuadPart = wcstoul(p + 1, NULL, 0) * 256 * 4096;
70 }
71 else
72 {
73 MaximumSize = InitialSize;
74 }
75 }
76
77 if (!RtlDosPathNameToNtPathName_U (ValueString,
78 &FileName,
79 NULL,
80 NULL))
81 {
82 return STATUS_OBJECT_PATH_INVALID;
83 }
84
85 /* If there is only a file name or if initial and max are both 0
86 * the system will pick the sizes. Then it makes intial the size of phyical memory
87 * and makes max the size of 1.5 * initial. If there isnt enough free space then it will
88 * fall back to intial 20% of free space and max 25%. There is a max of 1 gig before
89 * it doesnt make it bigger. */
90 if ((InitialSize.QuadPart == 0 && MaximumSize.QuadPart == 0) || p == NULL)
91 {
92 FILE_FS_SIZE_INFORMATION FileFsSize;
93 IO_STATUS_BLOCK IoStatusBlock;
94 HANDLE hFile;
95 SYSTEM_BASIC_INFORMATION SysBasicInfo;
96 UNICODE_STRING NtPathU;
97 LARGE_INTEGER FreeBytes = {{0, 0}};
98 OBJECT_ATTRIBUTES ObjectAttributes;
99
100 DPRINT("System managed pagefile...\n");
101 /* Make sure the path that is given for the file actually has the drive in it.
102 At this point if there is not file name, no sizes will be set therefore no page
103 file will be created */
104 if (wcslen(ValueString) <= 3 ||
105 ValueString[1] != L':' ||
106 ValueString[2] != L'\\')
107 {
108 DPRINT1("Invalid path for pagefile.\n");
109 goto Cleanup;
110 }
111
112 Status = NtQuerySystemInformation(SystemBasicInformation,
113 &SysBasicInfo,
114 sizeof(SysBasicInfo),
115 NULL);
116 if (!NT_SUCCESS(Status))
117 {
118 DPRINT1("Could not query for physical memory size.\n");
119 goto Cleanup;
120 }
121 DPRINT("PageSize: %d, PhysicalPages: %d, TotalMem: %d\n", SysBasicInfo.PageSize, SysBasicInfo.NumberOfPhysicalPages, (SysBasicInfo.NumberOfPhysicalPages * SysBasicInfo.PageSize) / 1024);
122
123 InitialSize.QuadPart = SysBasicInfo.NumberOfPhysicalPages *
124 SysBasicInfo.PageSize;
125 MaximumSize.QuadPart = InitialSize.QuadPart * 2;
126
127 DPRINT("InitialSize: %I64d PhysicalPages: %lu PageSize: %lu\n",InitialSize.QuadPart,SysBasicInfo.NumberOfPhysicalPages,SysBasicInfo.PageSize);
128
129 /* copy the drive letter, the colon and the slash,
130 tack a null on the end */
131 RootDriveLetter[0] = ValueString[0];
132 RootDriveLetter[1] = L':';
133 RootDriveLetter[2] = L'\\';
134 RootDriveLetter[3] = L'\0';
135 DPRINT("Root drive X:\\...\"%S\"\n",RootDriveLetter);
136
137 if (!RtlDosPathNameToNtPathName_U(RootDriveLetter,
138 &NtPathU,
139 NULL,
140 NULL))
141 {
142 DPRINT1("Invalid path to root of drive\n");
143 Status = STATUS_OBJECT_PATH_INVALID;
144 goto Cleanup;
145 }
146
147 InitializeObjectAttributes(&ObjectAttributes,
148 &NtPathU,
149 OBJ_CASE_INSENSITIVE,
150 NULL,
151 NULL);
152
153 /* Get a handle to the root to find the free space on the drive */
154 Status = NtCreateFile(&hFile,
155 0,
156 &ObjectAttributes,
157 &IoStatusBlock,
158 NULL,
159 0,
160 FILE_SHARE_READ | FILE_SHARE_WRITE,
161 FILE_OPEN,
162 0,
163 NULL,
164 0);
165
166 RtlFreeUnicodeString(&NtPathU);
167
168 if (!NT_SUCCESS(Status))
169 {
170 DPRINT1("Could not open a handle to the volume.\n");
171 goto Cleanup;
172 }
173
174 Status = NtQueryVolumeInformationFile(hFile,
175 &IoStatusBlock,
176 &FileFsSize,
177 sizeof(FILE_FS_SIZE_INFORMATION),
178 FileFsSizeInformation);
179
180 NtClose(hFile);
181
182 if (!NT_SUCCESS(Status))
183 {
184 DPRINT1("Querying the volume free space failed!\n");
185 goto Cleanup;
186 }
187
188 FreeBytes.QuadPart = FileFsSize.BytesPerSector *
189 FileFsSize.SectorsPerAllocationUnit *
190 FileFsSize.AvailableAllocationUnits.QuadPart;
191
192 DPRINT("Free bytes: %I64d Inital Size based on memory: %I64d \n",FreeBytes.QuadPart,InitialSize.QuadPart);
193
194
195 if (InitialSize.QuadPart > (FreeBytes.QuadPart / 4) || InitialSize.QuadPart == 0)
196 {
197 DPRINT("Inital Size took more then 25%% of free space\n");
198 /* Set by percentage of free space
199 * intial is 20%, and max is 25% */
200 InitialSize.QuadPart = FreeBytes.QuadPart / 5;
201 MaximumSize.QuadPart = FreeBytes.QuadPart / 4;
202 /* The page file is more then a gig, size it down */
203 if (InitialSize.QuadPart > GIGABYTE)
204 {
205 InitialSize.QuadPart = GIGABYTE;
206 MaximumSize.QuadPart = GIGABYTE * 1.5;
207 }
208 }
209
210
211 }
212
213 /* Make sure that max is not smaller then initial */
214 if (InitialSize.QuadPart > MaximumSize.QuadPart)
215 {
216 DPRINT("Max page file size was bigger then inital.\n");
217 MaximumSize.QuadPart = InitialSize.QuadPart;
218 }
219
220 DPRINT1("SMSS: Created paging file %wZ with size %I64d KB\n",
221 &FileName, InitialSize.QuadPart / 1024);
222
223 Status = NtCreatePagingFile(&FileName,
224 &InitialSize,
225 &MaximumSize,
226 0);
227
228 Cleanup:
229 RtlFreeUnicodeString(&FileName);
230
231 return Status;
232 }
233
234
235 NTSTATUS
236 SmCreatePagingFiles(VOID)
237 {
238 RTL_QUERY_REGISTRY_TABLE QueryTable[2];
239 NTSTATUS Status;
240
241 DbgPrint("SM: creating system paging files\n");
242 /*
243 * Disable paging file on MiniNT/Live CD.
244 */
245 if (RtlCheckRegistryKey(RTL_REGISTRY_CONTROL, L"MiniNT") == STATUS_SUCCESS)
246 {
247 return STATUS_SUCCESS;
248 }
249
250 RtlZeroMemory(&QueryTable,
251 sizeof(QueryTable));
252
253 QueryTable[0].Name = L"PagingFiles";
254 QueryTable[0].QueryRoutine = SmpPagingFilesQueryRoutine;
255
256 Status = RtlQueryRegistryValues(RTL_REGISTRY_CONTROL,
257 L"\\Session Manager\\Memory Management",
258 QueryTable,
259 NULL,
260 NULL);
261
262 return(Status);
263 }
264
265
266 /* EOF */