// Try to acquire TAS lock val = atomic_cmpxchg_acquire(&lock->val, 0, _Q_LOCKED_VAL); // expected value: 0, new value: _Q_LOCKED_VAL // if &lock->val is 0: &lock->val = 0, val = 0 // if &lock->val is not 0: val = &lock->val if (val == 0) // Got the lock, return directly, then execute critical setion return; curr_node = this_cpu_ptr(&komb_nodes[0]); // Begin slow path function komb_spin_lock_slowpath(lock); // Switch stack in this function }
/** * 41 if qnode.request == PRCSD : **/ // Check if request has been processed (critical section executed by combiner) if (curr_node->completed) { return0; } } // Phase 2 ... // Phase 3 ... }
Phase 2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
// Now waiter is at the head of the queue
/* * we're at the head of the waitqueue, wait for the owner & pending to * go away. * * *,x,y -> *,0,0 * * this wait loop must use a load-acquire such that we match the * store-release that clears the locked bit and create lock * sequentiality; this is because the set_locked() function below * does not imply a full barrier. * */ // Want to read &lock->val // !(VAL & _Q_LOCKED_PENDING_MASK) is a condition // when condition is true, read the value and save it to val val = atomic_cond_read_acquire(&lock->val, !(VAL & _Q_LOCKED_PENDING_MASK));
/* * claim the lock: * * n,0,0 -> 0,0,1 : lock, uncontended * *,*,0 -> *,*,1 : lock, contended * * If the queue head is the only one in the queue (lock value == tail) * and nobody is pending, clear the tail code and grab the lock. * Otherwise, we only need to grab the lock. */
/** * 52 if CAS(&lock.tail, qnode, None) == qnode: * 53 return # If only one in the queue, return **/ if (((val & _Q_TAIL_MASK) == tail) && atomic_try_cmpxchg_relaxed(&lock->val, &val, _Q_LOCKED_VAL)) goto release; /* No contention */
/* Either somebody is queued behind us or _Q_PENDING_VAL is set */ // Declare combining phase (line 63 in pesudocode) check_and_set_combiner(lock);
/* * contended path; wait for next if not observed yet, release. */
/** * 54 qnext = qnode.next * 55 while qnext is None: * 56 qnext = qnode.next **/ // Wait until qnext is get smp_cond_load_relaxed_sched(&curr_node->next, (VAL)); next_node = curr_node->next;
/** * 58 if qnext.next == None: * 59 notify_next_queue_head(qnext) # next waiter is combiner * 60 return **/ // <2 nodes in the queue, return if (next_node == NULL) { set_locked(lock); /* * Make this node spin on the locked variable and then it will * become the combiner. */ curr_node->locked = false; return; }
val = atomic_cmpxchg_acquire(&lock->val, 0, _Q_LOCKED_VAL); if (val == 0) return;
Fastpath: is val == 0 , get the lock
In phase 2
Now current node is notified by the previous node
Its CS is not executed
Wants to aquire global lock
1 2 3 4 5 6 7 8 9 10 11 12 13 14
/* * we're at the head of the waitqueue, wait for the owner & pending to * go away. * * *,x,y -> *,0,0 * * this wait loop must use a load-acquire such that we match the * store-release that clears the locked bit and create lock * sequentiality; this is because the set_locked() function below * does not imply a full barrier. * */
val = atomic_cond_read_acquire(&lock->val, !(VAL & _Q_LOCKED_PENDING_MASK));
Since
1
#define atomic_cond_read_acquire(v, c) smp_cond_load_acquire(&(v)->counter, (c))
When lock is not pending, read lock value, add ACQUIRE barrier?
1 2 3 4 5 6 7 8 9 10
/** * set_locked - Set the lock bit and own the lock * @lock: Pointer to queued spinlock structure * * *,*,0 -> *,0,1 */ static __always_inline voidset_locked(struct qspinlock *lock) { WRITE_ONCE(lock->locked, _Q_LOCKED_VAL); }