Hi,
Candy wrote:
NB: don't forget that user level programs can also load their own GS register.
Hmm, ok (I assumed user-level code wasn't allowed to change any segment registers). In this case you haven't got too much choice as you can't reserve a (segment or general) register for use by the kernel only.
For it to work you'd probably need re-entrancy locking - e.g.:
CPU #0 - CPU #0 information gets flushed from TLB
CPU #1 - CPU #1 information gets flushed from TLB
CPU #0 - tries to access CPU information page
CPU #0 - generates page fault
CPU #0 - maps CPU information page into page directory
CPU #1 - tries to access CPU information page
CPU #1 - physical address of wrong CPU information loaded into TLB
*** CPU #1 using wrong CPU information until TLB flushed ***
CPU #0 - returns to instruction that caused page fault
CPU #0 - intruction retry causes new page table entry to be loaded into TLB
CPU #0 - removes CPU information page from page table (leaving it in TLB)
Also you may not need to use a debugger interrupt to remove the CPU information page from the page table, if the CPUs TLB caching is reliable (no guarantees here - extensive testing may be needed to prove it's OK due to different CPU TLB cache sizes, etc). The basic idea would go something like:
Code: Select all
pageFaultHandler {
if( (CR2 & 0xFFFFF000) = CPU_information_page) {
temp = find_the_physical_address_for_this_CPUs_info();
lock_spinlock(the_CPU_information_PGF_lock);
set_page_table_entry(CR2, temp); /* insert CPU info page from page table */
invalidate_TLB(CR2); /* needed for Cyrix at least */
temp = (char *)CR2; /* dummy read to ensure new page table entry is in TLB */
set_page_table_entry(CR2, NULL); /* remove CPU info page from page table */
unlock_spinlock(the_CPU_information_PGF_lock);
return;
} else {
...
}
}
Then any CPU can access data from it's CPU information without any messing about. Otherwise locking and unlocking the re-entrancy lock may end up in different bits of code (e.g. lock in PGF handler and unlock in debug handler, or lock and unlock around all code that accesses the CPU info).
Depending on how often the CPU information needs to be accessed, the size of the CPUs TLB caches (number of TLB misses, page faults, etc) and the number of CPUs in the computer (lock contention, data cache thrashing where the re-entrancy lock is stored), it might be better to search the array of CPU information structures each time...
It may also be better to use GS (user-level code can still use DS, ES, FS and SS, but GS may be trashed by the kernel unexpectedly), and inline assembly:
Code: Select all
set_GS() {
temp = local_APIC_ID;
for(GS = first_CPU_GS; last_CPU_GS; GS = GS + 8) {
if(GS:CPUinfoStruct.APIC_ID = local_APIC_ID) return;
}
}
some_code_that_uses_CPU_info() {
if( (GS < first_CPU_GS) || (GS > first_CPU_GS) ) set_GS();
...
}
Another possibility would be to use a local APIC register (e.g. the local APIC timer count register if you don't use the timer), or even one of the debugging registers (e.g. DR3). Something like:
Code: Select all
some_code_that_uses_CPU_info() {
CPU_info_struct *CPU_info = DR3_or_local_APIC_register;
something = CPU_info->foo;
}
Candy wrote:
It won't be removed when another cpu uses it, it's no MOESI cache, it can only be read in and forgotten later on. It will only be invalidated by some ipi (or something else) sending an invalidation request. Since all processors have their own page for that place, they should never be freed.
Ooops, I got TLB and data cache mixed
You're right - the CPU would always have the page in it's TLB (unless it runs out of free TLB entries). The page would be in the CPUs data cache/s unless another CPU needed to modify the information or the CPU ran out of free data cache entries.
Cheers,
Brendan