Keyboard lights, TLB shootdowns, battery in sysfs, /proc/pid/maps
Caps, Num and Scroll Lock now light their LEDs on PS/2 and USB keyboards, share one state between keyboards, and stop toggling on key repeat. X sets them through evdev writes while it holds the keyboard. Unmapping memory in a process with threads on other CPUs now flushes their TLBs with an IPI. Without it a freed page stayed reachable through stale translations, which corrupted Epiphany's heap on SMP. ACPI batteries and AC adapters appear under /sys/class/power_supply. /proc/<pid>/maps lists file mappings, and a process killed by a fault names the file and offset it died in. r8169 gets a 256-entry RX ring. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
This commit is contained in:
parent
73dcea7496
commit
438e7af141
18 changed files with 945 additions and 9 deletions
|
|
@ -581,3 +581,19 @@ void cmostime(struct rtcdate *r)
|
|||
*r = t1;
|
||||
r->year += 2000;
|
||||
}
|
||||
|
||||
// A fixed interrupt to one processor, by APIC id. Waits for the local APIC
|
||||
// to say it has sent the last one before writing the next: two ICR writes
|
||||
// back to back lose the first.
|
||||
void
|
||||
lapic_send_ipi(int apicid, int vector)
|
||||
{
|
||||
if (!lapic)
|
||||
return;
|
||||
while (lapic[ICRLO] & DELIVS)
|
||||
;
|
||||
lapicw(ICRHI, apicid << 24);
|
||||
lapicw(ICRLO, vector); // fixed, physical, edge, no shorthand
|
||||
while (lapic[ICRLO] & DELIVS)
|
||||
;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@
|
|||
#include "core/spinlock.h"
|
||||
#include "arch/vmap.h"
|
||||
#include "ldls/kldls.h"
|
||||
#include "core/irq.h"
|
||||
#include "arch/traps.h"
|
||||
|
||||
int mappages(pml4e_t *pgdir, void *va, addr_t size, addr_t pa, int perm);
|
||||
static pte_t * walkpgdir(pml4e_t *pml4, const void *va, int alloc);
|
||||
|
|
@ -789,6 +791,90 @@ defer_free(uint64 pa)
|
|||
}
|
||||
}
|
||||
|
||||
// --- TLB shootdown ------------------------------------------------------------
|
||||
//
|
||||
// The deferred free above keeps a stale translation from reaching a page
|
||||
// someone else now owns. It does not keep one address space coherent: when a
|
||||
// thread unmaps a range and mmap hands the same addresses straight back, a
|
||||
// thread on another processor goes on reading and writing the *old* pages
|
||||
// through its TLB. Its stores vanish and its loads are stale. musl's malloc
|
||||
// noticed first -- a group's metadata no longer pointing back at it -- and
|
||||
// Epiphany, forty threads on four processors, died at a different address
|
||||
// every run.
|
||||
//
|
||||
// So whoever unmaps from a shared address space interrupts the processors
|
||||
// running it and waits until each has reloaded CR3. The vector is one of the
|
||||
// MSI lines, which trap() already dispatches with an EOI.
|
||||
|
||||
static int tlb_irq = -1;
|
||||
static volatile int tlb_busy;
|
||||
static volatile int tlb_acks;
|
||||
|
||||
static void
|
||||
tlb_ipi(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
lcr3(rcr3()); // everything but global pages
|
||||
__atomic_add_fetch(&tlb_acks, 1, __ATOMIC_RELEASE);
|
||||
}
|
||||
|
||||
void
|
||||
tlb_init(void)
|
||||
{
|
||||
tlb_irq = irq_alloc_msi();
|
||||
if (tlb_irq < 0 || irq_register(tlb_irq, tlb_ipi, 0, "tlb", IRQ_EDGE_HIGH | IRQ_QUIET) < 0) {
|
||||
cprintf("vm: no vector for TLB shootdown; threads on several CPUs are unsafe\n");
|
||||
tlb_irq = -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Every other processor now running pgdir forgets its translations.
|
||||
static void
|
||||
tlb_shootdown(pml4e_t *pgdir)
|
||||
{
|
||||
int targets = 0, me;
|
||||
|
||||
if (tlb_irq < 0 || ncpu_online < 2)
|
||||
return;
|
||||
// Only one shootdown at a time, taken without disabling interrupts: two
|
||||
// processors each waiting with interrupts off for the other to answer
|
||||
// would wait for ever.
|
||||
while (__atomic_exchange_n(&tlb_busy, 1, __ATOMIC_ACQUIRE))
|
||||
__builtin_ia32_pause();
|
||||
__atomic_store_n(&tlb_acks, 0, __ATOMIC_RELAXED);
|
||||
pushcli();
|
||||
me = cpunum();
|
||||
for (int c = 0; c < ncpu; c++) {
|
||||
if (c == me || cpu_pgdir[c] != pgdir || !cpus[c].started)
|
||||
continue;
|
||||
lapic_send_ipi(cpus[c].apicid, T_IRQ0 + tlb_irq);
|
||||
targets++;
|
||||
}
|
||||
popcli();
|
||||
// A processor that switched away in the meantime still answers: the
|
||||
// reload is harmless. One that never does is said once, not waited on.
|
||||
uint64 spins = 0;
|
||||
while (__atomic_load_n(&tlb_acks, __ATOMIC_ACQUIRE) < targets) {
|
||||
__builtin_ia32_pause();
|
||||
if (++spins == 200000000ULL) {
|
||||
static int said;
|
||||
if (!said++)
|
||||
cprintf("vm: a processor did not answer a TLB shootdown\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
__atomic_store_n(&tlb_busy, 0, __ATOMIC_RELEASE);
|
||||
}
|
||||
|
||||
// After unmapping from pgdir: shoot down if other threads may hold it.
|
||||
static void
|
||||
tlb_after_unmap(pml4e_t *pgdir)
|
||||
{
|
||||
struct proc *p = myproc();
|
||||
if (p && p->pgdir == pgdir && mm_shared(p->mm))
|
||||
tlb_shootdown(pgdir);
|
||||
}
|
||||
|
||||
// Free a user page just unmapped from pgdir: now, or once no TLB can hold it.
|
||||
static void
|
||||
user_page_free(pml4e_t *pgdir, uint64 pa)
|
||||
|
|
@ -836,6 +922,7 @@ vm_unmap_range(pml4e_t *pgdir, uint64 va, uint64 length)
|
|||
{
|
||||
uint64 a = PGROUNDDOWN(va);
|
||||
uint64 end = PGROUNDUP(va + length);
|
||||
int any = 0;
|
||||
for (; a < end; a += PGSIZE) {
|
||||
pte_t *pte = walkpgdir(pgdir, (void*)a, 0);
|
||||
if (pte && (*pte & PTE_P)) {
|
||||
|
|
@ -843,8 +930,11 @@ vm_unmap_range(pml4e_t *pgdir, uint64 va, uint64 length)
|
|||
*pte = 0;
|
||||
invlpg((void*)a);
|
||||
user_page_free(pgdir, pa);
|
||||
any = 1;
|
||||
}
|
||||
}
|
||||
if (any)
|
||||
tlb_after_unmap(pgdir);
|
||||
}
|
||||
|
||||
// Tear down a mapping without freeing what is underneath it.
|
||||
|
|
@ -860,13 +950,17 @@ vm_unmap_norelease(pml4e_t *pgdir, uint64 va, uint64 length)
|
|||
{
|
||||
uint64 a = PGROUNDDOWN(va);
|
||||
uint64 end = PGROUNDUP(va + length);
|
||||
int any = 0;
|
||||
for (; a < end; a += PGSIZE) {
|
||||
pte_t *pte = walkpgdir(pgdir, (void*)a, 0);
|
||||
if (pte && (*pte & PTE_P)) {
|
||||
*pte = 0;
|
||||
invlpg((void*)a);
|
||||
any = 1;
|
||||
}
|
||||
}
|
||||
if (any)
|
||||
tlb_after_unmap(pgdir);
|
||||
}
|
||||
|
||||
// Duplicate an mmap()'d VMA region from src into dst for fork().
|
||||
|
|
@ -996,6 +1090,7 @@ deallocuvm(pml4e_t *pgdir, uint64 oldsz, uint64 newsz)
|
|||
user_page_free(pgdir, pa);
|
||||
}
|
||||
}
|
||||
tlb_after_unmap(pgdir);
|
||||
return newsz;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue