Path: blob/master/tools/memory-model/Documentation/locking.txt
26288 views
[!] Note:1This file expands on the "Locking" section of recipes.txt,2focusing on locklessly accessing shared variables that are3otherwise protected by a lock.45Locking6=======78Locking is well-known and the common use cases are straightforward: Any9CPU holding a given lock sees any changes previously seen or made by any10CPU before it previously released that same lock. This last sentence11is the only part of this document that most developers will need to read.1213However, developers who would like to also access lock-protected shared14variables outside of their corresponding locks should continue reading.151617Locking and Prior Accesses18--------------------------1920The basic rule of locking is worth repeating:2122Any CPU holding a given lock sees any changes previously seen23or made by any CPU before it previously released that same lock.2425Note that this statement is a bit stronger than "Any CPU holding a26given lock sees all changes made by any CPU during the time that CPU was27previously holding this same lock". For example, consider the following28pair of code fragments:2930/* See MP+polocks.litmus. */31void CPU0(void)32{33WRITE_ONCE(x, 1);34spin_lock(&mylock);35WRITE_ONCE(y, 1);36spin_unlock(&mylock);37}3839void CPU1(void)40{41spin_lock(&mylock);42r0 = READ_ONCE(y);43spin_unlock(&mylock);44r1 = READ_ONCE(x);45}4647The basic rule guarantees that if CPU0() acquires mylock before CPU1(),48then both r0 and r1 must be set to the value 1. This also has the49consequence that if the final value of r0 is equal to 1, then the final50value of r1 must also be equal to 1. In contrast, the weaker rule would51say nothing about the final value of r1.525354Locking and Subsequent Accesses55-------------------------------5657The converse to the basic rule also holds: Any CPU holding a given58lock will not see any changes that will be made by any CPU after it59subsequently acquires this same lock. This converse statement is60illustrated by the following litmus test:6162/* See MP+porevlocks.litmus. */63void CPU0(void)64{65r0 = READ_ONCE(y);66spin_lock(&mylock);67r1 = READ_ONCE(x);68spin_unlock(&mylock);69}7071void CPU1(void)72{73spin_lock(&mylock);74WRITE_ONCE(x, 1);75spin_unlock(&mylock);76WRITE_ONCE(y, 1);77}7879This converse to the basic rule guarantees that if CPU0() acquires80mylock before CPU1(), then both r0 and r1 must be set to the value 0.81This also has the consequence that if the final value of r1 is equal82to 0, then the final value of r0 must also be equal to 0. In contrast,83the weaker rule would say nothing about the final value of r0.8485These examples show only a single pair of CPUs, but the effects of the86locking basic rule extend across multiple acquisitions of a given lock87across multiple CPUs.888990Double-Checked Locking91----------------------9293It is well known that more than just a lock is required to make94double-checked locking work correctly, This litmus test illustrates95one incorrect approach:9697/* See Documentation/litmus-tests/locking/DCL-broken.litmus. */98void CPU0(void)99{100r0 = READ_ONCE(flag);101if (r0 == 0) {102spin_lock(&lck);103r1 = READ_ONCE(flag);104if (r1 == 0) {105WRITE_ONCE(data, 1);106WRITE_ONCE(flag, 1);107}108spin_unlock(&lck);109}110r2 = READ_ONCE(data);111}112/* CPU1() is the exactly the same as CPU0(). */113114There are two problems. First, there is no ordering between the first115READ_ONCE() of "flag" and the READ_ONCE() of "data". Second, there is116no ordering between the two WRITE_ONCE() calls. It should therefore be117no surprise that "r2" can be zero, and a quick herd7 run confirms this.118119One way to fix this is to use smp_load_acquire() and smp_store_release()120as shown in this corrected version:121122/* See Documentation/litmus-tests/locking/DCL-fixed.litmus. */123void CPU0(void)124{125r0 = smp_load_acquire(&flag);126if (r0 == 0) {127spin_lock(&lck);128r1 = READ_ONCE(flag);129if (r1 == 0) {130WRITE_ONCE(data, 1);131smp_store_release(&flag, 1);132}133spin_unlock(&lck);134}135r2 = READ_ONCE(data);136}137/* CPU1() is the exactly the same as CPU0(). */138139The smp_load_acquire() guarantees that its load from "flags" will140be ordered before the READ_ONCE() from data, thus solving the first141problem. The smp_store_release() guarantees that its store will be142ordered after the WRITE_ONCE() to "data", solving the second problem.143The smp_store_release() pairs with the smp_load_acquire(), thus ensuring144that the ordering provided by each actually takes effect. Again, a145quick herd7 run confirms this.146147In short, if you access a lock-protected variable without holding the148corresponding lock, you will need to provide additional ordering, in149this case, via the smp_load_acquire() and the smp_store_release().150151152Ordering Provided by a Lock to CPUs Not Holding That Lock153---------------------------------------------------------154155It is not necessarily the case that accesses ordered by locking will be156seen as ordered by CPUs not holding that lock. Consider this example:157158/* See Z6.0+pooncelock+pooncelock+pombonce.litmus. */159void CPU0(void)160{161spin_lock(&mylock);162WRITE_ONCE(x, 1);163WRITE_ONCE(y, 1);164spin_unlock(&mylock);165}166167void CPU1(void)168{169spin_lock(&mylock);170r0 = READ_ONCE(y);171WRITE_ONCE(z, 1);172spin_unlock(&mylock);173}174175void CPU2(void)176{177WRITE_ONCE(z, 2);178smp_mb();179r1 = READ_ONCE(x);180}181182Counter-intuitive though it might be, it is quite possible to have183the final value of r0 be 1, the final value of z be 2, and the final184value of r1 be 0. The reason for this surprising outcome is that CPU2()185never acquired the lock, and thus did not fully benefit from the lock's186ordering properties.187188Ordering can be extended to CPUs not holding the lock by careful use189of smp_mb__after_spinlock():190191/* See Z6.0+pooncelock+poonceLock+pombonce.litmus. */192void CPU0(void)193{194spin_lock(&mylock);195WRITE_ONCE(x, 1);196WRITE_ONCE(y, 1);197spin_unlock(&mylock);198}199200void CPU1(void)201{202spin_lock(&mylock);203smp_mb__after_spinlock();204r0 = READ_ONCE(y);205WRITE_ONCE(z, 1);206spin_unlock(&mylock);207}208209void CPU2(void)210{211WRITE_ONCE(z, 2);212smp_mb();213r1 = READ_ONCE(x);214}215216This addition of smp_mb__after_spinlock() strengthens the lock217acquisition sufficiently to rule out the counter-intuitive outcome.218In other words, the addition of the smp_mb__after_spinlock() prohibits219the counter-intuitive result where the final value of r0 is 1, the final220value of z is 2, and the final value of r1 is 0.221222223No Roach-Motel Locking!224-----------------------225226This example requires familiarity with the herd7 "filter" clause, so227please read up on that topic in litmus-tests.txt.228229It is tempting to allow memory-reference instructions to be pulled230into a critical section, but this cannot be allowed in the general case.231For example, consider a spin loop preceding a lock-based critical section.232Now, herd7 does not model spin loops, but we can emulate one with two233loads, with a "filter" clause to constrain the first to return the234initial value and the second to return the updated value, as shown below:235236/* See Documentation/litmus-tests/locking/RM-fixed.litmus. */237void CPU0(void)238{239spin_lock(&lck);240r2 = atomic_inc_return(&y);241WRITE_ONCE(x, 1);242spin_unlock(&lck);243}244245void CPU1(void)246{247r0 = READ_ONCE(x);248r1 = READ_ONCE(x);249spin_lock(&lck);250r2 = atomic_inc_return(&y);251spin_unlock(&lck);252}253254filter (1:r0=0 /\ 1:r1=1)255exists (1:r2=1)256257The variable "x" is the control variable for the emulated spin loop.258CPU0() sets it to "1" while holding the lock, and CPU1() emulates the259spin loop by reading it twice, first into "1:r0" (which should get the260initial value "0") and then into "1:r1" (which should get the updated261value "1").262263The "filter" clause takes this into account, constraining "1:r0" to264equal "0" and "1:r1" to equal 1.265266Then the "exists" clause checks to see if CPU1() acquired its lock first,267which should not happen given the filter clause because CPU0() updates268"x" while holding the lock. And herd7 confirms this.269270But suppose that the compiler was permitted to reorder the spin loop271into CPU1()'s critical section, like this:272273/* See Documentation/litmus-tests/locking/RM-broken.litmus. */274void CPU0(void)275{276int r2;277278spin_lock(&lck);279r2 = atomic_inc_return(&y);280WRITE_ONCE(x, 1);281spin_unlock(&lck);282}283284void CPU1(void)285{286spin_lock(&lck);287r0 = READ_ONCE(x);288r1 = READ_ONCE(x);289r2 = atomic_inc_return(&y);290spin_unlock(&lck);291}292293filter (1:r0=0 /\ 1:r1=1)294exists (1:r2=1)295296If "1:r0" is equal to "0", "1:r1" can never equal "1" because CPU0()297cannot update "x" while CPU1() holds the lock. And herd7 confirms this,298showing zero executions matching the "filter" criteria.299300And this is why Linux-kernel lock and unlock primitives must prevent301code from entering critical sections. It is not sufficient to only302prevent code from leaving them.303304305