xv6, line by line
tour 29
Tours29 A disk read, end to end

Tour 29 · File system · about 30 minutes · 18 steps

A disk read, end to end

You type cat README. The first 512 bytes cat asks for live in disk block 48: in a fresh fs.img from this build, README is inode 2, its data blocks are 48, 49 and 50, and it is 2,441 bytes long (read straight out of the image’s root directory and inode table). Nobody has read block 48 since boot, so it is not in memory. This tour follows that one block from the request to the bytes landing in cat’s buffer.

Along the way, cat takes four locks of two different kinds (two sleep-locks and two spinlocks, not counting the brief per-process locks), builds a three-part message for the disk in shared memory, tells the device with a single store to a device register, and goes to sleep. The device copies 1024 bytes straight into kernel memory (DMA (direct memory access)), then raises an interrupt that any hart may answer (in our run, a different one). That hart walks a ring of completed requests and wakes cat, which resumes on whichever hart’s scheduler picks it up.

The numbers in this tour come from a traced run of this build under QEMU and gdb: cat was pid 3, it issued the request for block 48 from hart 0 using descriptors 0, 1 and 2, and hart 1 took the completion interrupt.

Best after: 5. Life of a system call, 9. Device interrupts and the PLIC, 16. sleep and wakeup, and the lost-wakeup problem, 17. Sleep-locks

Who is running where

Hart What it is doing
0 Running cat README (pid 3): the process this tour follows
1 Idle in its scheduler, or running something else
2 Idle, or running something else; the shell (pid 2) is asleep in wait

cat has already opened README (descriptor 3). Opening it needed the root directory (block 47) and the inode block (33), which were already in the buffer cache, so no disk I/O happened until now.

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. 1cat asks for 512 bytes user/cat.c
  2. 2fileread locks the inode kernel/file.c
  3. 3readi finds block 48 kernel/fs.c
  4. 4bread asks the cache first kernel/bio.c
  5. 5bget misses and recycles a buffer kernel/bio.c
  6. 6virtio_disk_rw takes the disk lock kernel/virtio_disk.c
  7. 7Allocating three descriptors kernel/virtio_disk.c
  8. 8The request, in three descriptors kernel/virtio_disk.c
  9. 9Publish in the avail ring, fence, notify kernel/virtio_disk.c
  10. 10cat sleeps on its buffer kernel/virtio_disk.c
  11. 11The device does the transfer by DMA kernel/virtio.h
  12. 12Some hart takes the interrupt kernel/trap.c
  13. 13virtio_disk_intr walks the used ring kernel/virtio_disk.c
  14. 14wakeup makes cat runnable kernel/proc.c
  15. 15cat resumes and frees the descriptors kernel/virtio_disk.c
  16. 16Back in bread, then into cat's buffer kernel/fs.c
  17. 17Three requests, eight descriptors kernel/virtio_disk.c
  18. 18What one block cost user/cat.c

Keys: ← → step · Home start