Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* $Id: debuglocks.c,v 1.11 2001/09/20 00:35:31 davem Exp $ |
| 2 | * debuglocks.c: Debugging versions of SMP locking primitives. |
| 3 | * |
| 4 | * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) |
| 5 | * Copyright (C) 1998-99 Anton Blanchard (anton@progsoc.uts.edu.au) |
| 6 | */ |
| 7 | |
| 8 | #include <linux/kernel.h> |
| 9 | #include <linux/sched.h> |
| 10 | #include <linux/threads.h> /* For NR_CPUS */ |
| 11 | #include <linux/spinlock.h> |
| 12 | #include <asm/psr.h> |
| 13 | #include <asm/system.h> |
| 14 | |
| 15 | #ifdef CONFIG_SMP |
| 16 | |
| 17 | /* Some notes on how these debugging routines work. When a lock is acquired |
| 18 | * an extra debugging member lock->owner_pc is set to the caller of the lock |
| 19 | * acquisition routine. Right before releasing a lock, the debugging program |
| 20 | * counter is cleared to zero. |
| 21 | * |
| 22 | * Furthermore, since PC's are 4 byte aligned on Sparc, we stuff the CPU |
| 23 | * number of the owner in the lowest two bits. |
| 24 | */ |
| 25 | |
| 26 | #define STORE_CALLER(A) __asm__ __volatile__("mov %%i7, %0" : "=r" (A)); |
| 27 | |
| 28 | static inline void show(char *str, spinlock_t *lock, unsigned long caller) |
| 29 | { |
| 30 | int cpu = smp_processor_id(); |
| 31 | |
| 32 | printk("%s(%p) CPU#%d stuck at %08lx, owner PC(%08lx):CPU(%lx)\n",str, |
| 33 | lock, cpu, caller, lock->owner_pc & ~3, lock->owner_pc & 3); |
| 34 | } |
| 35 | |
| 36 | static inline void show_read(char *str, rwlock_t *lock, unsigned long caller) |
| 37 | { |
| 38 | int cpu = smp_processor_id(); |
| 39 | |
| 40 | printk("%s(%p) CPU#%d stuck at %08lx, owner PC(%08lx):CPU(%lx)\n", str, |
| 41 | lock, cpu, caller, lock->owner_pc & ~3, lock->owner_pc & 3); |
| 42 | } |
| 43 | |
| 44 | static inline void show_write(char *str, rwlock_t *lock, unsigned long caller) |
| 45 | { |
| 46 | int cpu = smp_processor_id(); |
| 47 | int i; |
| 48 | |
| 49 | printk("%s(%p) CPU#%d stuck at %08lx, owner PC(%08lx):CPU(%lx)", str, |
| 50 | lock, cpu, caller, lock->owner_pc & ~3, lock->owner_pc & 3); |
| 51 | |
| 52 | for(i = 0; i < NR_CPUS; i++) |
| 53 | printk(" reader[%d]=%08lx", i, lock->reader_pc[i]); |
| 54 | |
| 55 | printk("\n"); |
| 56 | } |
| 57 | |
| 58 | #undef INIT_STUCK |
| 59 | #define INIT_STUCK 100000000 |
| 60 | |
| 61 | void _do_spin_lock(spinlock_t *lock, char *str) |
| 62 | { |
| 63 | unsigned long caller; |
| 64 | unsigned long val; |
| 65 | int cpu = smp_processor_id(); |
| 66 | int stuck = INIT_STUCK; |
| 67 | |
| 68 | STORE_CALLER(caller); |
| 69 | |
| 70 | again: |
| 71 | __asm__ __volatile__("ldstub [%1], %0" : "=r" (val) : "r" (&(lock->lock))); |
| 72 | if(val) { |
| 73 | while(lock->lock) { |
| 74 | if (!--stuck) { |
| 75 | show(str, lock, caller); |
| 76 | stuck = INIT_STUCK; |
| 77 | } |
| 78 | barrier(); |
| 79 | } |
| 80 | goto again; |
| 81 | } |
| 82 | lock->owner_pc = (cpu & 3) | (caller & ~3); |
| 83 | } |
| 84 | |
| 85 | int _spin_trylock(spinlock_t *lock) |
| 86 | { |
| 87 | unsigned long val; |
| 88 | unsigned long caller; |
| 89 | int cpu = smp_processor_id(); |
| 90 | |
| 91 | STORE_CALLER(caller); |
| 92 | |
| 93 | __asm__ __volatile__("ldstub [%1], %0" : "=r" (val) : "r" (&(lock->lock))); |
| 94 | if(!val) { |
| 95 | /* We got it, record our identity for debugging. */ |
| 96 | lock->owner_pc = (cpu & 3) | (caller & ~3); |
| 97 | } |
| 98 | return val == 0; |
| 99 | } |
| 100 | |
| 101 | void _do_spin_unlock(spinlock_t *lock) |
| 102 | { |
| 103 | lock->owner_pc = 0; |
| 104 | barrier(); |
| 105 | lock->lock = 0; |
| 106 | } |
| 107 | |
| 108 | void _do_read_lock(rwlock_t *rw, char *str) |
| 109 | { |
| 110 | unsigned long caller; |
| 111 | unsigned long val; |
| 112 | int cpu = smp_processor_id(); |
| 113 | int stuck = INIT_STUCK; |
| 114 | |
| 115 | STORE_CALLER(caller); |
| 116 | |
| 117 | wlock_again: |
| 118 | __asm__ __volatile__("ldstub [%1 + 3], %0" : "=r" (val) : "r" (&(rw->lock))); |
| 119 | if(val) { |
| 120 | while(rw->lock & 0xff) { |
| 121 | if (!--stuck) { |
| 122 | show_read(str, rw, caller); |
| 123 | stuck = INIT_STUCK; |
| 124 | } |
| 125 | barrier(); |
| 126 | } |
| 127 | goto wlock_again; |
| 128 | } |
| 129 | |
| 130 | rw->reader_pc[cpu] = caller; |
| 131 | barrier(); |
| 132 | rw->lock++; |
| 133 | } |
| 134 | |
| 135 | void _do_read_unlock(rwlock_t *rw, char *str) |
| 136 | { |
| 137 | unsigned long caller; |
| 138 | unsigned long val; |
| 139 | int cpu = smp_processor_id(); |
| 140 | int stuck = INIT_STUCK; |
| 141 | |
| 142 | STORE_CALLER(caller); |
| 143 | |
| 144 | wlock_again: |
| 145 | __asm__ __volatile__("ldstub [%1 + 3], %0" : "=r" (val) : "r" (&(rw->lock))); |
| 146 | if(val) { |
| 147 | while(rw->lock & 0xff) { |
| 148 | if (!--stuck) { |
| 149 | show_read(str, rw, caller); |
| 150 | stuck = INIT_STUCK; |
| 151 | } |
| 152 | barrier(); |
| 153 | } |
| 154 | goto wlock_again; |
| 155 | } |
| 156 | |
| 157 | rw->reader_pc[cpu] = 0; |
| 158 | barrier(); |
| 159 | rw->lock -= 0x1ff; |
| 160 | } |
| 161 | |
| 162 | void _do_write_lock(rwlock_t *rw, char *str) |
| 163 | { |
| 164 | unsigned long caller; |
| 165 | unsigned long val; |
| 166 | int cpu = smp_processor_id(); |
| 167 | int stuck = INIT_STUCK; |
| 168 | |
| 169 | STORE_CALLER(caller); |
| 170 | |
| 171 | wlock_again: |
| 172 | __asm__ __volatile__("ldstub [%1 + 3], %0" : "=r" (val) : "r" (&(rw->lock))); |
| 173 | if(val) { |
| 174 | wlock_wait: |
| 175 | while(rw->lock) { |
| 176 | if (!--stuck) { |
| 177 | show_write(str, rw, caller); |
| 178 | stuck = INIT_STUCK; |
| 179 | } |
| 180 | barrier(); |
| 181 | } |
| 182 | goto wlock_again; |
| 183 | } |
| 184 | |
| 185 | if (rw->lock & ~0xff) { |
| 186 | *(((unsigned char *)&rw->lock)+3) = 0; |
| 187 | barrier(); |
| 188 | goto wlock_wait; |
| 189 | } |
| 190 | |
| 191 | barrier(); |
| 192 | rw->owner_pc = (cpu & 3) | (caller & ~3); |
| 193 | } |
| 194 | |
| 195 | void _do_write_unlock(rwlock_t *rw) |
| 196 | { |
| 197 | rw->owner_pc = 0; |
| 198 | barrier(); |
| 199 | rw->lock = 0; |
| 200 | } |
| 201 | |
| 202 | #endif /* SMP */ |