1 /* 2 * COPYRIGHT: See COPYING in the top directory 3 * PROJECT: ReactOS kernel 4 * FILE: ntoskrnl/mm/rmap.c 5 * PURPOSE: Kernel memory managment functions 6 * 7 * PROGRAMMERS: David Welch (welch@cwcom.net) 8 */ 9 10 /* INCLUDES *****************************************************************/ 11 12 #include <ntoskrnl.h> 13 #include <cache/section/newmm.h> 14 #define NDEBUG 15 #include <debug.h> 16 17 /* TYPES ********************************************************************/ 18 19 /* GLOBALS ******************************************************************/ 20 21 static NPAGED_LOOKASIDE_LIST RmapLookasideList; 22 FAST_MUTEX RmapListLock; 23 24 /* FUNCTIONS ****************************************************************/ 25 26 _IRQL_requires_max_(DISPATCH_LEVEL) 27 static 28 VOID 29 NTAPI 30 RmapListFree( 31 _In_ __drv_freesMem(Mem) PVOID P) 32 { 33 ExFreePoolWithTag(P, TAG_RMAP); 34 } 35 36 CODE_SEG("INIT") 37 VOID 38 NTAPI 39 MmInitializeRmapList(VOID) 40 { 41 ExInitializeFastMutex(&RmapListLock); 42 ExInitializeNPagedLookasideList (&RmapLookasideList, 43 NULL, 44 RmapListFree, 45 0, 46 sizeof(MM_RMAP_ENTRY), 47 TAG_RMAP, 48 50); 49 } 50 51 NTSTATUS 52 NTAPI 53 MmPageOutPhysicalAddress(PFN_NUMBER Page) 54 { 55 PMM_RMAP_ENTRY entry; 56 PMEMORY_AREA MemoryArea; 57 PMMSUPPORT AddressSpace; 58 ULONG Type; 59 PVOID Address; 60 PEPROCESS Process; 61 ULONGLONG Offset; 62 NTSTATUS Status = STATUS_SUCCESS; 63 64 ExAcquireFastMutex(&RmapListLock); 65 entry = MmGetRmapListHeadPage(Page); 66 67 #ifdef NEWCC 68 // Special case for NEWCC: we can have a page that's only in a segment 69 // page table 70 if (entry && RMAP_IS_SEGMENT(entry->Address) && entry->Next == NULL) 71 { 72 /* NEWCC does locking itself */ 73 ExReleaseFastMutex(&RmapListLock); 74 return MmpPageOutPhysicalAddress(Page); 75 } 76 #endif 77 78 while (entry && RMAP_IS_SEGMENT(entry->Address)) 79 entry = entry->Next; 80 81 if (entry == NULL) 82 { 83 ExReleaseFastMutex(&RmapListLock); 84 return(STATUS_UNSUCCESSFUL); 85 } 86 87 Process = entry->Process; 88 89 Address = entry->Address; 90 91 if ((((ULONG_PTR)Address) & 0xFFF) != 0) 92 { 93 KeBugCheck(MEMORY_MANAGEMENT); 94 } 95 96 if (Address < MmSystemRangeStart) 97 { 98 if (!ExAcquireRundownProtection(&Process->RundownProtect)) 99 { 100 ExReleaseFastMutex(&RmapListLock); 101 return STATUS_PROCESS_IS_TERMINATING; 102 } 103 104 Status = ObReferenceObjectByPointer(Process, PROCESS_ALL_ACCESS, NULL, KernelMode); 105 ExReleaseFastMutex(&RmapListLock); 106 if (!NT_SUCCESS(Status)) 107 { 108 ExReleaseRundownProtection(&Process->RundownProtect); 109 return Status; 110 } 111 AddressSpace = &Process->Vm; 112 } 113 else 114 { 115 ExReleaseFastMutex(&RmapListLock); 116 AddressSpace = MmGetKernelAddressSpace(); 117 } 118 119 MmLockAddressSpace(AddressSpace); 120 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, Address); 121 if (MemoryArea == NULL || MemoryArea->DeleteInProgress) 122 { 123 MmUnlockAddressSpace(AddressSpace); 124 if (Address < MmSystemRangeStart) 125 { 126 ExReleaseRundownProtection(&Process->RundownProtect); 127 ObDereferenceObject(Process); 128 } 129 return(STATUS_UNSUCCESSFUL); 130 } 131 Type = MemoryArea->Type; 132 if (Type == MEMORY_AREA_SECTION_VIEW) 133 { 134 ULONG_PTR Entry; 135 Offset = MemoryArea->Data.SectionData.ViewOffset.QuadPart + 136 ((ULONG_PTR)Address - MA_GetStartingAddress(MemoryArea)); 137 138 MmLockSectionSegment(MemoryArea->Data.SectionData.Segment); 139 140 /* 141 * Get or create a pageop 142 */ 143 Entry = MmGetPageEntrySectionSegment(MemoryArea->Data.SectionData.Segment, 144 (PLARGE_INTEGER)&Offset); 145 if (Entry && MM_IS_WAIT_PTE(Entry)) 146 { 147 MmUnlockSectionSegment(MemoryArea->Data.SectionData.Segment); 148 MmUnlockAddressSpace(AddressSpace); 149 if (Address < MmSystemRangeStart) 150 { 151 ExReleaseRundownProtection(&Process->RundownProtect); 152 ObDereferenceObject(Process); 153 } 154 return(STATUS_UNSUCCESSFUL); 155 } 156 157 MmSetPageEntrySectionSegment(MemoryArea->Data.SectionData.Segment, (PLARGE_INTEGER)&Offset, MAKE_SWAP_SSE(MM_WAIT_ENTRY)); 158 159 /* 160 * Release locks now we have a page op. 161 */ 162 MmUnlockSectionSegment(MemoryArea->Data.SectionData.Segment); 163 MmUnlockAddressSpace(AddressSpace); 164 165 /* 166 * Do the actual page out work. 167 */ 168 Status = MmPageOutSectionView(AddressSpace, MemoryArea, Address, Entry); 169 } 170 else if (Type == MEMORY_AREA_CACHE) 171 { 172 /* NEWCC does locking itself */ 173 MmUnlockAddressSpace(AddressSpace); 174 Status = MmpPageOutPhysicalAddress(Page); 175 } 176 else 177 { 178 KeBugCheck(MEMORY_MANAGEMENT); 179 } 180 181 if (Address < MmSystemRangeStart) 182 { 183 ExReleaseRundownProtection(&Process->RundownProtect); 184 ObDereferenceObject(Process); 185 } 186 return(Status); 187 } 188 189 VOID 190 NTAPI 191 MmSetCleanAllRmaps(PFN_NUMBER Page) 192 { 193 PMM_RMAP_ENTRY current_entry; 194 195 ExAcquireFastMutex(&RmapListLock); 196 current_entry = MmGetRmapListHeadPage(Page); 197 if (current_entry == NULL) 198 { 199 DPRINT1("MmIsDirtyRmap: No rmaps.\n"); 200 KeBugCheck(MEMORY_MANAGEMENT); 201 } 202 while (current_entry != NULL) 203 { 204 if (!RMAP_IS_SEGMENT(current_entry->Address)) 205 MmSetCleanPage(current_entry->Process, current_entry->Address); 206 current_entry = current_entry->Next; 207 } 208 ExReleaseFastMutex(&RmapListLock); 209 } 210 211 VOID 212 NTAPI 213 MmSetDirtyAllRmaps(PFN_NUMBER Page) 214 { 215 PMM_RMAP_ENTRY current_entry; 216 217 ExAcquireFastMutex(&RmapListLock); 218 current_entry = MmGetRmapListHeadPage(Page); 219 if (current_entry == NULL) 220 { 221 DPRINT1("MmIsDirtyRmap: No rmaps.\n"); 222 KeBugCheck(MEMORY_MANAGEMENT); 223 } 224 while (current_entry != NULL) 225 { 226 if (!RMAP_IS_SEGMENT(current_entry->Address)) 227 MmSetDirtyPage(current_entry->Process, current_entry->Address); 228 current_entry = current_entry->Next; 229 } 230 ExReleaseFastMutex(&RmapListLock); 231 } 232 233 BOOLEAN 234 NTAPI 235 MmIsDirtyPageRmap(PFN_NUMBER Page) 236 { 237 PMM_RMAP_ENTRY current_entry; 238 239 ExAcquireFastMutex(&RmapListLock); 240 current_entry = MmGetRmapListHeadPage(Page); 241 if (current_entry == NULL) 242 { 243 ExReleaseFastMutex(&RmapListLock); 244 return(FALSE); 245 } 246 while (current_entry != NULL) 247 { 248 if ( 249 !RMAP_IS_SEGMENT(current_entry->Address) && 250 MmIsDirtyPage(current_entry->Process, current_entry->Address)) 251 { 252 ExReleaseFastMutex(&RmapListLock); 253 return(TRUE); 254 } 255 current_entry = current_entry->Next; 256 } 257 ExReleaseFastMutex(&RmapListLock); 258 return(FALSE); 259 } 260 261 VOID 262 NTAPI 263 MmInsertRmap(PFN_NUMBER Page, PEPROCESS Process, 264 PVOID Address) 265 { 266 PMM_RMAP_ENTRY current_entry; 267 PMM_RMAP_ENTRY new_entry; 268 ULONG PrevSize; 269 if (!RMAP_IS_SEGMENT(Address)) 270 Address = (PVOID)PAGE_ROUND_DOWN(Address); 271 272 new_entry = ExAllocateFromNPagedLookasideList(&RmapLookasideList); 273 if (new_entry == NULL) 274 { 275 KeBugCheck(MEMORY_MANAGEMENT); 276 } 277 new_entry->Address = Address; 278 new_entry->Process = (PEPROCESS)Process; 279 #if DBG 280 #ifdef __GNUC__ 281 new_entry->Caller = __builtin_return_address(0); 282 #else 283 new_entry->Caller = _ReturnAddress(); 284 #endif 285 #endif 286 287 if ( 288 !RMAP_IS_SEGMENT(Address) && 289 MmGetPfnForProcess(Process, Address) != Page) 290 { 291 DPRINT1("Insert rmap (%d, 0x%.8X) 0x%.8X which doesn't match physical " 292 "address 0x%.8X\n", Process ? Process->UniqueProcessId : 0, 293 Address, 294 MmGetPfnForProcess(Process, Address) << PAGE_SHIFT, 295 Page << PAGE_SHIFT); 296 KeBugCheck(MEMORY_MANAGEMENT); 297 } 298 299 ExAcquireFastMutex(&RmapListLock); 300 current_entry = MmGetRmapListHeadPage(Page); 301 new_entry->Next = current_entry; 302 #if DBG 303 while (current_entry) 304 { 305 if (current_entry->Address == new_entry->Address && current_entry->Process == new_entry->Process) 306 { 307 DbgPrint("MmInsertRmap tries to add a second rmap entry for address %p\n current caller ", 308 current_entry->Address); 309 DbgPrint("%p", new_entry->Caller); 310 DbgPrint("\n previous caller "); 311 DbgPrint("%p", current_entry->Caller); 312 DbgPrint("\n"); 313 KeBugCheck(MEMORY_MANAGEMENT); 314 } 315 current_entry = current_entry->Next; 316 } 317 #endif 318 MmSetRmapListHeadPage(Page, new_entry); 319 ExReleaseFastMutex(&RmapListLock); 320 if (!RMAP_IS_SEGMENT(Address)) 321 { 322 if (Process == NULL) 323 { 324 Process = PsInitialSystemProcess; 325 } 326 if (Process) 327 { 328 PrevSize = InterlockedExchangeAddUL(&Process->Vm.WorkingSetSize, PAGE_SIZE); 329 if (PrevSize >= Process->Vm.PeakWorkingSetSize) 330 { 331 Process->Vm.PeakWorkingSetSize = PrevSize + PAGE_SIZE; 332 } 333 } 334 } 335 } 336 337 VOID 338 NTAPI 339 MmDeleteAllRmaps(PFN_NUMBER Page, PVOID Context, 340 VOID (*DeleteMapping)(PVOID Context, PEPROCESS Process, 341 PVOID Address)) 342 { 343 PMM_RMAP_ENTRY current_entry; 344 PMM_RMAP_ENTRY previous_entry; 345 PEPROCESS Process; 346 347 ExAcquireFastMutex(&RmapListLock); 348 current_entry = MmGetRmapListHeadPage(Page); 349 if (current_entry == NULL) 350 { 351 DPRINT1("MmDeleteAllRmaps: No rmaps.\n"); 352 KeBugCheck(MEMORY_MANAGEMENT); 353 } 354 MmSetRmapListHeadPage(Page, NULL); 355 ExReleaseFastMutex(&RmapListLock); 356 357 while (current_entry != NULL) 358 { 359 previous_entry = current_entry; 360 current_entry = current_entry->Next; 361 if (!RMAP_IS_SEGMENT(previous_entry->Address)) 362 { 363 if (DeleteMapping) 364 { 365 DeleteMapping(Context, previous_entry->Process, 366 previous_entry->Address); 367 } 368 Process = previous_entry->Process; 369 ExFreeToNPagedLookasideList(&RmapLookasideList, previous_entry); 370 if (Process == NULL) 371 { 372 Process = PsInitialSystemProcess; 373 } 374 if (Process) 375 { 376 (void)InterlockedExchangeAddUL(&Process->Vm.WorkingSetSize, -PAGE_SIZE); 377 } 378 } 379 else 380 { 381 ExFreeToNPagedLookasideList(&RmapLookasideList, previous_entry); 382 } 383 } 384 } 385 386 VOID 387 NTAPI 388 MmDeleteRmap(PFN_NUMBER Page, PEPROCESS Process, 389 PVOID Address) 390 { 391 PMM_RMAP_ENTRY current_entry, previous_entry; 392 393 ExAcquireFastMutex(&RmapListLock); 394 previous_entry = NULL; 395 current_entry = MmGetRmapListHeadPage(Page); 396 397 while (current_entry != NULL) 398 { 399 if (current_entry->Process == (PEPROCESS)Process && 400 current_entry->Address == Address) 401 { 402 if (previous_entry == NULL) 403 { 404 MmSetRmapListHeadPage(Page, current_entry->Next); 405 } 406 else 407 { 408 previous_entry->Next = current_entry->Next; 409 } 410 ExReleaseFastMutex(&RmapListLock); 411 ExFreeToNPagedLookasideList(&RmapLookasideList, current_entry); 412 if (!RMAP_IS_SEGMENT(Address)) 413 { 414 if (Process == NULL) 415 { 416 Process = PsInitialSystemProcess; 417 } 418 if (Process) 419 { 420 (void)InterlockedExchangeAddUL(&Process->Vm.WorkingSetSize, -PAGE_SIZE); 421 } 422 } 423 return; 424 } 425 previous_entry = current_entry; 426 current_entry = current_entry->Next; 427 } 428 KeBugCheck(MEMORY_MANAGEMENT); 429 } 430 431 /* 432 433 Return the process pointer given when a previous call to MmInsertRmap was 434 called with a process and address pointer that conform to the segment rmap 435 schema. In short, this requires the address part to be 0xffffff00 + n 436 where n is between 0 and 255. When such an rmap exists, it specifies a 437 segment rmap in which the process part is a pointer to a slice of a section 438 page table, and the low 8 bits of the address represent a page index in the 439 page table slice. Together, this information is used by 440 MmGetSectionAssociation to determine which page entry points to this page in 441 the segment page table. 442 443 */ 444 445 PVOID 446 NTAPI 447 MmGetSegmentRmap(PFN_NUMBER Page, PULONG RawOffset) 448 { 449 PCACHE_SECTION_PAGE_TABLE Result = NULL; 450 PMM_RMAP_ENTRY current_entry;//, previous_entry; 451 452 ExAcquireFastMutex(&RmapListLock); 453 //previous_entry = NULL; 454 current_entry = MmGetRmapListHeadPage(Page); 455 while (current_entry != NULL) 456 { 457 if (RMAP_IS_SEGMENT(current_entry->Address)) 458 { 459 Result = (PCACHE_SECTION_PAGE_TABLE)current_entry->Process; 460 *RawOffset = (ULONG_PTR)current_entry->Address & ~RMAP_SEGMENT_MASK; 461 InterlockedIncrementUL(&Result->Segment->ReferenceCount); 462 ExReleaseFastMutex(&RmapListLock); 463 return Result; 464 } 465 //previous_entry = current_entry; 466 current_entry = current_entry->Next; 467 } 468 ExReleaseFastMutex(&RmapListLock); 469 return NULL; 470 } 471 472 /* 473 474 Remove the section rmap associated with the indicated page, if it exists. 475 476 */ 477 478 VOID 479 NTAPI 480 MmDeleteSectionAssociation(PFN_NUMBER Page) 481 { 482 PMM_RMAP_ENTRY current_entry, previous_entry; 483 484 ExAcquireFastMutex(&RmapListLock); 485 previous_entry = NULL; 486 current_entry = MmGetRmapListHeadPage(Page); 487 while (current_entry != NULL) 488 { 489 if (RMAP_IS_SEGMENT(current_entry->Address)) 490 { 491 if (previous_entry == NULL) 492 { 493 MmSetRmapListHeadPage(Page, current_entry->Next); 494 } 495 else 496 { 497 previous_entry->Next = current_entry->Next; 498 } 499 ExReleaseFastMutex(&RmapListLock); 500 ExFreeToNPagedLookasideList(&RmapLookasideList, current_entry); 501 return; 502 } 503 previous_entry = current_entry; 504 current_entry = current_entry->Next; 505 } 506 ExReleaseFastMutex(&RmapListLock); 507 } 508