-
-
Notifications
You must be signed in to change notification settings - Fork 2k
[NTOS] Implement support for global pages #7937
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
This is important for properly flushing the TLB on SMP.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR implements support for global pages and updates the TLB flushing mechanism across the kernel. Key changes include:
- Removal of the old inline global page setup in favor of a dedicated MiInitializeGlobalPages() call.
- Replacement of legacy TLB flush calls (KeFlushCurrentTb, KeFlushProcessTb, etc.) with new Kx-prefixed functions.
- Introduction of new IPI routines and header updates to support SMP and architectural differences.
Reviewed Changes
Copilot reviewed 34 out of 34 changed files in this pull request and generated 2 comments.
Show a summary per file
File | Description |
---|---|
ntoskrnl/mm/ARM3/i386/init.c | Removes inline global page configuration and calls MiInitializeGlobalPages(). Adjusts TLB flush function. |
ntoskrnl/mm/ARM3/hypermap.c | Updates TLB flushing calls to use KeFlushSingleTb / KeFlushRangeTb. |
ntoskrnl/ke/tlbflush.c | Adds new implementations for TLB flushing routines supporting SMP. |
ntoskrnl/ke/ipi.c and ntoskrnl/ke/amd64/ipi.c | Introduces stub and new implementations respectively for IPI requests. |
Other files (cpu.c, freeze.c, stubs.c, headers) | Update flush functions calls and inline definitions to align with new APIs. |
ntoskrnl/include/config.h | Enables global page support by defining GLOBAL_PAGES_AWESOME. |
|
||
VOID | ||
NTAPI | ||
KiIpiSendRequest( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like this new version supersedes the old one at line 75, while the function at line 75 appears to be more or less the implementation for KiIpiSend() (of line 36).
KeActiveProcessors |= 1ULL << Cpu; | ||
|
||
/* We are running the initial system process now */ | ||
InterlockedOr64(&KiInitialProcess.Pcb.ActiveProcessors, 1ULL << Cpu); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks like this is already done in KiInitializeHandBuiltThread
(see line 134) already, no? (it's invoked either for CPU 0 byKiInitializeKernel()
-- which in turn is invoked from KiSystemStartupBootStack
, or, it's invoked for CPU != 0 directly by KiSystemStartupBootStack()
).
|
||
VOID | ||
NTAPI | ||
KeFlushSingleTb( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These main flush Tb functions should raise irql to SYNCH_LEVEL (with KeRaiseIrqlToSynchLevel
) and lower it back once finished.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What would be the point of that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some synchronization issues perhaps (or for using the ipi routines)? I'm just stating what the previous functions were doing, and something that has been observed elsewhere:
https://community.osr.com/t/more-questions-about-mapping-user-kernel-mode-memory/13920/9
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is the usual "That's what Windows does" argument. We are not Windows. Our code isn't Windows code. Windows has all kinds of magic sauce, changing from version to version. For example Windows has some timestamp stuff going on (https://azius.com/the-page-fault-in-nonpaged-pool-syndrome/). We don't have that. What we have is either an IPI on SMP, which does the raise already, or simply one or more invlpg instructions. There is nothing to synchronize. In the worst case you'd invalidate TLB entries that have already been invalidated by a cr3 switch.
// | ||
Offset = MI_ZERO_PTES; | ||
PointerPte->u.Hard.PageFrameNumber = Offset; | ||
KeFlushProcessTb(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MiMapPageInHyperSpace at line 69 does a similar flush; why isn't it needed here in this case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem here was that a process flush wouln't work, because these are global PTEs (See line 151). I changed it back to where it was before, just turning it into a global range flush instead of the process flush. It's all not optimal, but it should work just fine. I might improve it in a separate PR.
|
||
/* Set new TSS fields */ | ||
//Pcr->TssBase->IoMapBase = NewProcess->IopmOffset; | ||
KiSwapProcess(NewProcess, OldProcess); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: a similar change could be done in x86 in KiSwapContextExit
(compare with the x86 KiSwapProcess
implementation) -- this would ensure having the SMP-specific code run there as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not an SMP PR and won't change anything in x86 that isn't strictly needed. I'll leave that part to @DarkFire01.
#endif | ||
|
||
static const ULONG KxFlushIndividualProcessPagesMaximum = 33; // Based on Linux tlb_single_page_flush_ceiling | ||
static const ULONG KxFlushIndividualGlobalPagesMaximum = 100; // Just a guess |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What are these variables for? Some sort of arbitrary threshold that when reached in KeFlushRangeTb and friends, a full flush is done?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. Use the source, Luke.
{ | ||
/* Set it on the template PTE and PDE */ | ||
ValidKernelPte.u.Hard.Global = TRUE; | ||
//ValidKernelPde.u.Hard.Global = TRUE; // FIXME: disabled for now |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's happening if you enable this one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As usual: crash and burn. PDEs just need more work.
/* Make this a transition PTE */ | ||
MI_MAKE_TRANSITION_PTE(PointerPte, Page, Protection); | ||
KeInvalidateTlbEntry(MiAddressToPte(PointerPte)); | ||
KeFlushSingleTb(MiAddressToPte(PointerPte), FALSE); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Noob question (I could have asked it in other places): What dictates the choice of flushing the TLB only for the processors on which the "current" process is running, versus, flushing unconditionally on all processors?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it's a process local page, then there is no need to flush it on processors that don't run in the context of the current process, because they first need to switch to it, i.e. switch cr3, which flushes the TLB already for non-global pages. And WSLE entries are process local.
KeFlushCurrentTb(); | ||
|
||
/* Update the flush stamp and return to original IRQL */ | ||
InterlockedExchangeAdd(&KiTbFlushTimeStamp, 1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, by the way, about this timestamp, you may find this reading interesting: https://azius.com/the-page-fault-in-nonpaged-pool-syndrome/
(The MS KB mentioned in the article can be found here.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I am aware of it. We don't make use of this feature and I doubt we ever will, because IMO it's pointless. It doesn't work in user-mode, and in kernel mode there are much better ways to avoid flushes.
Assert that only the single zeroing thread calls these functions.
- Add support for global pages - Add support for SMP - Improve naming to be more explicit about what the individual functions do - Unify shared code
8d8c2ff
to
12d75df
Compare
Purpose
This PR implements support for global pages (kernel pages that are not flushed out of the TLB on a process switch).
Proposed changes
TODO (not addressed in this PR)
Testbot runs (Filled in by Devs)