Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1201 lines
31 KiB

  1. /*++
  2. Copyright (c) 1999 Microsoft Corporation
  3. Module Name:
  4. prefboot.c
  5. Abstract:
  6. This module contains the code for boot prefetching.
  7. Author:
  8. Cenk Ergan (cenke) 15-Mar-2000
  9. Revision History:
  10. --*/
  11. #include "cc.h"
  12. #include "zwapi.h"
  13. #include "prefetch.h"
  14. #include "preftchp.h"
  15. #include "stdio.h"
  16. #ifdef ALLOC_PRAGMA
  17. #pragma alloc_text(PAGE, CcPfBeginBootPhase)
  18. #pragma alloc_text(PAGE, CcPfBootWorker)
  19. #pragma alloc_text(PAGE, CcPfBootQueueEndTraceTimer)
  20. #endif // ALLOC_PRAGMA
  21. //
  22. // Globals:
  23. //
  24. //
  25. // Whether the system is currently prefetching for boot.
  26. //
  27. LOGICAL CcPfPrefetchingForBoot = FALSE;
  28. //
  29. // Current boot phase, only updated in begin boot phase routine.
  30. //
  31. PF_BOOT_PHASE_ID CcPfBootPhase = 0;
  32. //
  33. // Prefetcher globals.
  34. //
  35. extern CCPF_PREFETCHER_GLOBALS CcPfGlobals;
  36. //
  37. // Routines for boot prefetching.
  38. //
  39. NTSTATUS
  40. CcPfBeginBootPhase(
  41. PF_BOOT_PHASE_ID Phase
  42. )
  43. /*++
  44. Routine Description:
  45. This routine is the control center for the boot prefetcher.
  46. It is called to notify boot prefetcher of boot progress.
  47. Arguments:
  48. Phase - Boot phase the system is entering.
  49. Return Value:
  50. Status.
  51. Environment:
  52. Kernel mode. IRQL == PASSIVE_LEVEL.
  53. --*/
  54. {
  55. LARGE_INTEGER VideoInitEndTime;
  56. LARGE_INTEGER MaxWaitTime;
  57. LONGLONG VideoInitTimeIn100ns;
  58. HANDLE ThreadHandle;
  59. PETHREAD Thread;
  60. PERFINFO_BOOT_PHASE_START LogEntry;
  61. PF_BOOT_PHASE_ID OriginalPhase;
  62. PF_BOOT_PHASE_ID NewPhase;
  63. ULONG VideoInitTime;
  64. NTSTATUS Status;
  65. //
  66. // This is the boot prefetcher. It is allocated and free'd in this routine.
  67. // It is passed to the spawned boot worker if boot prefetching is enabled.
  68. //
  69. static PCCPF_BOOT_PREFETCHER BootPrefetcher = NULL;
  70. //
  71. // This is the system time when we started initializing the video.
  72. //
  73. static LARGE_INTEGER VideoInitStartTime;
  74. DBGPR((CCPFID,PFTRC,"CCPF: BeginBootPhase(%d)\n", (ULONG)Phase));
  75. //
  76. // Make sure phase is valid.
  77. //
  78. if (Phase >= PfMaxBootPhaseId) {
  79. Status = STATUS_INVALID_PARAMETER;
  80. goto cleanup;
  81. }
  82. //
  83. // Log phase to trace buffer.
  84. //
  85. if (PERFINFO_IS_GROUP_ON(PERF_LOADER)) {
  86. LogEntry.Phase = Phase;
  87. PerfInfoLogBytes(PERFINFO_LOG_TYPE_BOOT_PHASE_START,
  88. &LogEntry,
  89. sizeof(LogEntry));
  90. }
  91. //
  92. // Update the global current boot phase.
  93. //
  94. for (;;) {
  95. OriginalPhase = CcPfBootPhase;
  96. if (Phase <= OriginalPhase) {
  97. Status = STATUS_TOO_LATE;
  98. goto cleanup;
  99. }
  100. //
  101. // If CcPfBootPhase is still OriginalPhase, set it to Phase.
  102. //
  103. NewPhase = InterlockedCompareExchange(&(LONG)CcPfBootPhase, Phase, OriginalPhase);
  104. if (NewPhase == OriginalPhase) {
  105. //
  106. // CcPfBootPhase was still OriginalPhase, so now it is set to
  107. // Phase. We are done.
  108. //
  109. break;
  110. }
  111. }
  112. Status = STATUS_SUCCESS;
  113. //
  114. // Perform the work we have to do for this boot phase.
  115. //
  116. switch (Phase) {
  117. case PfSystemDriverInitPhase:
  118. //
  119. // Update whether prefetcher is enabled or not.
  120. //
  121. CcPfDetermineEnablePrefetcher();
  122. //
  123. // If boot prefetching is not enabled, we are done.
  124. //
  125. if (!CCPF_IS_PREFETCHER_ENABLED() ||
  126. CcPfGlobals.Parameters.Parameters.EnableStatus[PfSystemBootScenarioType] != PfSvEnabled) {
  127. Status = STATUS_NOT_SUPPORTED;
  128. break;
  129. }
  130. //
  131. // Allocate and initialize boot prefetcher.
  132. //
  133. BootPrefetcher = ExAllocatePoolWithTag(NonPagedPool,
  134. sizeof(*BootPrefetcher),
  135. CCPF_ALLOC_BOOTWRKR_TAG);
  136. if (!BootPrefetcher) {
  137. Status = STATUS_INSUFFICIENT_RESOURCES;
  138. break;
  139. }
  140. KeInitializeEvent(&BootPrefetcher->SystemDriversPrefetchingDone,
  141. NotificationEvent,
  142. FALSE);
  143. KeInitializeEvent(&BootPrefetcher->PreSmssPrefetchingDone,
  144. NotificationEvent,
  145. FALSE);
  146. KeInitializeEvent(&BootPrefetcher->VideoInitPrefetchingDone,
  147. NotificationEvent,
  148. FALSE);
  149. KeInitializeEvent(&BootPrefetcher->VideoInitStarted,
  150. NotificationEvent,
  151. FALSE);
  152. //
  153. // Kick off the boot worker in paralel.
  154. //
  155. Status = PsCreateSystemThread(&ThreadHandle,
  156. THREAD_ALL_ACCESS,
  157. NULL,
  158. NULL,
  159. NULL,
  160. CcPfBootWorker,
  161. BootPrefetcher);
  162. if (NT_SUCCESS(Status)) {
  163. //
  164. // Give boot worker some head start by bumping its
  165. // priority. This helps to make sure pages we will
  166. // prefetch are put into transition before boot gets
  167. // ahead of the prefetcher.
  168. //
  169. Status = ObReferenceObjectByHandle(ThreadHandle,
  170. THREAD_SET_INFORMATION,
  171. PsThreadType,
  172. KernelMode,
  173. &Thread,
  174. NULL);
  175. if (NT_SUCCESS(Status)) {
  176. KeSetPriorityThread(&Thread->Tcb, HIGH_PRIORITY - 1);
  177. ObDereferenceObject(Thread);
  178. }
  179. ZwClose(ThreadHandle);
  180. //
  181. // Before returning to initialize system drivers, wait
  182. // for boot worker to make progress.
  183. //
  184. KeWaitForSingleObject(&BootPrefetcher->SystemDriversPrefetchingDone,
  185. Executive,
  186. KernelMode,
  187. FALSE,
  188. NULL);
  189. } else {
  190. //
  191. // Free the allocated boot prefetcher.
  192. //
  193. ExFreePool(BootPrefetcher);
  194. BootPrefetcher = NULL;
  195. }
  196. break;
  197. case PfSessionManagerInitPhase:
  198. //
  199. // Wait for boot worker to make enough progress before launching
  200. // session manager.
  201. //
  202. if (BootPrefetcher) {
  203. KeWaitForSingleObject(&BootPrefetcher->PreSmssPrefetchingDone,
  204. Executive,
  205. KernelMode,
  206. FALSE,
  207. NULL);
  208. }
  209. break;
  210. case PfVideoInitPhase:
  211. //
  212. // Note when video initialization started.
  213. //
  214. KeQuerySystemTime(&VideoInitStartTime);
  215. //
  216. // Signal boot prefetcher to start prefetching in parallel to video
  217. // initialization.
  218. //
  219. if (BootPrefetcher) {
  220. KeSetEvent(&BootPrefetcher->VideoInitStarted,
  221. IO_NO_INCREMENT,
  222. FALSE);
  223. }
  224. break;
  225. case PfPostVideoInitPhase:
  226. //
  227. // Note when we complete video initialization. Save how long video
  228. // initialization took in the registry in milliseconds.
  229. //
  230. KeQuerySystemTime(&VideoInitEndTime);
  231. VideoInitTimeIn100ns = VideoInitEndTime.QuadPart - VideoInitStartTime.QuadPart;
  232. VideoInitTime = (ULONG) (VideoInitTimeIn100ns / (1i64 * 10 * 1000));
  233. KeEnterCriticalRegionThread(KeGetCurrentThread());
  234. ExAcquireResourceSharedLite(&CcPfGlobals.Parameters.ParametersLock, TRUE);
  235. Status = CcPfSetParameter(CcPfGlobals.Parameters.ParametersKey,
  236. CCPF_VIDEO_INIT_TIME_VALUE_NAME,
  237. REG_DWORD,
  238. &VideoInitTime,
  239. sizeof(VideoInitTime));
  240. ExReleaseResourceLite(&CcPfGlobals.Parameters.ParametersLock);
  241. KeLeaveCriticalRegionThread(KeGetCurrentThread());
  242. //
  243. // Wait for prefetching parallel to video initialization to complete.
  244. //
  245. if (BootPrefetcher) {
  246. KeWaitForSingleObject(&BootPrefetcher->VideoInitPrefetchingDone,
  247. Executive,
  248. KernelMode,
  249. FALSE,
  250. NULL);
  251. }
  252. break;
  253. case PfBootAcceptedRegistryInitPhase:
  254. //
  255. // Service Controller has accepted this boot as a valid boot.
  256. // Boot & system services have initialized successfully.
  257. //
  258. //
  259. // We are done with boot prefetching. No one else could be accessing
  260. // BootPrefetcher structure at this point.
  261. //
  262. if (BootPrefetcher) {
  263. //
  264. // Cleanup the allocated boot prefetcher.
  265. //
  266. ExFreePool(BootPrefetcher);
  267. BootPrefetcher = NULL;
  268. //
  269. // Determine if the prefetcher is enabled now that boot
  270. // is over.
  271. //
  272. CcPfDetermineEnablePrefetcher();
  273. }
  274. //
  275. // The user may not log in after booting.
  276. // Queue a timer to end boot trace.
  277. //
  278. MaxWaitTime.QuadPart = -1i64 * 60 * 1000 * 1000 * 10; // 60 seconds.
  279. CcPfBootQueueEndTraceTimer(&MaxWaitTime);
  280. break;
  281. case PfUserShellReadyPhase:
  282. //
  283. // Explorer has started, but start menu items may still be launching.
  284. // Queue a timer to end boot trace.
  285. //
  286. MaxWaitTime.QuadPart = -1i64 * 30 * 1000 * 1000 * 10; // 30 seconds.
  287. CcPfBootQueueEndTraceTimer(&MaxWaitTime);
  288. break;
  289. default:
  290. //
  291. // Ignored for now.
  292. //
  293. Status = STATUS_SUCCESS;
  294. }
  295. //
  296. // Fall through with status from switch statement.
  297. //
  298. cleanup:
  299. DBGPR((CCPFID,PFTRC,"CCPF: BeginBootPhase(%d)=%x\n", (ULONG)Phase, Status));
  300. return Status;
  301. }
  302. VOID
  303. CcPfBootWorker(
  304. PCCPF_BOOT_PREFETCHER BootPrefetcher
  305. )
  306. /*++
  307. Routine Description:
  308. This routine is queued to prefetch and start tracing boot in parallel.
  309. Arguments:
  310. BootPrefetcher - Pointer to boot prefetcher context.
  311. Return Value:
  312. None.
  313. Environment:
  314. Kernel mode. IRQL == PASSIVE_LEVEL.
  315. --*/
  316. {
  317. PF_SCENARIO_ID BootScenarioId;
  318. CCPF_PREFETCH_HEADER PrefetchHeader;
  319. CCPF_BASIC_SCENARIO_INFORMATION ScenarioInfo;
  320. CCPF_BOOT_SCENARIO_INFORMATION BootScenarioInfo;
  321. PERFINFO_BOOT_PREFETCH_INFORMATION LogEntry;
  322. ULONG NumPages;
  323. ULONG RequiredSize;
  324. ULONG NumPagesPrefetched;
  325. ULONG TotalPagesPrefetched;
  326. ULONG BootPrefetchAdjustment;
  327. ULONG AvailablePages;
  328. ULONG NumPagesToPrefetch;
  329. ULONG TotalPagesToPrefetch;
  330. ULONG RemainingDataPages;
  331. ULONG RemainingImagePages;
  332. ULONG VideoInitTime;
  333. ULONG VideoInitPagesPerSecond;
  334. ULONG VideoInitMaxPages;
  335. ULONG RemainingVideoInitPages;
  336. ULONG VideoInitDataPages;
  337. ULONG VideoInitImagePages;
  338. ULONG PrefetchPhaseIdx;
  339. ULONG LastPrefetchPhaseIdx;
  340. ULONG SystemDriverPrefetchingPhaseIdx;
  341. ULONG PreSmssPrefetchingPhaseIdx;
  342. ULONG VideoInitPrefetchingPhaseIdx;
  343. ULONG ValueSize;
  344. CCPF_BOOT_SCENARIO_PHASE BootPhaseIdx;
  345. NTSTATUS Status;
  346. BOOLEAN OutOfAvailablePages;
  347. //
  348. // First we will prefetch data pages, then image pages.
  349. //
  350. enum {
  351. DataCursor = 0,
  352. ImageCursor,
  353. MaxCursor
  354. } CursorIdx;
  355. CCPF_BOOT_PREFETCH_CURSOR Cursors[MaxCursor];
  356. PCCPF_BOOT_PREFETCH_CURSOR Cursor;
  357. //
  358. // Initialize locals.
  359. //
  360. CcPfInitializePrefetchHeader(&PrefetchHeader);
  361. TotalPagesPrefetched = 0;
  362. OutOfAvailablePages = FALSE;
  363. DBGPR((CCPFID,PFTRC,"CCPF: BootWorker()\n"));
  364. //
  365. // Initialize boot scenario ID.
  366. //
  367. wcsncpy(BootScenarioId.ScenName,
  368. PF_BOOT_SCENARIO_NAME,
  369. PF_SCEN_ID_MAX_CHARS);
  370. BootScenarioId.ScenName[PF_SCEN_ID_MAX_CHARS] = 0;
  371. BootScenarioId.HashId = PF_BOOT_SCENARIO_HASHID;
  372. //
  373. // Start boot prefetch tracing.
  374. //
  375. CcPfBeginTrace(&BootScenarioId, PfSystemBootScenarioType, PsInitialSystemProcess);
  376. //
  377. // If we try to prefetch more pages then what we have available, we will
  378. // end up cannibalizing the pages we prefetched into the standby list.
  379. // To avoid cannibalizing, we check MmAvailablePages but leave some
  380. // breathing room for metadata pages, allocations from the driver
  381. // initialization phase etc.
  382. //
  383. BootPrefetchAdjustment = 512;
  384. //
  385. // We also know that right after we prefetch for boot, in smss when
  386. // initializing the registry we'll use up 8-10MB of prefetched pages if we
  387. // don't have anything left in the free list. So we leave some room for
  388. // that too.
  389. //
  390. BootPrefetchAdjustment += 8 * 1024 * 1024 / PAGE_SIZE;
  391. //
  392. // Get prefetch instructions.
  393. //
  394. Status = CcPfGetPrefetchInstructions(&BootScenarioId,
  395. PfSystemBootScenarioType,
  396. &PrefetchHeader.Scenario);
  397. if (!NT_SUCCESS(Status)) {
  398. goto cleanup;
  399. }
  400. //
  401. // Query the total number of pages to be prefetched.
  402. //
  403. Status = CcPfQueryScenarioInformation(PrefetchHeader.Scenario,
  404. CcPfBasicScenarioInformation,
  405. &ScenarioInfo,
  406. sizeof(ScenarioInfo),
  407. &RequiredSize);
  408. if (!NT_SUCCESS(Status)) {
  409. goto cleanup;
  410. }
  411. //
  412. // Query the number of pages we have to prefetch for boot phases.
  413. //
  414. Status = CcPfQueryScenarioInformation(PrefetchHeader.Scenario,
  415. CcPfBootScenarioInformation,
  416. &BootScenarioInfo,
  417. sizeof(BootScenarioInfo),
  418. &RequiredSize);
  419. if (!NT_SUCCESS(Status)) {
  420. goto cleanup;
  421. }
  422. //
  423. // Read how long it took to initialize video in the last boot.
  424. //
  425. KeEnterCriticalRegionThread(KeGetCurrentThread());
  426. ExAcquireResourceSharedLite(&CcPfGlobals.Parameters.ParametersLock, TRUE);
  427. ValueSize = sizeof(VideoInitTime);
  428. Status = CcPfGetParameter(CcPfGlobals.Parameters.ParametersKey,
  429. CCPF_VIDEO_INIT_TIME_VALUE_NAME,
  430. REG_DWORD,
  431. &VideoInitTime,
  432. &ValueSize);
  433. ExReleaseResourceLite(&CcPfGlobals.Parameters.ParametersLock);
  434. KeLeaveCriticalRegionThread(KeGetCurrentThread());
  435. if (!NT_SUCCESS(Status)) {
  436. //
  437. // Reset video init time, so we don't attempt to prefetch
  438. // in parallel to it.
  439. //
  440. VideoInitTime = 0;
  441. } else {
  442. //
  443. // Verify the value we read from registry.
  444. //
  445. if (VideoInitTime > CCPF_MAX_VIDEO_INIT_TIME) {
  446. VideoInitTime = 0;
  447. }
  448. }
  449. //
  450. // Read how many pages per second we should be trying to prefetching
  451. // in parallel to video initialization.
  452. //
  453. KeEnterCriticalRegionThread(KeGetCurrentThread());
  454. ExAcquireResourceSharedLite(&CcPfGlobals.Parameters.ParametersLock, TRUE);
  455. ValueSize = sizeof(VideoInitPagesPerSecond);
  456. Status = CcPfGetParameter(CcPfGlobals.Parameters.ParametersKey,
  457. CCPF_VIDEO_INIT_PAGES_PER_SECOND_VALUE_NAME,
  458. REG_DWORD,
  459. &VideoInitPagesPerSecond,
  460. &ValueSize);
  461. ExReleaseResourceLite(&CcPfGlobals.Parameters.ParametersLock);
  462. KeLeaveCriticalRegionThread(KeGetCurrentThread());
  463. if (!NT_SUCCESS(Status)) {
  464. //
  465. // There was no valid value in the registry. Use the default.
  466. //
  467. VideoInitPagesPerSecond = CCPF_VIDEO_INIT_DEFAULT_PAGES_PER_SECOND;
  468. } else {
  469. //
  470. // Verify the value we read from registry.
  471. //
  472. if (VideoInitPagesPerSecond > CCPF_VIDEO_INIT_MAX_PAGES_PER_SECOND) {
  473. VideoInitPagesPerSecond = CCPF_VIDEO_INIT_MAX_PAGES_PER_SECOND;
  474. }
  475. }
  476. //
  477. // Determine how many pages max we can prefetch in parallel to video
  478. // initialization.
  479. //
  480. VideoInitMaxPages = (VideoInitTime / 1000) * VideoInitPagesPerSecond;
  481. //
  482. // We can only prefetch pages used after winlogon in parallel to video
  483. // initialization. Determine exactly how many pages we will prefetch
  484. // starting from the last boot phase.
  485. //
  486. RemainingVideoInitPages = VideoInitMaxPages;
  487. VideoInitDataPages = 0;
  488. VideoInitImagePages = 0;
  489. for (BootPhaseIdx = CcPfBootScenMaxPhase - 1;
  490. RemainingVideoInitPages && (BootPhaseIdx >= CcPfBootScenSystemProcInitPhase);
  491. BootPhaseIdx--) {
  492. NumPages = CCPF_MIN(RemainingVideoInitPages, BootScenarioInfo.NumImagePages[BootPhaseIdx]);
  493. VideoInitImagePages += NumPages;
  494. RemainingVideoInitPages -= NumPages;
  495. if (RemainingVideoInitPages) {
  496. NumPages = CCPF_MIN(RemainingVideoInitPages, BootScenarioInfo.NumDataPages[BootPhaseIdx]);
  497. VideoInitDataPages += NumPages;
  498. RemainingVideoInitPages -= NumPages;
  499. }
  500. }
  501. //
  502. // Let MM know that we have started prefetching for boot.
  503. //
  504. CcPfPrefetchingForBoot = TRUE;
  505. //
  506. // Log that we are starting prefetch disk I/Os.
  507. //
  508. if (PERFINFO_IS_GROUP_ON(PERF_DISK_IO)) {
  509. LogEntry.Action = 0;
  510. LogEntry.Status = 0;
  511. LogEntry.Pages = ScenarioInfo.NumDataPages + ScenarioInfo.NumImagePages;
  512. PerfInfoLogBytes(PERFINFO_LOG_TYPE_BOOT_PREFETCH_INFORMATION,
  513. &LogEntry,
  514. sizeof(LogEntry));
  515. }
  516. //
  517. // Verify & open the volumes that we will prefetch from.
  518. //
  519. Status = CcPfOpenVolumesForPrefetch(&PrefetchHeader);
  520. if (!NT_SUCCESS(Status)) {
  521. goto cleanup;
  522. }
  523. //
  524. // Prefetch the metadata.
  525. //
  526. CcPfPrefetchMetadata(&PrefetchHeader);
  527. //
  528. // Initialize the boot prefetch cursors for data and image.
  529. //
  530. RtlZeroMemory(Cursors, sizeof(Cursors));
  531. Cursors[DataCursor].PrefetchType = CcPfPrefetchPartOfDataPages;
  532. Cursors[ImageCursor].PrefetchType = CcPfPrefetchPartOfImagePages;
  533. PrefetchPhaseIdx = 0;
  534. RemainingDataPages = ScenarioInfo.NumDataPages;
  535. RemainingImagePages = ScenarioInfo.NumImagePages;
  536. //
  537. // Setup the cursors for phases in which we will prefetch for boot.
  538. // First we will prefetch for system drivers.
  539. //
  540. NumPages = BootScenarioInfo.NumDataPages[CcPfBootScenDriverInitPhase];
  541. Cursors[DataCursor].NumPagesForPhase[PrefetchPhaseIdx] = NumPages;
  542. RemainingDataPages -= NumPages;
  543. NumPages = BootScenarioInfo.NumImagePages[CcPfBootScenDriverInitPhase];
  544. Cursors[ImageCursor].NumPagesForPhase[PrefetchPhaseIdx] = NumPages;
  545. RemainingImagePages -= NumPages;
  546. SystemDriverPrefetchingPhaseIdx = PrefetchPhaseIdx;
  547. PrefetchPhaseIdx++;
  548. //
  549. // Account for the video init pages we will prefetch last.
  550. //
  551. RemainingDataPages -= VideoInitDataPages;
  552. RemainingImagePages -= VideoInitImagePages;
  553. //
  554. // If we have plenty of available memory, prefetch the rest of the pages
  555. // (i.e. left over after driver init pages) in one pass.
  556. //
  557. TotalPagesToPrefetch = ScenarioInfo.NumDataPages + ScenarioInfo.NumImagePages;
  558. if (MmAvailablePages > BootPrefetchAdjustment + TotalPagesToPrefetch) {
  559. Cursors[DataCursor].NumPagesForPhase[PrefetchPhaseIdx] = RemainingDataPages;
  560. RemainingDataPages = 0;
  561. Cursors[ImageCursor].NumPagesForPhase[PrefetchPhaseIdx] = RemainingImagePages;
  562. RemainingImagePages = 0;
  563. PrefetchPhaseIdx++;
  564. } else {
  565. //
  566. // We will be short on memory. Try to prefetch for as many phases of
  567. // boot as we can in parallel. Prefetching data & image pages per boot
  568. // phase, so we don't end up with data pages for all phase but no image
  569. // pages so we have to go to the disk in each phase. Prefetching in
  570. // chunks also help that all the pages we need for the initial phases
  571. // of boot ending up at the end of the standby list, since when
  572. // CcPfPrefetchingForBoot is set, prefetched pages will be inserted
  573. // from the front of the standby list.
  574. //
  575. for (BootPhaseIdx = CcPfBootScenDriverInitPhase + 1;
  576. BootPhaseIdx < CcPfBootScenMaxPhase;
  577. BootPhaseIdx++) {
  578. //
  579. // If we don't have any type of pages left to prefetch, we are done.
  580. //
  581. if (!RemainingDataPages && !RemainingImagePages) {
  582. break;
  583. }
  584. NumPages = CCPF_MIN(RemainingDataPages, BootScenarioInfo.NumDataPages[BootPhaseIdx]);
  585. RemainingDataPages -= NumPages;
  586. Cursors[DataCursor].NumPagesForPhase[PrefetchPhaseIdx] = NumPages;
  587. NumPages = CCPF_MIN(RemainingImagePages, BootScenarioInfo.NumImagePages[BootPhaseIdx]);
  588. RemainingImagePages -= NumPages;
  589. Cursors[ImageCursor].NumPagesForPhase[PrefetchPhaseIdx] = NumPages;
  590. PrefetchPhaseIdx++;
  591. }
  592. }
  593. PreSmssPrefetchingPhaseIdx = PrefetchPhaseIdx - 1;
  594. //
  595. // If we'll be prefetching pages in parallel to video initialization, now
  596. // add the phase for it.
  597. //
  598. if (VideoInitDataPages || VideoInitImagePages) {
  599. Cursors[DataCursor].NumPagesForPhase[PrefetchPhaseIdx] = VideoInitDataPages;
  600. Cursors[ImageCursor].NumPagesForPhase[PrefetchPhaseIdx] = VideoInitImagePages;
  601. VideoInitPrefetchingPhaseIdx = PrefetchPhaseIdx;
  602. PrefetchPhaseIdx++;
  603. } else {
  604. //
  605. // We won't have a prefetching phase parallel to video initialization.
  606. //
  607. VideoInitPrefetchingPhaseIdx = CCPF_MAX_BOOT_PREFETCH_PHASES;
  608. }
  609. //
  610. // We should not end up with more prefetch phases than we have room for.
  611. //
  612. CCPF_ASSERT(PrefetchPhaseIdx < CCPF_MAX_BOOT_PREFETCH_PHASES);
  613. LastPrefetchPhaseIdx = PrefetchPhaseIdx;
  614. //
  615. // Prefetch the data and image pages for each boot prefetching phase,
  616. // waiting for & signaling the events matching those phases so boot
  617. // is synchronized with prefetching. (I.e. we prefetch pages for a boot
  618. // phase before we start that boot phase.)
  619. //
  620. for (PrefetchPhaseIdx = 0; PrefetchPhaseIdx < LastPrefetchPhaseIdx; PrefetchPhaseIdx++) {
  621. //
  622. // If this is the video init prefetching phase, wait for video
  623. // initialization to begin.
  624. //
  625. if (PrefetchPhaseIdx == VideoInitPrefetchingPhaseIdx) {
  626. KeWaitForSingleObject(&BootPrefetcher->VideoInitStarted,
  627. Executive,
  628. KernelMode,
  629. FALSE,
  630. NULL);
  631. }
  632. for (CursorIdx = 0; CursorIdx < MaxCursor; CursorIdx++) {
  633. Cursor = &Cursors[CursorIdx];
  634. NumPagesToPrefetch = Cursor->NumPagesForPhase[PrefetchPhaseIdx];
  635. //
  636. // For prefetch phases before SMSS is launched keep an eye on
  637. // how much memory is still available to prefetch into so we
  638. // don't cannibalize ourselves. After SMSS our heuristics on
  639. // standby-list composition do not make sense.
  640. //
  641. if (PrefetchPhaseIdx <= PreSmssPrefetchingPhaseIdx) {
  642. //
  643. // Check if we have available memory to prefetch more.
  644. //
  645. if (TotalPagesPrefetched + BootPrefetchAdjustment >= MmAvailablePages) {
  646. OutOfAvailablePages = TRUE;
  647. NumPagesToPrefetch = 0;
  648. } else {
  649. //
  650. // Check if we have to adjust NumPagesToPrefetch and prefetch
  651. // one last chunk.
  652. //
  653. AvailablePages = MmAvailablePages;
  654. AvailablePages -= (TotalPagesPrefetched + BootPrefetchAdjustment);
  655. if (AvailablePages < NumPagesToPrefetch) {
  656. NumPagesToPrefetch = AvailablePages;
  657. }
  658. }
  659. }
  660. if (NumPagesToPrefetch) {
  661. Status = CcPfPrefetchSections(&PrefetchHeader,
  662. Cursor->PrefetchType,
  663. &Cursor->StartCursor,
  664. NumPagesToPrefetch,
  665. &NumPagesPrefetched,
  666. &Cursor->EndCursor);
  667. if (!NT_SUCCESS(Status)) {
  668. goto cleanup;
  669. }
  670. } else {
  671. NumPagesPrefetched = 0;
  672. }
  673. //
  674. // Update our position.
  675. //
  676. Cursor->StartCursor = Cursor->EndCursor;
  677. TotalPagesPrefetched += NumPagesPrefetched;
  678. }
  679. //
  680. // Note that we are done with this prefetching phase and
  681. // system boot can continue.
  682. //
  683. if (PrefetchPhaseIdx == SystemDriverPrefetchingPhaseIdx) {
  684. KeSetEvent(&BootPrefetcher->SystemDriversPrefetchingDone,
  685. IO_NO_INCREMENT,
  686. FALSE);
  687. }
  688. if (PrefetchPhaseIdx == PreSmssPrefetchingPhaseIdx) {
  689. KeSetEvent(&BootPrefetcher->PreSmssPrefetchingDone,
  690. IO_NO_INCREMENT,
  691. FALSE);
  692. }
  693. if (PrefetchPhaseIdx == VideoInitPrefetchingPhaseIdx) {
  694. KeSetEvent(&BootPrefetcher->VideoInitPrefetchingDone,
  695. IO_NO_INCREMENT,
  696. FALSE);
  697. }
  698. }
  699. Status = STATUS_SUCCESS;
  700. cleanup:
  701. //
  702. // Log that we are done with boot prefetch disk I/Os.
  703. //
  704. if (PERFINFO_IS_GROUP_ON(PERF_DISK_IO)) {
  705. LogEntry.Action = 1;
  706. LogEntry.Status = Status;
  707. LogEntry.Pages = TotalPagesPrefetched;
  708. PerfInfoLogBytes(PERFINFO_LOG_TYPE_BOOT_PREFETCH_INFORMATION,
  709. &LogEntry,
  710. sizeof(LogEntry));
  711. }
  712. //
  713. // Make sure all the events system may wait for before proceeding with
  714. // boot are signaled.
  715. //
  716. KeSetEvent(&BootPrefetcher->SystemDriversPrefetchingDone,
  717. IO_NO_INCREMENT,
  718. FALSE);
  719. KeSetEvent(&BootPrefetcher->PreSmssPrefetchingDone,
  720. IO_NO_INCREMENT,
  721. FALSE);
  722. KeSetEvent(&BootPrefetcher->VideoInitPrefetchingDone,
  723. IO_NO_INCREMENT,
  724. FALSE);
  725. //
  726. // Let MM know that we are done prefetching for boot.
  727. //
  728. CcPfPrefetchingForBoot = FALSE;
  729. //
  730. // Cleanup prefetching context.
  731. //
  732. CcPfCleanupPrefetchHeader(&PrefetchHeader);
  733. if (PrefetchHeader.Scenario) {
  734. ExFreePool(PrefetchHeader.Scenario);
  735. }
  736. DBGPR((CCPFID,PFTRC,"CCPF: BootWorker()=%x,%d\n",Status,(ULONG)OutOfAvailablePages));
  737. }
  738. NTSTATUS
  739. CcPfBootQueueEndTraceTimer (
  740. PLARGE_INTEGER Timeout
  741. )
  742. /*++
  743. Routine Description:
  744. This routine allocates and queues a timer that will attempt to end
  745. the boot trace when it fires.
  746. Arguments:
  747. Timeout - Timeout for the timer.
  748. Return Value:
  749. Status.
  750. Environment:
  751. Kernel mode. IRQL <= PASSIVE_LEVEL.
  752. --*/
  753. {
  754. PVOID Allocation;
  755. PKTIMER Timer;
  756. PKDPC Dpc;
  757. ULONG AllocationSize;
  758. NTSTATUS Status;
  759. BOOLEAN TimerAlreadyQueued;
  760. //
  761. // Initialize locals.
  762. //
  763. Allocation = NULL;
  764. //
  765. // Make a single allocation for the timer and dpc.
  766. //
  767. AllocationSize = sizeof(KTIMER);
  768. AllocationSize += sizeof(KDPC);
  769. Allocation = ExAllocatePoolWithTag(NonPagedPool,
  770. AllocationSize,
  771. CCPF_ALLOC_BOOTWRKR_TAG);
  772. if (!Allocation) {
  773. Status = STATUS_INSUFFICIENT_RESOURCES;
  774. goto cleanup;
  775. }
  776. Timer = Allocation;
  777. Dpc = (PKDPC)(Timer + 1);
  778. //
  779. // Initialize the timer and DPC. We'll be passing the allocation to the
  780. // queued DPC so it can be freed.
  781. //
  782. KeInitializeTimer(Timer);
  783. KeInitializeDpc(Dpc, CcPfEndBootTimerRoutine, Allocation);
  784. //
  785. // Queue the timer.
  786. //
  787. TimerAlreadyQueued = KeSetTimer(Timer, *Timeout, Dpc);
  788. CCPF_ASSERT(!TimerAlreadyQueued);
  789. Status = STATUS_SUCCESS;
  790. cleanup:
  791. if (!NT_SUCCESS(Status)) {
  792. if (Allocation) {
  793. ExFreePool(Allocation);
  794. }
  795. }
  796. return Status;
  797. }
  798. VOID
  799. CcPfEndBootTimerRoutine(
  800. IN PKDPC Dpc,
  801. IN PVOID DeferredContext,
  802. IN PVOID SystemArgument1,
  803. IN PVOID SystemArgument2
  804. )
  805. /*++
  806. Routine Description:
  807. This routine is invoked as the DPC handler for a timer queued to
  808. mark the end of boot and end the boot trace if one is active.
  809. Arguments:
  810. DeferredContext - Allocated memory for the timer & dpc that need
  811. to be freed.
  812. Return Value:
  813. None.
  814. Environment:
  815. Kernel mode. IRQL == DISPATCH_LEVEL.
  816. --*/
  817. {
  818. PCCPF_TRACE_HEADER BootTrace;
  819. PERFINFO_BOOT_PHASE_START LogEntry;
  820. UNREFERENCED_PARAMETER (Dpc);
  821. UNREFERENCED_PARAMETER (SystemArgument1);
  822. UNREFERENCED_PARAMETER (SystemArgument2);
  823. //
  824. // Initialize locals.
  825. //
  826. BootTrace = NULL;
  827. //
  828. // Is the boot trace still active?
  829. //
  830. BootTrace = CcPfReferenceProcessTrace(PsInitialSystemProcess);
  831. if (BootTrace && BootTrace->ScenarioType == PfSystemBootScenarioType) {
  832. //
  833. // Is somebody already ending the boot trace?
  834. //
  835. if (!InterlockedCompareExchange(&BootTrace->EndTraceCalled, 1, 0)) {
  836. //
  837. // We set EndTraceCalled from 0 to 1. Queue the
  838. // workitem to end the trace.
  839. //
  840. ExQueueWorkItem(&BootTrace->EndTraceWorkItem, DelayedWorkQueue);
  841. //
  842. // Log that we are ending the boot trace.
  843. //
  844. if (PERFINFO_IS_GROUP_ON(PERF_LOADER)) {
  845. LogEntry.Phase = PfMaxBootPhaseId;
  846. PerfInfoLogBytes(PERFINFO_LOG_TYPE_BOOT_PHASE_START,
  847. &LogEntry,
  848. sizeof(LogEntry));
  849. }
  850. }
  851. }
  852. //
  853. // Free the memory allocated for the timer and dpc.
  854. //
  855. CCPF_ASSERT(DeferredContext);
  856. ExFreePool(DeferredContext);
  857. if (BootTrace) {
  858. CcPfDecRef(&BootTrace->RefCount);
  859. }
  860. return;
  861. }