How bread and brelse share 30 cached blocks between harts with a spinlock, a sleep-lock and a reference count, and how virtio_disk_rw hands a request to the disk through descriptors, rings and a doorbell, then sleeps until the interrupt.
1warm-upType a number
cat needs file-system block 48, so virtio_disk_rw builds a request for it. Which
sector number does it put in the request header?
The driver has NUM = 8 descriptors, and every request uses exactly three. At most how
many disk requests can be in flight (handed to the device and not yet freed) at once?
kernel/virtio_disk.c
199// allocate three descriptors (they need not be contiguous).
200// disk transfers always use three descriptors.
Put the steps of a disk read in virtio_disk_rw in order.
fill in the header, data and status descriptors
get three free descriptors with alloc3_desc
compute the sector and acquire disk.vdisk_lock
record b in disk.info[idx[0]] and set b->disk = 1
write idx[0] into the avail ring, fence, then increment avail->idx
store to the QUEUE_NOTIFY register
sleep until b->disk == 0, then free the chain
7solidClick the line
Most of the driver talks to the device through ordinary RAM that both share. Click the one
line in this excerpt that writes to a device register (memory-mapped I/O): the
doorbell.
While a request is in flight, gdb shows one of its three descriptors with flags = 0x3.
Decode it (VRING_DESC_F_NEXT = 1, VRING_DESC_F_WRITE = 2).
Value: 0x3
9solidFill in the machine state
Hart 1 is idle in its scheduler loop when the disk interrupt arrives. It traps through
kernelvec and kerneltrap into devintr, which calls virtio_disk_intr. What
is hart 1’s state just after acquire(&disk.vdisk_lock) returns?
cat’s read system call has reached virtio_disk_rw (through fileread,
readi and bread). It holds README’s inode lock and the buffer’s sleep-lock, and is
at line 284, about to ring the doorbell. What is the state of its hart?
Right after boot, binit has built the LRU list and every buffer has refcnt == 0. The
first bget miss scans from bcache.head.prev. Which index i of bcache.buf[i] does
it recycle?
On a cache hit, bget increments b->refcntbefore releasing bcache.lock and
calling acquiresleep. What could go wrong if it released bcache.lock first and
incremented refcnt afterwards?
Child A (hart 1) and child B (hart 2) both need block 46 at once. A’s bget misses,
relabels a buffer as 46 with valid = 0, and releases bcache.lock; then B’s bget runs
while A is reading the block from disk. Which statements are true?
When alloc3_desc gets only one or two descriptors, it gives them back before
virtio_disk_rw sleeps. Why not keep them and wait only for the missing ones?
kernel/virtio_disk.c
199// allocate three descriptors (they need not be contiguous).
200// disk transfers always use three descriptors.
cat issued a request on hart 0 and is asleep. The completion interrupt is taken on
hart 1. How does virtio_disk_intr know which buffer, and so which sleeper, the
completion is for?
virtio_disk_rw waits with while (b->disk == 1) { sleep_prepare(b); release; sleep(); acquire; } instead of sleeping once. When can sleep() return while the disk is still
working on the request?
kernel/virtio_disk.c
286// Wait for virtio_disk_intr() to say request has finished.
True or false: panic("bget: no buffers") can only happen while a log transaction has
pinned buffers; with no transaction running, every buffer is eventually free.
Which of these are done by the hart that takes the disk completion interrupt (in
devintr and virtio_disk_intr), rather than by the process that issued the request?