Test yourself · category 9 of 20
Spinlocks and interrupt state acquire and release at the instruction level, push_off/pop_off, noff and intena, and why interrupts must be off while a spinlock is held.
All Not yet answered Got wrong Reset this category
acquire calls push_off before it tries to take the lock. Why must
interrupts be off before the lock is taken, rather than just after?
kernel/spinlock.c
24 push_off (); // disable interrupts to avoid deadlock.
28 // On RISC-V, __atomic_exchange_n turns into an atomic swap:
31 // amoswap.w.aq a5, a5, (s1)
33 // Passing __ATOMIC_ACQUIRE to __atomic_exchange_n tells
34 // the C compiler and the processor to not move loads or stores
35 // past this point, to ensure that the critical section's memory
36 // references happen strictly after the lock is acquired.
37 while ( __atomic_exchange_n(& lk -> locked , 1 , __ATOMIC_ACQUIRE) != 0 )
40 // Record info about lock acquisition for holding() and debugging.
Check Try again
2 solid Choose all that apply Which of these locks are acquired inside an interrupt handler (from devintr or
code it calls) in this kernel?
Check Try again
A process is in kwait holding wait_lock and a zombie child’s p->lock. It calls
freeproc , which calls kfree , which is now inside acquire(&kmem.lock) with the
lock held. What is mycpu()->noff at that moment?
Check Try again
Put the steps of acquire in the order they happen.
kernel/spinlock.c
24 push_off (); // disable interrupts to avoid deadlock.
28 // On RISC-V, __atomic_exchange_n turns into an atomic swap:
31 // amoswap.w.aq a5, a5, (s1)
33 // Passing __ATOMIC_ACQUIRE to __atomic_exchange_n tells
34 // the C compiler and the processor to not move loads or stores
35 // past this point, to ensure that the critical section's memory
36 // references happen strictly after the lock is acquired.
37 while ( __atomic_exchange_n(& lk -> locked , 1 , __ATOMIC_ACQUIRE) != 0 )
40 // Record info about lock acquisition for holding() and debugging.
holding(lk) check, panic if this hart already holds the lock↑ ↓ amoswap.w.aq in a loop until the old value is 0↑ ↓ push_off(): interrupts off, noff + 1↑ ↓ lk->cpu = mycpu()↑ ↓ Check Try again
In pop_off , click the line that can turn interrupts back on.
Your pick: none yet (click a line in the code)
Check Try again
6 solid Fill in the machine state Hart 1 is running cat’s consoleread (a read system call). It has just executed
line 95, acquire(&cons.lock), and the lock is now held. What is the state of hart 1?
kernel/console.c
97 // wait until interrupt handler has put some
98 // input into cons.buffer.
Privilege mode choose… M (machine) S (supervisor) U (user) Active stack choose… user stack the process's kernel stack scheduler stack (stack0) boot stack (stack0) no usable stack Page table (satp) choose… paging off kernel page table user page table Interrupts (sstatus.SIE) choose… on off noff choose… 0 1 2 3 4 intena choose… 0 1 — (noff is 0)
Check Try again
gdb shows sstatus = 0x200000122 on a hart running kernel code. Decode it.
Value: 0x200000122
SIE (bit 1) choose… 1 (interrupts enabled) 0 (interrupts disabled) SPIE (bit 5) choose… 1 0 SPP (bit 8): where sret returns choose… supervisor mode user mode Could this hart be holding a spinlock right now? choose… no yes
Check Try again
Match each panic message with the mistake that triggers it.
panic: acquirechoose… this hart tried to take a spinlock it already holds this hart released a spinlock it does not hold pop_off found interrupts already on a process tried to switch away while holding a spinlock besides its own p->lock panic: releasechoose… this hart tried to take a spinlock it already holds this hart released a spinlock it does not hold pop_off found interrupts already on a process tried to switch away while holding a spinlock besides its own p->lock panic: sched lockschoose… this hart tried to take a spinlock it already holds this hart released a spinlock it does not hold pop_off found interrupts already on a process tried to switch away while holding a spinlock besides its own p->lock panic: pop_off - interruptiblechoose… this hart tried to take a spinlock it already holds this hart released a spinlock it does not hold pop_off found interrupts already on a process tried to switch away while holding a spinlock besides its own p->lock
Check Try again
9 deep True or false, and why True or false: holding tells you whether the current process holds the lock.
Check Try again
Hart 0 holds a spinlock. Hart 1, inside acquire , executes the swap below with
a5 = 1 and s1 pointing at the lock’s locked word. What happens?
80000c02: mv a5,a4
80000c04: amoswap.w.aq a5,a5,(s1)
80000c08: sext.w a5,a5
80000c0a: bnez a5,80000c02
kernel/spinlock.c
37 while ( __atomic_exchange_n(& lk -> locked , 1 , __ATOMIC_ACQUIRE) != 0 )
Check Try again
Hart 0 holds kmem.lock. A process on hart 1 calls acquire(&kmem.lock). Until hart 0
releases it, what is hart 1 doing?
Check Try again
12 warm-up True or false, and why True or false: push_off saves the current value of SIE into intena every time it is
called.
kernel/spinlock.c
95 // disable interrupts to prevent an involuntary context
96 // switch while using mycpu().
Check Try again
A spinlock is free (locked is 0). Three harts execute their amoswap.w.aq on its
locked word at the same instant. How many of the three swaps return 0?
Check Try again
Why does xv6 hold spinlocks only for short stretches of code, never across a disk read
or a wait for input?
Check Try again
Check Try again
release frees the lock with __atomic_store_n(&lk->locked, 0, __ATOMIC_RELEASE),
which compiles to fence rw,w followed by sw zero,0(s1). What does the fence
prevent?
kernel/spinlock.c
53 // Release the lock, equivalent to lk->locked = 0.
55 // This code doesn't use a C assignment, since the C standard
56 // implies that an assignment might be implemented with
57 // multiple store instructions.
59 // On RISC-V, __atomic_store_n turns into a single atomic store:
64 // The __ATOMIC_RELEASE argument to __atomic_store_n tells the
65 // the C compiler and the CPU to not move loads or stores past
66 // this point, to ensure that all the stores in the critical
67 // section are visible to other CPUs before the lock is released,
68 // and that loads in the critical section occur strictly before
69 // the lock is released.
71 // On RISC-V, this generates a fence instruction before the store:
73 __atomic_store_n(& lk -> locked , 0 , __ATOMIC_RELEASE);
Check Try again
release clears lk->cpu on line 51 before it stores 0 into lk->locked on line
73. What could go wrong if the two were the other way round?
kernel/spinlock.c
53 // Release the lock, equivalent to lk->locked = 0.
55 // This code doesn't use a C assignment, since the C standard
56 // implies that an assignment might be implemented with
57 // multiple store instructions.
59 // On RISC-V, __atomic_store_n turns into a single atomic store:
64 // The __ATOMIC_RELEASE argument to __atomic_store_n tells the
65 // the C compiler and the CPU to not move loads or stores past
66 // this point, to ensure that all the stores in the critical
67 // section are visible to other CPUs before the lock is released,
68 // and that loads in the critical section occur strictly before
69 // the lock is released.
71 // On RISC-V, this generates a fence instruction before the store:
73 __atomic_store_n(& lk -> locked , 0 , __ATOMIC_RELEASE);
Check Try again
Click the line of release from which another hart’s acquire can succeed.
kernel/spinlock.c
53 // Release the lock, equivalent to lk->locked = 0.
55 // This code doesn't use a C assignment, since the C standard
56 // implies that an assignment might be implemented with
57 // multiple store instructions.
59 // On RISC-V, __atomic_store_n turns into a single atomic store:
64 // The __ATOMIC_RELEASE argument to __atomic_store_n tells the
65 // the C compiler and the CPU to not move loads or stores past
66 // this point, to ensure that all the stores in the critical
67 // section are visible to other CPUs before the lock is released,
68 // and that loads in the critical section occur strictly before
69 // the lock is released.
71 // On RISC-V, this generates a fence instruction before the store:
73 __atomic_store_n(& lk -> locked , 0 , __ATOMIC_RELEASE);
Your pick: none yet (click a line in the code)
Check Try again
A system call on hart 0 is in sys_uptime , holding tickslock, when hart 0’s timer
deadline passes. Put the events in order.
the pending timer interrupt is taken, and clockintr acquires tickslock, which hart 0 no longer holds ↑ ↓ pop_off takes noff to 0, sees intena 1, and sets SIE ↑ ↓ release stores 0 into tickslock.locked ↑ ↓ the amoswap takes tickslock ↑ ↓ the timer deadline passes: the interrupt becomes pending but is not taken ↑ ↓ acquire’s push_off clears SIE on hart 0 and records intena = 1 ↑ ↓ Check Try again
20 solid Choose all that apply Check Try again
The comment above holding says “Interrupts must be off.” What could go wrong if
holding(lk) ran with interrupts on?
kernel/spinlock.c
78 // Check whether this cpu is holding the lock.
79 // Interrupts must be off.
Check Try again
sh went to sleep in consoleread (a system call) on hart 0, so the p->lock it
took in sleep was acquired with SIE on and hart 0’s intena is 1. Suppose line 456
(mycpu()->intena = 0) were deleted. What goes wrong when hart 0’s scheduler
continues after its swtch?
kernel/proc.c
448 // Switch to chosen process. It is the process's job
449 // to release its lock and then reacquire it
450 // before jumping back to us.
455 // Don't re-enable interrupts on release.
458 // Process is done running for now.
459 // It should have changed its p->state before coming back.
Check Try again
23 deep Choose all that apply Which of these can change sstatus.SIE from 0 to 1 on a hart in this kernel?
Check Try again
24 deep Fill in the machine state A process called exit(0). kexit has just executed line 360,
release(&wait_lock), while still holding its own p->lock (taken on line 355). What
is the state of its hart?
kernel/proc.c
349 // Give any children to init.
352 // Parent might be sleeping in wait().
362 // Jump into the scheduler, never to return.
Privilege mode choose… M (machine) S (supervisor) U (user) Active stack choose… user stack the process's kernel stack scheduler stack (stack0) boot stack (stack0) no usable stack Page table (satp) choose… paging off kernel page table user page table Interrupts (sstatus.SIE) choose… on off noff choose… 0 1 2 3 4 intena choose… 0 1 — (noff is 0)
Check Try again
iput drops the last reference to an unlinked file. Holding itable.lock, it calls
acquiresleep(&ip->lock) on line 362. Inside acquiresleep , line 32 calls
myproc . Right after myproc’s push_off, what is mycpu()->noff?
Check Try again
← fork, exec, exit, wait, kill All categories Sleep/wakeup and sleep-locks →