debuglocks.c 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. /*
  2. * Debugging versions of SMP locking primitives.
  3. *
  4. * Copyright (C) 2004 Thibaut VARENE <varenet@parisc-linux.org>
  5. *
  6. * Some code stollen from alpha & sparc64 ;)
  7. *
  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.
  12. *
  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.
  17. *
  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
  21. *
  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
  25. * linux console.
  26. */
  27. #include <linux/config.h>
  28. #include <linux/kernel.h>
  29. #include <linux/sched.h>
  30. #include <linux/spinlock.h>
  31. #include <linux/hardirq.h> /* in_interrupt() */
  32. #include <asm/system.h>
  33. #include <asm/hardirq.h> /* in_interrupt() */
  34. #include <asm/pdc.h>
  35. #undef INIT_STUCK
  36. #define INIT_STUCK 1L << 30
  37. #ifdef CONFIG_DEBUG_SPINLOCK
  38. void _dbg_spin_lock(spinlock_t * lock, const char *base_file, int line_no)
  39. {
  40. volatile unsigned int *a;
  41. long stuck = INIT_STUCK;
  42. void *inline_pc = __builtin_return_address(0);
  43. unsigned long started = jiffies;
  44. int printed = 0;
  45. int cpu = smp_processor_id();
  46. try_again:
  47. /* Do the actual locking */
  48. /* <T-Bone> ggg: we can't get stuck on the outter loop?
  49. * <ggg> T-Bone: We can hit the outer loop
  50. * alot if multiple CPUs are constantly racing for a lock
  51. * and the backplane is NOT fair about which CPU sees
  52. * the update first. But it won't hang since every failed
  53. * attempt will drop us back into the inner loop and
  54. * decrement `stuck'.
  55. * <ggg> K-class and some of the others are NOT fair in the HW
  56. * implementation so we could see false positives.
  57. * But fixing the lock contention is easier than
  58. * fixing the HW to be fair.
  59. * <tausq> __ldcw() returns 1 if we get the lock; otherwise we
  60. * spin until the value of the lock changes, or we time out.
  61. */
  62. mb();
  63. a = __ldcw_align(lock);
  64. while (stuck && (__ldcw(a) == 0))
  65. while ((*a == 0) && --stuck);
  66. mb();
  67. if (unlikely(stuck <= 0)) {
  68. pdc_printf(
  69. "%s:%d: spin_lock(%s/%p) stuck in %s at %p(%d)"
  70. " owned by %s:%d in %s at %p(%d)\n",
  71. base_file, line_no, lock->module, lock,
  72. current->comm, inline_pc, cpu,
  73. lock->bfile, lock->bline, lock->task->comm,
  74. lock->previous, lock->oncpu);
  75. stuck = INIT_STUCK;
  76. printed = 1;
  77. goto try_again;
  78. }
  79. /* Exiting. Got the lock. */
  80. lock->oncpu = cpu;
  81. lock->previous = inline_pc;
  82. lock->task = current;
  83. lock->bfile = (char *)base_file;
  84. lock->bline = line_no;
  85. if (unlikely(printed)) {
  86. pdc_printf(
  87. "%s:%d: spin_lock grabbed in %s at %p(%d) %ld ticks\n",
  88. base_file, line_no, current->comm, inline_pc,
  89. cpu, jiffies - started);
  90. }
  91. }
  92. void _dbg_spin_unlock(spinlock_t * lock, const char *base_file, int line_no)
  93. {
  94. CHECK_LOCK(lock);
  95. volatile unsigned int *a;
  96. mb();
  97. a = __ldcw_align(lock);
  98. if (unlikely((*a != 0) && lock->babble)) {
  99. lock->babble--;
  100. pdc_printf(
  101. "%s:%d: spin_unlock(%s:%p) not locked\n",
  102. base_file, line_no, lock->module, lock);
  103. }
  104. *a = 1;
  105. mb();
  106. }
  107. int _dbg_spin_trylock(spinlock_t * lock, const char *base_file, int line_no)
  108. {
  109. int ret;
  110. volatile unsigned int *a;
  111. mb();
  112. a = __ldcw_align(lock);
  113. ret = (__ldcw(a) != 0);
  114. mb();
  115. if (ret) {
  116. lock->oncpu = smp_processor_id();
  117. lock->previous = __builtin_return_address(0);
  118. lock->task = current;
  119. } else {
  120. lock->bfile = (char *)base_file;
  121. lock->bline = line_no;
  122. }
  123. return ret;
  124. }
  125. #endif /* CONFIG_DEBUG_SPINLOCK */
  126. #ifdef CONFIG_DEBUG_RWLOCK
  127. /* Interrupts trouble detailed explanation, thx Grant:
  128. *
  129. * o writer (wants to modify data) attempts to acquire the rwlock
  130. * o He gets the write lock.
  131. * o Interupts are still enabled, we take an interrupt with the
  132. * write still holding the lock.
  133. * o interrupt handler tries to acquire the rwlock for read.
  134. * o deadlock since the writer can't release it at this point.
  135. *
  136. * In general, any use of spinlocks that competes between "base"
  137. * level and interrupt level code will risk deadlock. Interrupts
  138. * need to be disabled in the base level routines to avoid it.
  139. * Or more precisely, only the IRQ the base level routine
  140. * is competing with for the lock. But it's more efficient/faster
  141. * to just disable all interrupts on that CPU to guarantee
  142. * once it gets the lock it can release it quickly too.
  143. */
  144. void _dbg_write_lock(rwlock_t *rw, const char *bfile, int bline)
  145. {
  146. void *inline_pc = __builtin_return_address(0);
  147. unsigned long started = jiffies;
  148. long stuck = INIT_STUCK;
  149. int printed = 0;
  150. int cpu = smp_processor_id();
  151. if(unlikely(in_interrupt())) { /* acquiring write lock in interrupt context, bad idea */
  152. pdc_printf("write_lock caller: %s:%d, IRQs enabled,\n", bfile, bline);
  153. BUG();
  154. }
  155. /* Note: if interrupts are disabled (which is most likely), the printk
  156. will never show on the console. We might need a polling method to flush
  157. the dmesg buffer anyhow. */
  158. retry:
  159. _raw_spin_lock(&rw->lock);
  160. if(rw->counter != 0) {
  161. /* this basically never happens */
  162. _raw_spin_unlock(&rw->lock);
  163. stuck--;
  164. if ((unlikely(stuck <= 0)) && (rw->counter < 0)) {
  165. pdc_printf(
  166. "%s:%d: write_lock stuck on writer"
  167. " in %s at %p(%d) %ld ticks\n",
  168. bfile, bline, current->comm, inline_pc,
  169. cpu, jiffies - started);
  170. stuck = INIT_STUCK;
  171. printed = 1;
  172. }
  173. else if (unlikely(stuck <= 0)) {
  174. pdc_printf(
  175. "%s:%d: write_lock stuck on reader"
  176. " in %s at %p(%d) %ld ticks\n",
  177. bfile, bline, current->comm, inline_pc,
  178. cpu, jiffies - started);
  179. stuck = INIT_STUCK;
  180. printed = 1;
  181. }
  182. while(rw->counter != 0);
  183. goto retry;
  184. }
  185. /* got it. now leave without unlocking */
  186. rw->counter = -1; /* remember we are locked */
  187. if (unlikely(printed)) {
  188. pdc_printf(
  189. "%s:%d: write_lock grabbed in %s at %p(%d) %ld ticks\n",
  190. bfile, bline, current->comm, inline_pc,
  191. cpu, jiffies - started);
  192. }
  193. }
  194. int _dbg_write_trylock(rwlock_t *rw, const char *bfile, int bline)
  195. {
  196. #if 0
  197. void *inline_pc = __builtin_return_address(0);
  198. int cpu = smp_processor_id();
  199. #endif
  200. if(unlikely(in_interrupt())) { /* acquiring write lock in interrupt context, bad idea */
  201. pdc_printf("write_lock caller: %s:%d, IRQs enabled,\n", bfile, bline);
  202. BUG();
  203. }
  204. /* Note: if interrupts are disabled (which is most likely), the printk
  205. will never show on the console. We might need a polling method to flush
  206. the dmesg buffer anyhow. */
  207. _raw_spin_lock(&rw->lock);
  208. if(rw->counter != 0) {
  209. /* this basically never happens */
  210. _raw_spin_unlock(&rw->lock);
  211. return 0;
  212. }
  213. /* got it. now leave without unlocking */
  214. rw->counter = -1; /* remember we are locked */
  215. #if 0
  216. pdc_printf("%s:%d: try write_lock grabbed in %s at %p(%d)\n",
  217. bfile, bline, current->comm, inline_pc, cpu);
  218. #endif
  219. return 1;
  220. }
  221. void _dbg_read_lock(rwlock_t * rw, const char *bfile, int bline)
  222. {
  223. #if 0
  224. void *inline_pc = __builtin_return_address(0);
  225. unsigned long started = jiffies;
  226. int cpu = smp_processor_id();
  227. #endif
  228. unsigned long flags;
  229. local_irq_save(flags);
  230. _raw_spin_lock(&rw->lock);
  231. rw->counter++;
  232. #if 0
  233. pdc_printf(
  234. "%s:%d: read_lock grabbed in %s at %p(%d) %ld ticks\n",
  235. bfile, bline, current->comm, inline_pc,
  236. cpu, jiffies - started);
  237. #endif
  238. _raw_spin_unlock(&rw->lock);
  239. local_irq_restore(flags);
  240. }
  241. #endif /* CONFIG_DEBUG_RWLOCK */