More swapping fixes
[reactos.git] / reactos / ntoskrnl / mm / pagefile.c
1 /*
2 * ReactOS kernel
3 * Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19 /* $Id: pagefile.c,v 1.15 2002/01/08 00:49:00 dwelch Exp $
20 *
21 * PROJECT: ReactOS kernel
22 * FILE: ntoskrnl/mm/pagefile.c
23 * PURPOSE: Paging file functions
24 * PROGRAMMER: David Welch (welch@mcmail.com)
25 * UPDATE HISTORY:
26 * Created 22/05/98
27 */
28
29 /* INCLUDES *****************************************************************/
30
31 #include <ddk/ntddk.h>
32 #include <internal/io.h>
33 #include <internal/mm.h>
34 #include <napi/core.h>
35
36 #include <internal/debug.h>
37
38 /* TYPES *********************************************************************/
39
40 typedef struct _PAGINGFILE
41 {
42 LIST_ENTRY PagingFileListEntry;
43 PFILE_OBJECT FileObject;
44 ULONG MaximumSize;
45 ULONG CurrentSize;
46 ULONG FreePages;
47 ULONG UsedPages;
48 PULONG AllocMap;
49 KSPIN_LOCK AllocMapLock;
50 ULONG AllocMapSize;
51 } PAGINGFILE, *PPAGINGFILE;
52
53 /* GLOBALS *******************************************************************/
54
55 #define MAX_PAGING_FILES (32)
56
57 /* List of paging files, both used and free */
58 static PPAGINGFILE PagingFileList[MAX_PAGING_FILES];
59
60 /* Lock for examining the list of paging files */
61 static KSPIN_LOCK PagingFileListLock;
62
63 /* Number of pages that are available for swapping */
64 static ULONG MiFreeSwapPages;
65
66 /* Number of pages that have been allocated for swapping */
67 static ULONG MiUsedSwapPages;
68
69 /*
70 * Number of pages that have been reserved for swapping but not yet allocated
71 */
72 static ULONG MiReservedSwapPages;
73
74 /*
75 * Ratio between reserved and available swap pages, e.g. setting this to five
76 * forces one swap page to be available for every five swap pages that are
77 * reserved. Setting this to zero turns off commit checking altogether.
78 */
79 #define MM_PAGEFILE_COMMIT_RATIO (1)
80
81 /*
82 * Number of pages that can be used for potentially swapable memory without
83 * pagefile space being reserved. The intention is that this allows smss
84 * to start up and create page files while ordinarily having a commit
85 * ratio of one.
86 */
87 #define MM_PAGEFILE_COMMIT_GRACE (256)
88
89 #if 0
90 static PVOID MmCoreDumpPageFrame;
91 static BYTE MmCoreDumpHeader[PAGESIZE];
92 #endif
93
94 /*
95 * Translate between a swap entry and a file and offset pair.
96 */
97 #define FILE_FROM_ENTRY(i) ((i) >> 24)
98 #define OFFSET_FROM_ENTRY(i) (((i) & 0xffffff) - 1)
99 #define ENTRY_FROM_FILE_OFFSET(i, j) (((i) << 24) | ((j) + 1))
100
101 /* FUNCTIONS *****************************************************************/
102
103 NTSTATUS MmWriteToSwapPage(SWAPENTRY SwapEntry, PMDL Mdl)
104 {
105 ULONG i, offset;
106 LARGE_INTEGER file_offset;
107 IO_STATUS_BLOCK Iosb;
108 NTSTATUS Status;
109
110 if (SwapEntry == 0)
111 {
112 KeBugCheck(0);
113 return(STATUS_UNSUCCESSFUL);
114 }
115
116 i = FILE_FROM_ENTRY(SwapEntry);
117 offset = OFFSET_FROM_ENTRY(SwapEntry);
118
119 if (i > MAX_PAGING_FILES)
120 {
121 DPRINT1("Bad swap entry 0x%.8X\n", SwapEntry);
122 KeBugCheck(0);
123 }
124 if (PagingFileList[i]->FileObject == NULL ||
125 PagingFileList[i]->FileObject->DeviceObject == NULL)
126 {
127 DPRINT1("Bad paging file 0x%.8X\n", SwapEntry);
128 KeBugCheck(0);
129 }
130
131 file_offset.QuadPart = offset * 4096;
132
133 Status = IoPageWrite(PagingFileList[i]->FileObject,
134 Mdl,
135 &file_offset,
136 &Iosb,
137 TRUE);
138 return(Status);
139 }
140
141 NTSTATUS MmReadFromSwapPage(SWAPENTRY SwapEntry, PMDL Mdl)
142 {
143 ULONG i, offset;
144 LARGE_INTEGER file_offset;
145 IO_STATUS_BLOCK Iosb;
146 NTSTATUS Status;
147
148 if (SwapEntry == 0)
149 {
150 KeBugCheck(0);
151 return(STATUS_UNSUCCESSFUL);
152 }
153
154 i = FILE_FROM_ENTRY(SwapEntry);
155 offset = OFFSET_FROM_ENTRY(SwapEntry);
156
157 if (i > MAX_PAGING_FILES)
158 {
159 DPRINT1("Bad swap entry 0x%.8X\n", SwapEntry);
160 KeBugCheck(0);
161 }
162 if (PagingFileList[i]->FileObject == NULL ||
163 PagingFileList[i]->FileObject->DeviceObject == NULL)
164 {
165 DPRINT1("Bad paging file 0x%.8X\n", SwapEntry);
166 KeBugCheck(0);
167 }
168
169 file_offset.QuadPart = offset * 4096;
170
171 Status = IoPageRead(PagingFileList[i]->FileObject,
172 Mdl,
173 &file_offset,
174 &Iosb,
175 TRUE);
176 return(Status);
177 }
178
179 VOID
180 MmInitPagingFile(VOID)
181 {
182 ULONG i;
183
184 KeInitializeSpinLock(&PagingFileListLock);
185
186 MiFreeSwapPages = 0;
187 MiUsedSwapPages = 0;
188 MiReservedSwapPages = 0;
189
190 for (i = 0; i < MAX_PAGING_FILES; i++)
191 {
192 PagingFileList[i] = NULL;
193 }
194 }
195
196 BOOLEAN
197 MmReserveSwapPages(ULONG Nr)
198 {
199 KIRQL oldIrql;
200 ULONG MiAvailSwapPages;
201
202 KeAcquireSpinLock(&PagingFileListLock, &oldIrql);
203 MiAvailSwapPages =
204 (MiFreeSwapPages * MM_PAGEFILE_COMMIT_RATIO) + MM_PAGEFILE_COMMIT_GRACE;
205 if (MM_PAGEFILE_COMMIT_RATIO != 0 && MiAvailSwapPages < MiReservedSwapPages)
206 {
207 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
208 return(FALSE);
209 }
210 MiReservedSwapPages = MiReservedSwapPages + Nr;
211 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
212
213 return(TRUE);
214 }
215
216 VOID
217 MmDereserveSwapPages(ULONG Nr)
218 {
219 KIRQL oldIrql;
220
221 KeAcquireSpinLock(&PagingFileListLock, &oldIrql);
222 MiReservedSwapPages = MiReservedSwapPages - Nr;
223 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
224 }
225
226 static ULONG
227 MiAllocPageFromPagingFile(PPAGINGFILE PagingFile)
228 {
229 KIRQL oldIrql;
230 ULONG i, j;
231
232 KeAcquireSpinLock(&PagingFile->AllocMapLock, &oldIrql);
233
234 for (i = 0; i < PagingFile->AllocMapSize; i++)
235 {
236 for (j = 0; j < 32; j++)
237 {
238 if (!(PagingFile->AllocMap[i] & (1 << j)))
239 {
240 break;
241 }
242 }
243 if (j == 32)
244 {
245 continue;
246 }
247 PagingFile->AllocMap[i] |= (1 << j);
248 PagingFile->UsedPages++;
249 PagingFile->FreePages--;
250 KeReleaseSpinLock(&PagingFile->AllocMapLock, oldIrql);
251 return((i * 32) + j);
252 }
253
254 KeReleaseSpinLock(&PagingFile->AllocMapLock, oldIrql);
255 return(0xFFFFFFFF);
256 }
257
258 VOID
259 MmFreeSwapPage(SWAPENTRY Entry)
260 {
261 ULONG i;
262 ULONG off;
263 KIRQL oldIrql;
264
265 i = FILE_FROM_ENTRY(Entry);
266 off = OFFSET_FROM_ENTRY(Entry);
267
268 KeAcquireSpinLock(&PagingFileListLock, &oldIrql);
269 if (PagingFileList[i] == NULL)
270 {
271 KeBugCheck(0);
272 }
273 KeAcquireSpinLockAtDpcLevel(&PagingFileList[i]->AllocMapLock);
274
275 PagingFileList[i]->AllocMap[off / 32] &= (~(1 << (off % 32)));
276
277 PagingFileList[i]->FreePages++;
278 PagingFileList[i]->UsedPages--;
279
280 MiFreeSwapPages++;
281 MiUsedSwapPages--;
282
283 KeReleaseSpinLockFromDpcLevel(&PagingFileList[i]->AllocMapLock);
284 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
285 }
286
287 SWAPENTRY
288 MmAllocSwapPage(VOID)
289 {
290 KIRQL oldIrql;
291 ULONG i;
292 ULONG off;
293 SWAPENTRY entry;
294 static BOOLEAN SwapSpaceMessage = FALSE;
295
296 KeAcquireSpinLock(&PagingFileListLock, &oldIrql);
297
298 if (MiFreeSwapPages == 0)
299 {
300 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
301 if (!SwapSpaceMessage)
302 {
303 DPRINT1("MM: Out of swap space.\n");
304 SwapSpaceMessage = TRUE;
305 }
306 return(0);
307 }
308
309 for (i = 0; i < MAX_PAGING_FILES; i++)
310 {
311 if (PagingFileList[i] != NULL &&
312 PagingFileList[i]->FreePages >= 1)
313 {
314 off = MiAllocPageFromPagingFile(PagingFileList[i]);
315 if (off == 0xFFFFFFFF)
316 {
317 KeBugCheck(0);
318 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
319 return(STATUS_UNSUCCESSFUL);
320 }
321 MiUsedSwapPages++;
322 MiFreeSwapPages--;
323 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
324
325 entry = ENTRY_FROM_FILE_OFFSET(i, off);
326 return(entry);
327 }
328 }
329
330 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
331 if (!SwapSpaceMessage)
332 {
333 DPRINT1("MM: Out of swap space.\n");
334 SwapSpaceMessage = TRUE;
335 }
336 return(0);
337 }
338
339 #if 0
340 NTSTATUS STDCALL MmDumpToPagingFile(PCONTEXT Context,
341 ULONG BugCode,
342 ULONG ExceptionCode,
343 ULONG Cr2)
344 {
345 ((PMM_CORE_DUMP_HEADER)MmCoreDumpHeader)->Magic =
346 MM_CORE_DUMP_HEADER_MAGIC;
347 ((PMM_CORE_DUMP_HEADER)MmCoreDumpHeader)->Version =
348 MM_CORE_DUMP_HEADER_VERSION;
349 memcpy(&((PMM_CORE_DUMP_HEADER)MmCoreDumpHeader)->Context,
350 Context,
351 sizeof(CONTEXT));
352 ((PMM_CORE_DUMP_HEADER)MmCoreDumpHeader)->DumpLength = 0;
353 ((PMM_CORE_DUMP_HEADER)MmCoreDumpHeader)->BugCode = BugCode;
354 ((PMM_CORE_DUMP_HEADER)MmCoreDumpHeader)->ExceptionCode =
355 ExceptionCode;
356 ((PMM_CORE_DUMP_HEADER)MmCoreDumpHeader)->Cr2 = Cr2;
357 ((PMM_CORE_DUMP_HEADER)MmCoreDumpHeader)->Cr3 = 0;
358 }
359 #endif
360
361 NTSTATUS STDCALL
362 NtCreatePagingFile(IN PUNICODE_STRING PageFileName,
363 IN ULONG MinimumSize,
364 IN ULONG MaximumSize,
365 OUT PULONG ActualSize)
366 {
367 NTSTATUS Status;
368 OBJECT_ATTRIBUTES ObjectAttributes;
369 HANDLE FileHandle;
370 IO_STATUS_BLOCK IoStatus;
371 PFILE_OBJECT FileObject;
372 PPAGINGFILE PagingFile;
373 KIRQL oldIrql;
374 ULONG AllocMapSize;
375 ULONG i;
376 PVOID Buffer;
377 LARGE_INTEGER ByteOffset;
378
379 DPRINT1("NtCreatePagingFile(PageFileName %wZ, MinimumSize %d)\n",
380 PageFileName, MinimumSize);
381
382 InitializeObjectAttributes(&ObjectAttributes,
383 PageFileName,
384 0,
385 NULL,
386 NULL);
387
388 Status = IoCreateFile(&FileHandle,
389 FILE_ALL_ACCESS,
390 &ObjectAttributes,
391 &IoStatus,
392 NULL,
393 0,
394 0,
395 FILE_OPEN_IF,
396 FILE_SYNCHRONOUS_IO_NONALERT,
397 NULL,
398 0,
399 CreateFileTypeNone,
400 NULL,
401 SL_OPEN_PAGING_FILE);
402 if (!NT_SUCCESS(Status))
403 {
404 return(Status);
405 }
406
407 Buffer = ExAllocatePool(NonPagedPool, 4096);
408 memset(Buffer, 0, 4096);
409 ByteOffset.QuadPart = MinimumSize * 4096;
410 Status = NtWriteFile(FileHandle,
411 NULL,
412 NULL,
413 NULL,
414 &IoStatus,
415 Buffer,
416 4096,
417 &ByteOffset,
418 NULL);
419 if (!NT_SUCCESS(Status))
420 {
421 NtClose(FileHandle);
422 return(Status);
423 }
424
425 Status = ObReferenceObjectByHandle(FileHandle,
426 FILE_ALL_ACCESS,
427 IoFileObjectType,
428 UserMode,
429 (PVOID*)&FileObject,
430 NULL);
431 if (!NT_SUCCESS(Status))
432 {
433 NtClose(FileHandle);
434 return(Status);
435 }
436
437 NtClose(FileHandle);
438
439 PagingFile = ExAllocatePool(NonPagedPool, sizeof(*PagingFile));
440 if (PagingFile == NULL)
441 {
442 ObDereferenceObject(FileObject);
443 return(STATUS_NO_MEMORY);
444 }
445
446 PagingFile->FileObject = FileObject;
447 PagingFile->MaximumSize = PagingFile->CurrentSize = MinimumSize;
448 PagingFile->FreePages = MinimumSize;
449 PagingFile->UsedPages = 0;
450 KeInitializeSpinLock(&PagingFile->AllocMapLock);
451
452 AllocMapSize = (MinimumSize / 32) + 1;
453 PagingFile->AllocMap = ExAllocatePool(NonPagedPool,
454 AllocMapSize * sizeof(ULONG));
455 PagingFile->AllocMapSize = AllocMapSize;
456
457 if (PagingFile->AllocMap == NULL)
458 {
459 ExFreePool(PagingFile);
460 ObDereferenceObject(FileObject);
461 return(STATUS_NO_MEMORY);
462 }
463
464 KeAcquireSpinLock(&PagingFileListLock, &oldIrql);
465 for (i = 0; i < MAX_PAGING_FILES; i++)
466 {
467 if (PagingFileList[i] == NULL)
468 {
469 PagingFileList[i] = PagingFile;
470 break;
471 }
472 }
473 MiFreeSwapPages = MiFreeSwapPages + MinimumSize;
474 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
475
476 return(STATUS_SUCCESS);
477 }
478
479
480 /* EOF */
481
482
483
484
485
486
487