xv6, line by line
tour 44
Tours44 One interrupt, three landing sites

Tour 44 · The dance of privilege · about 28 minutes · 19 steps

One interrupt, three landing sites

A timer interrupt is the same event every time: on some hart, the clock passes stimecmp. But where it lands depends entirely on what that hart was doing. There are exactly three possibilities in xv6, and they differ in every answer to the master question:

  • Site A, user mode. The hart was running a program. The interrupt enters through the trampoline, switches page table and stack, and lands on an empty kernel stack.
  • Site B, a process in the kernel. The hart was in a system call. The interrupt pushes a 256-byte frame onto the process’s kernel stack, on top of whatever is there, and may end with the process continuing on a different hart.
  • Site C, the scheduler. The hart was between processes. The frame goes onto the scheduler stack, and there is nobody to preempt.

This tour visits all three with values measured in this build: gdb attached to QEMU while usertests preempt ran on three harts. Along the way it answers why kernelvec doesn’t restore tp, why kerneltrap keeps sepc and sstatus in local variables, and why the scheduler opens a two-instruction window for interrupts.

Best after: 8. Traps taken inside the kernel, 11. From a timer tick to a context switch, 41. Every transition: mode, stack and page table, 43. A system call, CSR by CSR

Who is running where

usertests preempt is running. It forks three children that spin forever in user mode, then kills them. In our run:

Hart What it is doing
0 Running child pid 5, spinning in user mode: site A
1 Running usertests itself (pid 3), entering a system call: site B
2 Idle in its scheduler at times: site C

The harts trade roles constantly; the measurements below come from several moments of the same run, and each step names the hart it saw. The shell (pid 2) is waiting for usertests.

Three harts are running. This tour follows one path through the code, but the machine has three CPUs executing at the same time. Watch the locks held display at the top of each step, and read the Meanwhile, on other harts boxes: they show what the other CPUs could be doing at that very moment.
The route
  1. 1Each hart's own alarm clock kernel/start.c
  2. 2Site A: a child spinning in user mode user/usertests.c
  3. 3The tick enters through the trampoline kernel/trampoline.S
  4. 4usertrap treats the tick as a device kernel/trap.c
  5. 5Site B: usertests enables interrupts in a system call kernel/trap.c
  6. 6The hardware's part, and a frame on the current stack kernel/kernelvec.S
  7. 7Saving only what C might clobber kernel/kernelvec.S
  8. 8kerneltrap keeps sepc and sstatus for itself kernel/trap.c
  9. 9A tick with a process attached means yield kernel/trap.c
  10. 10Resumed on hart 0, with hart 0's leftovers in the CSRs kernel/proc.c
  11. 11Putting this thread's sepc and sstatus back kernel/trap.c
  12. 12Restore everything except tp, then sret kernel/kernelvec.S
  13. 13Site C: the scheduler opens a two-instruction window kernel/proc.c
  14. 14The trap lands on the scheduler stack kernel/kernelvec.S
  15. 15No process, no yield kernel/trap.c
  16. 16Why the window is open, and why it is so small kernel/proc.c
  17. 17Why the scheduler needs a stack of its own kernel/proc.c
  18. 18stvec is right for every landing site kernel/trap.c
  19. 19Three sites, side by side kernel/kernelvec.S

Keys: ← → step · Home start