|
|
@@ -96,6 +96,42 @@ static inline struct mcs_spinlock *decode_tail(u32 tail)
|
|
|
|
|
|
#define _Q_LOCKED_PENDING_MASK (_Q_LOCKED_MASK | _Q_PENDING_MASK)
|
|
|
|
|
|
+/**
|
|
|
+ * clear_pending_set_locked - take ownership and clear the pending bit.
|
|
|
+ * @lock: Pointer to queued spinlock structure
|
|
|
+ *
|
|
|
+ * *,1,0 -> *,0,1
|
|
|
+ */
|
|
|
+static __always_inline void clear_pending_set_locked(struct qspinlock *lock)
|
|
|
+{
|
|
|
+ atomic_add(-_Q_PENDING_VAL + _Q_LOCKED_VAL, &lock->val);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * xchg_tail - Put in the new queue tail code word & retrieve previous one
|
|
|
+ * @lock : Pointer to queued spinlock structure
|
|
|
+ * @tail : The new queue tail code word
|
|
|
+ * Return: The previous queue tail code word
|
|
|
+ *
|
|
|
+ * xchg(lock, tail)
|
|
|
+ *
|
|
|
+ * p,*,* -> n,*,* ; prev = xchg(lock, node)
|
|
|
+ */
|
|
|
+static __always_inline u32 xchg_tail(struct qspinlock *lock, u32 tail)
|
|
|
+{
|
|
|
+ u32 old, new, val = atomic_read(&lock->val);
|
|
|
+
|
|
|
+ for (;;) {
|
|
|
+ new = (val & _Q_LOCKED_PENDING_MASK) | tail;
|
|
|
+ old = atomic_cmpxchg(&lock->val, val, new);
|
|
|
+ if (old == val)
|
|
|
+ break;
|
|
|
+
|
|
|
+ val = old;
|
|
|
+ }
|
|
|
+ return old;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* queued_spin_lock_slowpath - acquire the queued spinlock
|
|
|
* @lock: Pointer to queued spinlock structure
|
|
|
@@ -178,15 +214,7 @@ void queued_spin_lock_slowpath(struct qspinlock *lock, u32 val)
|
|
|
*
|
|
|
* *,1,0 -> *,0,1
|
|
|
*/
|
|
|
- for (;;) {
|
|
|
- new = (val & ~_Q_PENDING_MASK) | _Q_LOCKED_VAL;
|
|
|
-
|
|
|
- old = atomic_cmpxchg(&lock->val, val, new);
|
|
|
- if (old == val)
|
|
|
- break;
|
|
|
-
|
|
|
- val = old;
|
|
|
- }
|
|
|
+ clear_pending_set_locked(lock);
|
|
|
return;
|
|
|
|
|
|
/*
|
|
|
@@ -203,37 +231,26 @@ queue:
|
|
|
node->next = NULL;
|
|
|
|
|
|
/*
|
|
|
- * We have already touched the queueing cacheline; don't bother with
|
|
|
- * pending stuff.
|
|
|
- *
|
|
|
- * trylock || xchg(lock, node)
|
|
|
- *
|
|
|
- * 0,0,0 -> 0,0,1 ; no tail, not locked -> no tail, locked.
|
|
|
- * p,y,x -> n,y,x ; tail was p -> tail is n; preserving locked.
|
|
|
+ * We touched a (possibly) cold cacheline in the per-cpu queue node;
|
|
|
+ * attempt the trylock once more in the hope someone let go while we
|
|
|
+ * weren't watching.
|
|
|
*/
|
|
|
- for (;;) {
|
|
|
- new = _Q_LOCKED_VAL;
|
|
|
- if (val)
|
|
|
- new = tail | (val & _Q_LOCKED_PENDING_MASK);
|
|
|
-
|
|
|
- old = atomic_cmpxchg(&lock->val, val, new);
|
|
|
- if (old == val)
|
|
|
- break;
|
|
|
-
|
|
|
- val = old;
|
|
|
- }
|
|
|
+ if (queued_spin_trylock(lock))
|
|
|
+ goto release;
|
|
|
|
|
|
/*
|
|
|
- * we won the trylock; forget about queueing.
|
|
|
+ * We have already touched the queueing cacheline; don't bother with
|
|
|
+ * pending stuff.
|
|
|
+ *
|
|
|
+ * p,*,* -> n,*,*
|
|
|
*/
|
|
|
- if (new == _Q_LOCKED_VAL)
|
|
|
- goto release;
|
|
|
+ old = xchg_tail(lock, tail);
|
|
|
|
|
|
/*
|
|
|
* if there was a previous node; link it and wait until reaching the
|
|
|
* head of the waitqueue.
|
|
|
*/
|
|
|
- if (old & ~_Q_LOCKED_PENDING_MASK) {
|
|
|
+ if (old & _Q_TAIL_MASK) {
|
|
|
prev = decode_tail(old);
|
|
|
WRITE_ONCE(prev->next, node);
|
|
|
|