2 * Debugging versions of SMP locking primitives.
4 * Copyright (C) 2004 Thibaut VARENE <varenet@parisc-linux.org>
6 * Some code stollen from alpha & sparc64 ;)
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 * We use pdc_printf() throughout the file for all output messages, to avoid
23 * losing messages because of disabled interrupts. Since we're using these
24 * messages for debugging purposes, it makes sense not to send them to the
29 #include <linux/config.h>
30 #include <linux/kernel.h>
31 #include <linux/sched.h>
32 #include <linux/spinlock.h>
33 #include <linux/hardirq.h> /* in_interrupt() */
34 #include <asm/system.h>
35 #include <asm/hardirq.h> /* in_interrupt() */
39 #define INIT_STUCK 1L << 30
41 #ifdef CONFIG_DEBUG_SPINLOCK
44 void _dbg_spin_lock(spinlock_t
* lock
, const char *base_file
, int line_no
)
46 volatile unsigned int *a
;
47 long stuck
= INIT_STUCK
;
48 void *inline_pc
= __builtin_return_address(0);
49 unsigned long started
= jiffies
;
51 int cpu
= smp_processor_id();
55 /* Do the actual locking */
56 /* <T-Bone> ggg: we can't get stuck on the outter loop?
57 * <ggg> T-Bone: We can hit the outer loop
58 * alot if multiple CPUs are constantly racing for a lock
59 * and the backplane is NOT fair about which CPU sees
60 * the update first. But it won't hang since every failed
61 * attempt will drop us back into the inner loop and
63 * <ggg> K-class and some of the others are NOT fair in the HW
64 * implementation so we could see false positives.
65 * But fixing the lock contention is easier than
66 * fixing the HW to be fair.
67 * <tausq> __ldcw() returns 1 if we get the lock; otherwise we
68 * spin until the value of the lock changes, or we time out.
71 a
= __ldcw_align(lock
);
72 while (stuck
&& (__ldcw(a
) == 0))
73 while ((*a
== 0) && --stuck
);
76 if (unlikely(stuck
<= 0)) {
78 "%s:%d: spin_lock(%s/%p) stuck in %s at %p(%d)"
79 " owned by %s:%d in %s at %p(%d)\n",
80 base_file
, line_no
, lock
->module
, lock
,
81 current
->comm
, inline_pc
, cpu
,
82 lock
->bfile
, lock
->bline
, lock
->task
->comm
,
83 lock
->previous
, lock
->oncpu
);
89 /* Exiting. Got the lock. */
91 lock
->previous
= inline_pc
;
93 lock
->bfile
= (char *)base_file
;
94 lock
->bline
= line_no
;
96 if (unlikely(printed
)) {
98 "%s:%d: spin_lock grabbed in %s at %p(%d) %ld ticks\n",
99 base_file
, line_no
, current
->comm
, inline_pc
,
100 cpu
, jiffies
- started
);
104 void _dbg_spin_unlock(spinlock_t
* lock
, const char *base_file
, int line_no
)
107 volatile unsigned int *a
;
109 a
= __ldcw_align(lock
);
110 if (unlikely((*a
!= 0) && lock
->babble
)) {
113 "%s:%d: spin_unlock(%s:%p) not locked\n",
114 base_file
, line_no
, lock
->module
, lock
);
120 int _dbg_spin_trylock(spinlock_t
* lock
, const char *base_file
, int line_no
)
123 volatile unsigned int *a
;
125 a
= __ldcw_align(lock
);
126 ret
= (__ldcw(a
) != 0);
129 lock
->oncpu
= smp_processor_id();
130 lock
->previous
= __builtin_return_address(0);
131 lock
->task
= current
;
133 lock
->bfile
= (char *)base_file
;
134 lock
->bline
= line_no
;
139 #endif /* CONFIG_DEBUG_SPINLOCK */
141 #ifdef CONFIG_DEBUG_RWLOCK
143 /* Interrupts trouble detailed explanation, thx Grant:
145 * o writer (wants to modify data) attempts to acquire the rwlock
146 * o He gets the write lock.
147 * o Interupts are still enabled, we take an interrupt with the
148 * write still holding the lock.
149 * o interrupt handler tries to acquire the rwlock for read.
150 * o deadlock since the writer can't release it at this point.
152 * In general, any use of spinlocks that competes between "base"
153 * level and interrupt level code will risk deadlock. Interrupts
154 * need to be disabled in the base level routines to avoid it.
155 * Or more precisely, only the IRQ the base level routine
156 * is competing with for the lock. But it's more efficient/faster
157 * to just disable all interrupts on that CPU to guarantee
158 * once it gets the lock it can release it quickly too.
161 void _dbg_write_lock(rwlock_t
*rw
, const char *bfile
, int bline
)
163 void *inline_pc
= __builtin_return_address(0);
164 unsigned long started
= jiffies
;
165 long stuck
= INIT_STUCK
;
167 int cpu
= smp_processor_id();
169 if(unlikely(in_interrupt())) { /* acquiring write lock in interrupt context, bad idea */
170 pdc_printf("write_lock caller: %s:%d, IRQs enabled,\n", bfile
, bline
);
174 /* Note: if interrupts are disabled (which is most likely), the printk
175 will never show on the console. We might need a polling method to flush
176 the dmesg buffer anyhow. */
179 _raw_spin_lock(&rw
->lock
);
181 if(rw
->counter
!= 0) {
182 /* this basically never happens */
183 _raw_spin_unlock(&rw
->lock
);
186 if ((unlikely(stuck
<= 0)) && (rw
->counter
< 0)) {
188 "%s:%d: write_lock stuck on writer"
189 " in %s at %p(%d) %ld ticks\n",
190 bfile
, bline
, current
->comm
, inline_pc
,
191 cpu
, jiffies
- started
);
195 else if (unlikely(stuck
<= 0)) {
197 "%s:%d: write_lock stuck on reader"
198 " in %s at %p(%d) %ld ticks\n",
199 bfile
, bline
, current
->comm
, inline_pc
,
200 cpu
, jiffies
- started
);
205 while(rw
->counter
!= 0);
210 /* got it. now leave without unlocking */
211 rw
->counter
= -1; /* remember we are locked */
213 if (unlikely(printed
)) {
215 "%s:%d: write_lock grabbed in %s at %p(%d) %ld ticks\n",
216 bfile
, bline
, current
->comm
, inline_pc
,
217 cpu
, jiffies
- started
);
221 int _dbg_write_trylock(rwlock_t
*rw
, const char *bfile
, int bline
)
224 void *inline_pc
= __builtin_return_address(0);
225 int cpu
= smp_processor_id();
228 if(unlikely(in_interrupt())) { /* acquiring write lock in interrupt context, bad idea */
229 pdc_printf("write_lock caller: %s:%d, IRQs enabled,\n", bfile
, bline
);
233 /* Note: if interrupts are disabled (which is most likely), the printk
234 will never show on the console. We might need a polling method to flush
235 the dmesg buffer anyhow. */
237 _raw_spin_lock(&rw
->lock
);
239 if(rw
->counter
!= 0) {
240 /* this basically never happens */
241 _raw_spin_unlock(&rw
->lock
);
245 /* got it. now leave without unlocking */
246 rw
->counter
= -1; /* remember we are locked */
248 pdc_printf("%s:%d: try write_lock grabbed in %s at %p(%d)\n",
249 bfile
, bline
, current
->comm
, inline_pc
, cpu
);
254 void _dbg_read_lock(rwlock_t
* rw
, const char *bfile
, int bline
)
257 void *inline_pc
= __builtin_return_address(0);
258 unsigned long started
= jiffies
;
259 int cpu
= smp_processor_id();
263 local_irq_save(flags
);
264 _raw_spin_lock(&rw
->lock
);
269 "%s:%d: read_lock grabbed in %s at %p(%d) %ld ticks\n",
270 bfile
, bline
, current
->comm
, inline_pc
,
271 cpu
, jiffies
- started
);
273 _raw_spin_unlock(&rw
->lock
);
274 local_irq_restore(flags
);
277 #endif /* CONFIG_DEBUG_RWLOCK */