aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPeter Zijlstra (Intel) <peterz@infradead.org>2015-04-24 14:56:32 -0400
committerIngo Molnar <mingo@kernel.org>2015-05-08 06:36:32 -0400
commitc1fb159db9f2e50e0f4025bed92a67a6a7bfa7b7 (patch)
tree9ac363fc2c98e839092dc0392924f296bef87dda
parentd73a33973f16ab6703e75ea00edee857afa3406e (diff)
locking/qspinlock: Add pending bit
Because the qspinlock needs to touch a second cacheline (the per-cpu mcs_nodes[]); add a pending bit and allow a single in-word spinner before we punt to the second cacheline. It is possible so observe the pending bit without the locked bit when the last owner has just released but the pending owner has not yet taken ownership. In this case we would normally queue -- because the pending bit is already taken. However, in this case the pending bit is guaranteed to be released 'soon', therefore wait for it and avoid queueing. Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Signed-off-by: Waiman Long <Waiman.Long@hp.com> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Cc: Andrew Morton <akpm@linux-foundation.org> Cc: Boris Ostrovsky <boris.ostrovsky@oracle.com> Cc: Borislav Petkov <bp@alien8.de> Cc: Daniel J Blueman <daniel@numascale.com> Cc: David Vrabel <david.vrabel@citrix.com> Cc: Douglas Hatch <doug.hatch@hp.com> Cc: H. Peter Anvin <hpa@zytor.com> Cc: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Oleg Nesterov <oleg@redhat.com> Cc: Paolo Bonzini <paolo.bonzini@gmail.com> Cc: Paul E. McKenney <paulmck@linux.vnet.ibm.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Raghavendra K T <raghavendra.kt@linux.vnet.ibm.com> Cc: Rik van Riel <riel@redhat.com> Cc: Scott J Norton <scott.norton@hp.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: virtualization@lists.linux-foundation.org Cc: xen-devel@lists.xenproject.org Link: http://lkml.kernel.org/r/1429901803-29771-4-git-send-email-Waiman.Long@hp.com Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r--include/asm-generic/qspinlock_types.h12
-rw-r--r--kernel/locking/qspinlock.c119
2 files changed, 107 insertions, 24 deletions
diff --git a/include/asm-generic/qspinlock_types.h b/include/asm-generic/qspinlock_types.h
index aec05c7ad2f6..7ee6632cb818 100644
--- a/include/asm-generic/qspinlock_types.h
+++ b/include/asm-generic/qspinlock_types.h
@@ -36,8 +36,9 @@ typedef struct qspinlock {
36 * Bitfields in the atomic value: 36 * Bitfields in the atomic value:
37 * 37 *
38 * 0- 7: locked byte 38 * 0- 7: locked byte
39 * 8- 9: tail index 39 * 8: pending
40 * 10-31: tail cpu (+1) 40 * 9-10: tail index
41 * 11-31: tail cpu (+1)
41 */ 42 */
42#define _Q_SET_MASK(type) (((1U << _Q_ ## type ## _BITS) - 1)\ 43#define _Q_SET_MASK(type) (((1U << _Q_ ## type ## _BITS) - 1)\
43 << _Q_ ## type ## _OFFSET) 44 << _Q_ ## type ## _OFFSET)
@@ -45,7 +46,11 @@ typedef struct qspinlock {
45#define _Q_LOCKED_BITS 8 46#define _Q_LOCKED_BITS 8
46#define _Q_LOCKED_MASK _Q_SET_MASK(LOCKED) 47#define _Q_LOCKED_MASK _Q_SET_MASK(LOCKED)
47 48
48#define _Q_TAIL_IDX_OFFSET (_Q_LOCKED_OFFSET + _Q_LOCKED_BITS) 49#define _Q_PENDING_OFFSET (_Q_LOCKED_OFFSET + _Q_LOCKED_BITS)
50#define _Q_PENDING_BITS 1
51#define _Q_PENDING_MASK _Q_SET_MASK(PENDING)
52
53#define _Q_TAIL_IDX_OFFSET (_Q_PENDING_OFFSET + _Q_PENDING_BITS)
49#define _Q_TAIL_IDX_BITS 2 54#define _Q_TAIL_IDX_BITS 2
50#define _Q_TAIL_IDX_MASK _Q_SET_MASK(TAIL_IDX) 55#define _Q_TAIL_IDX_MASK _Q_SET_MASK(TAIL_IDX)
51 56
@@ -54,5 +59,6 @@ typedef struct qspinlock {
54#define _Q_TAIL_CPU_MASK _Q_SET_MASK(TAIL_CPU) 59#define _Q_TAIL_CPU_MASK _Q_SET_MASK(TAIL_CPU)
55 60
56#define _Q_LOCKED_VAL (1U << _Q_LOCKED_OFFSET) 61#define _Q_LOCKED_VAL (1U << _Q_LOCKED_OFFSET)
62#define _Q_PENDING_VAL (1U << _Q_PENDING_OFFSET)
57 63
58#endif /* __ASM_GENERIC_QSPINLOCK_TYPES_H */ 64#endif /* __ASM_GENERIC_QSPINLOCK_TYPES_H */
diff --git a/kernel/locking/qspinlock.c b/kernel/locking/qspinlock.c
index 029b51ce10ea..af9c2ef6e930 100644
--- a/kernel/locking/qspinlock.c
+++ b/kernel/locking/qspinlock.c
@@ -94,24 +94,28 @@ static inline struct mcs_spinlock *decode_tail(u32 tail)
94 return per_cpu_ptr(&mcs_nodes[idx], cpu); 94 return per_cpu_ptr(&mcs_nodes[idx], cpu);
95} 95}
96 96
97#define _Q_LOCKED_PENDING_MASK (_Q_LOCKED_MASK | _Q_PENDING_MASK)
98
97/** 99/**
98 * queued_spin_lock_slowpath - acquire the queued spinlock 100 * queued_spin_lock_slowpath - acquire the queued spinlock
99 * @lock: Pointer to queued spinlock structure 101 * @lock: Pointer to queued spinlock structure
100 * @val: Current value of the queued spinlock 32-bit word 102 * @val: Current value of the queued spinlock 32-bit word
101 * 103 *
102 * (queue tail, lock value) 104 * (queue tail, pending bit, lock value)
103 *
104 * fast : slow : unlock
105 * : :
106 * uncontended (0,0) --:--> (0,1) --------------------------------:--> (*,0)
107 * : | ^--------. / :
108 * : v \ | :
109 * uncontended : (n,x) --+--> (n,0) | :
110 * queue : | ^--' | :
111 * : v | :
112 * contended : (*,x) --+--> (*,0) -----> (*,1) ---' :
113 * queue : ^--' :
114 * 105 *
106 * fast : slow : unlock
107 * : :
108 * uncontended (0,0,0) -:--> (0,0,1) ------------------------------:--> (*,*,0)
109 * : | ^--------.------. / :
110 * : v \ \ | :
111 * pending : (0,1,1) +--> (0,1,0) \ | :
112 * : | ^--' | | :
113 * : v | | :
114 * uncontended : (n,x,y) +--> (n,0,0) --' | :
115 * queue : | ^--' | :
116 * : v | :
117 * contended : (*,x,y) +--> (*,0,0) ---> (*,0,1) -' :
118 * queue : ^--' :
115 */ 119 */
116void queued_spin_lock_slowpath(struct qspinlock *lock, u32 val) 120void queued_spin_lock_slowpath(struct qspinlock *lock, u32 val)
117{ 121{
@@ -121,6 +125,75 @@ void queued_spin_lock_slowpath(struct qspinlock *lock, u32 val)
121 125
122 BUILD_BUG_ON(CONFIG_NR_CPUS >= (1U << _Q_TAIL_CPU_BITS)); 126 BUILD_BUG_ON(CONFIG_NR_CPUS >= (1U << _Q_TAIL_CPU_BITS));
123 127
128 /*
129 * wait for in-progress pending->locked hand-overs
130 *
131 * 0,1,0 -> 0,0,1
132 */
133 if (val == _Q_PENDING_VAL) {
134 while ((val = atomic_read(&lock->val)) == _Q_PENDING_VAL)
135 cpu_relax();
136 }
137
138 /*
139 * trylock || pending
140 *
141 * 0,0,0 -> 0,0,1 ; trylock
142 * 0,0,1 -> 0,1,1 ; pending
143 */
144 for (;;) {
145 /*
146 * If we observe any contention; queue.
147 */
148 if (val & ~_Q_LOCKED_MASK)
149 goto queue;
150
151 new = _Q_LOCKED_VAL;
152 if (val == new)
153 new |= _Q_PENDING_VAL;
154
155 old = atomic_cmpxchg(&lock->val, val, new);
156 if (old == val)
157 break;
158
159 val = old;
160 }
161
162 /*
163 * we won the trylock
164 */
165 if (new == _Q_LOCKED_VAL)
166 return;
167
168 /*
169 * we're pending, wait for the owner to go away.
170 *
171 * *,1,1 -> *,1,0
172 */
173 while ((val = atomic_read(&lock->val)) & _Q_LOCKED_MASK)
174 cpu_relax();
175
176 /*
177 * take ownership and clear the pending bit.
178 *
179 * *,1,0 -> *,0,1
180 */
181 for (;;) {
182 new = (val & ~_Q_PENDING_MASK) | _Q_LOCKED_VAL;
183
184 old = atomic_cmpxchg(&lock->val, val, new);
185 if (old == val)
186 break;
187
188 val = old;
189 }
190 return;
191
192 /*
193 * End of pending bit optimistic spinning and beginning of MCS
194 * queuing.
195 */
196queue:
124 node = this_cpu_ptr(&mcs_nodes[0]); 197 node = this_cpu_ptr(&mcs_nodes[0]);
125 idx = node->count++; 198 idx = node->count++;
126 tail = encode_tail(smp_processor_id(), idx); 199 tail = encode_tail(smp_processor_id(), idx);
@@ -130,15 +203,18 @@ void queued_spin_lock_slowpath(struct qspinlock *lock, u32 val)
130 node->next = NULL; 203 node->next = NULL;
131 204
132 /* 205 /*
206 * We have already touched the queueing cacheline; don't bother with
207 * pending stuff.
208 *
133 * trylock || xchg(lock, node) 209 * trylock || xchg(lock, node)
134 * 210 *
135 * 0,0 -> 0,1 ; no tail, not locked -> no tail, locked. 211 * 0,0,0 -> 0,0,1 ; no tail, not locked -> no tail, locked.
136 * p,x -> n,x ; tail was p -> tail is n; preserving locked. 212 * p,y,x -> n,y,x ; tail was p -> tail is n; preserving locked.
137 */ 213 */
138 for (;;) { 214 for (;;) {
139 new = _Q_LOCKED_VAL; 215 new = _Q_LOCKED_VAL;
140 if (val) 216 if (val)
141 new = tail | (val & _Q_LOCKED_MASK); 217 new = tail | (val & _Q_LOCKED_PENDING_MASK);
142 218
143 old = atomic_cmpxchg(&lock->val, val, new); 219 old = atomic_cmpxchg(&lock->val, val, new);
144 if (old == val) 220 if (old == val)
@@ -157,7 +233,7 @@ void queued_spin_lock_slowpath(struct qspinlock *lock, u32 val)
157 * if there was a previous node; link it and wait until reaching the 233 * if there was a previous node; link it and wait until reaching the
158 * head of the waitqueue. 234 * head of the waitqueue.
159 */ 235 */
160 if (old & ~_Q_LOCKED_MASK) { 236 if (old & ~_Q_LOCKED_PENDING_MASK) {
161 prev = decode_tail(old); 237 prev = decode_tail(old);
162 WRITE_ONCE(prev->next, node); 238 WRITE_ONCE(prev->next, node);
163 239
@@ -165,18 +241,19 @@ void queued_spin_lock_slowpath(struct qspinlock *lock, u32 val)
165 } 241 }
166 242
167 /* 243 /*
168 * we're at the head of the waitqueue, wait for the owner to go away. 244 * we're at the head of the waitqueue, wait for the owner & pending to
245 * go away.
169 * 246 *
170 * *,x -> *,0 247 * *,x,y -> *,0,0
171 */ 248 */
172 while ((val = atomic_read(&lock->val)) & _Q_LOCKED_MASK) 249 while ((val = atomic_read(&lock->val)) & _Q_LOCKED_PENDING_MASK)
173 cpu_relax(); 250 cpu_relax();
174 251
175 /* 252 /*
176 * claim the lock: 253 * claim the lock:
177 * 254 *
178 * n,0 -> 0,1 : lock, uncontended 255 * n,0,0 -> 0,0,1 : lock, uncontended
179 * *,0 -> *,1 : lock, contended 256 * *,0,0 -> *,0,1 : lock, contended
180 */ 257 */
181 for (;;) { 258 for (;;) {
182 new = _Q_LOCKED_VAL; 259 new = _Q_LOCKED_VAL;