xv6, line by line
tour 32
Tours32 Crash recovery

Tour 32 · File system · about 33 minutes · 20 steps

Crash recovery

You type echo hi > f on a freshly made disk, and the power goes out. Not before the command, not after it: in the middle of the kernel writing its changes to the disk. When the machine comes back, is there a file f? Is there half of one: an inode that is marked used but that no directory names, or a directory entry pointing at an inode that is still free?

This tour answers that question for every moment the power could fail. It follows the first transaction of echo hi > f, the one that creates f, through the four stages of commit, and for each stage it says exactly what the disk holds. Then it cuts the power (for real, in QEMU), boots again, and watches recover_from_log and install_trans repair the disk before any process gets to look at it. Finally it follows ireclaim, which cleans up the one kind of damage the log cannot prevent: a file deleted while it was still open.

The log itself (how begin_op, log_write and group commit work) is the subject of Tour 31: The log: begin_op, commit and group commit. Disk I/O and the buffer cache are Tour 29: A disk read, end to end and Tour 30: The buffer cache. This tour is about what happens when everything is cut off halfway.

Best after: 29. A disk read, end to end, 30. The buffer cache, 31. The log: begin_op, commit and group commit

Who is running where

The machine has three harts. The disk is a fresh fs.img; on the first boot init has already created console as inode 23. When the tour starts:

Hart What it is doing
0 Idle in its scheduler
1 Running sh (pid 3), the child the shell forked to run echo hi > f: the process this tour follows
2 Idle in its scheduler, or taking a disk interrupt

The parent shell (pid 2) is asleep in kwait. Block numbers in this tour come from this build’s mkfs (nmeta 47: block 1 is the superblock, the log is blocks 2–32, inodes 33–45, the bitmap 46, data from 47) and from a traced copy of the kernel.

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. 1The shell opens f before echo even exists user/sh.c
  2. 2sys_open starts a transaction and creates the file in memory kernel/sysfile.c
  3. 3log_write records a block number, not a write kernel/log.c
  4. 4end_op sees it is the last one out and commits kernel/log.c
  5. 5Four stages, and what the disk holds after each kernel/log.c
  6. 6Stage 1: copy the changed blocks into the log kernel/log.c
  7. 7Stage 2: one block write is the commit kernel/log.c
  8. 8Stage 3: install the blocks at home kernel/log.c
  9. 9Stage 4: erase the transaction, and let the others in kernel/log.c
  10. 10The power cut, as test-xv6.py does it test-xv6.py
  11. 11The next boot, in the first process kernel/proc.c
  12. 12fsinit checks the superblock, then repairs, then reclaims kernel/fs.c
  13. 13initlog reads the header kernel/log.c
  14. 14install_trans, in recovery mode kernel/log.c
  15. 15Clear the log, and the disk is consistent again kernel/log.c
  16. 16How an orphan is made, on purpose user/forphan.c
  17. 17ireclaim scans every inode for orphans kernel/fs.c
  18. 18The orphan gets the close it never had kernel/fs.c
  19. 19The test that does all of this twenty times test-xv6.py
  20. 20What it cost, and what it bought kernel/log.c

Keys: ← → step · Home start