Skip to content

Commit 95bcade

Browse files
wildea01Ingo Molnar
authored andcommitted
locking/qspinlock: Ensure node is initialised before updating prev->next
When a locker ends up queuing on the qspinlock locking slowpath, we initialise the relevant mcs node and publish it indirectly by updating the tail portion of the lock word using xchg_tail. If we find that there was a pre-existing locker in the queue, we subsequently update their ->next field to point at our node so that we are notified when it's our turn to take the lock. This can be roughly illustrated as follows: /* Initialise the fields in node and encode a pointer to node in tail */ tail = initialise_node(node); /* * Exchange tail into the lockword using an atomic read-modify-write * operation with release semantics */ old = xchg_tail(lock, tail); /* If there was a pre-existing waiter ... */ if (old & _Q_TAIL_MASK) { prev = decode_tail(old); smp_read_barrier_depends(); /* ... then update their ->next field to point to node. WRITE_ONCE(prev->next, node); } The conditional update of prev->next therefore relies on the address dependency from the result of xchg_tail ensuring order against the prior initialisation of node. However, since the release semantics of the xchg_tail operation apply only to the write portion of the RmW, then this ordering is not guaranteed and it is possible for the CPU to return old before the writes to node have been published, consequently allowing us to point prev->next to an uninitialised node. This patch fixes the problem by making the update of prev->next a RELEASE operation, which also removes the reliance on dependency ordering. Signed-off-by: Will Deacon <[email protected]> Acked-by: Peter Zijlstra (Intel) <[email protected]> Cc: Linus Torvalds <[email protected]> Cc: Thomas Gleixner <[email protected]> Link: http://lkml.kernel.org/r/[email protected] Signed-off-by: Ingo Molnar <[email protected]>
1 parent 79e9023 commit 95bcade

File tree

1 file changed

+7
-6
lines changed

1 file changed

+7
-6
lines changed

kernel/locking/qspinlock.c

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -408,14 +408,15 @@ void queued_spin_lock_slowpath(struct qspinlock *lock, u32 val)
408408
*/
409409
if (old & _Q_TAIL_MASK) {
410410
prev = decode_tail(old);
411+
411412
/*
412-
* The above xchg_tail() is also a load of @lock which
413-
* generates, through decode_tail(), a pointer. The address
414-
* dependency matches the RELEASE of xchg_tail() such that
415-
* the subsequent access to @prev happens after.
413+
* We must ensure that the stores to @node are observed before
414+
* the write to prev->next. The address dependency from
415+
* xchg_tail is not sufficient to ensure this because the read
416+
* component of xchg_tail is unordered with respect to the
417+
* initialisation of @node.
416418
*/
417-
418-
WRITE_ONCE(prev->next, node);
419+
smp_store_release(&prev->next, node);
419420

420421
pv_wait_node(node, prev);
421422
arch_mcs_spin_lock_contended(&node->locked);

0 commit comments

Comments
 (0)