summaryrefslogtreecommitdiffstats
path: root/arch/sparc/lib/debuglocks.c
blob: 62313e4ae0c8e6f9be7cbcb389aed410f478404e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
/* $Id: debuglocks.c,v 1.7 1999/04/21 02:26:58 anton Exp $
 * debuglocks.c: Debugging versions of SMP locking primitives.
 *
 * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
 * Copyright (C) 1998-99 Anton Blanchard (anton@progsoc.uts.edu.au)
 */

#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/tasks.h>	/* For NR_CPUS */
#include <asm/psr.h>
#include <asm/system.h>
#include <asm/spinlock.h>

/* To enable this code, just define SPIN_LOCK_DEBUG in asm/spinlock.h */
#ifdef SPIN_LOCK_DEBUG

/* Some notes on how these debugging routines work.  When a lock is acquired
 * an extra debugging member lock->owner_pc is set to the caller of the lock
 * acquisition routine.  Right before releasing a lock, the debugging program
 * counter is cleared to zero.
 *
 * Furthermore, since PC's are 4 byte aligned on Sparc, we stuff the CPU
 * number of the owner in the lowest two bits.
 */

#define STORE_CALLER(A) __asm__ __volatile__("mov %%i7, %0" : "=r" (A));

static inline void show(char *str, spinlock_t *lock, unsigned long caller)
{
	int cpu = smp_processor_id();
	extern spinlock_t console_lock;

	if (lock != &console_lock)
		printk("%s(%p) CPU#%d stuck at %08lx, owner PC(%08lx):CPU(%lx)\n",str,
			lock, cpu, caller, lock->owner_pc & ~3, lock->owner_pc & 3);
}

static inline void show_read(char *str, rwlock_t *lock, unsigned long caller)
{
	int cpu = smp_processor_id();

	printk("%s(%p) CPU#%d stuck at %08lx, owner PC(%08lx):CPU(%lx)\n", str,
		lock, cpu, caller, lock->owner_pc & ~3, lock->owner_pc & 3);
}

static inline void show_write(char *str, rwlock_t *lock, unsigned long caller)
{
	int cpu = smp_processor_id();
	int i;

	printk("%s(%p) CPU#%d stuck at %08lx, owner PC(%08lx):CPU(%lx)", str,
		lock, cpu, caller, lock->owner_pc & ~3, lock->owner_pc & 3);

	for(i = 0; i < NR_CPUS; i++)
		printk(" reader[i]=%08lx", lock->reader_pc[i]);

	printk("\n");
}

#undef INIT_STUCK
#define INIT_STUCK 100000000

void _do_spin_lock(spinlock_t *lock, char *str)
{
	unsigned long caller;
	unsigned long val;
	int cpu = smp_processor_id();
	int stuck = INIT_STUCK;

	STORE_CALLER(caller);

again:
	__asm__ __volatile__("ldstub [%1], %0" : "=r" (val) : "r" (&(lock->lock)));
	if(val) {
		while(lock->lock) {
			if (!--stuck) {
				show(str, lock, caller);
				stuck = INIT_STUCK;
			}
			barrier();
		}
		goto again;
	}
	lock->owner_pc = (cpu & 3) | (caller & ~3);
}

int _spin_trylock(spinlock_t *lock)
{
	unsigned long val;
	unsigned long caller;
	int cpu = smp_processor_id();

	STORE_CALLER(caller);

	__asm__ __volatile__("ldstub [%1], %0" : "=r" (val) : "r" (&(lock->lock)));
	if(!val) {
		/* We got it, record our identity for debugging. */
		lock->owner_pc = (cpu & 3) | (caller & ~3);
	}
	return val == 0;
}

void _do_spin_unlock(spinlock_t *lock)
{
	lock->owner_pc = 0;
	barrier();
	lock->lock = 0;
}

void _do_read_lock(rwlock_t *rw, char *str)
{
	unsigned long caller;
	unsigned long val;
	int cpu = smp_processor_id();
	int stuck = INIT_STUCK;

	STORE_CALLER(caller);

wlock_again:
	__asm__ __volatile__("ldstub [%1 + 3], %0" : "=r" (val) : "r" (&(rw->lock)));
	if(val) {
		while(rw->lock & 0xff) {
			if (!--stuck) {
				show_read(str, rw, caller);
				stuck = INIT_STUCK;
			}
			barrier();
		}
		goto wlock_again;
	}

	rw->reader_pc[cpu] = caller;
	barrier();
	rw->lock++;
}

void _do_read_unlock(rwlock_t *rw, char *str)
{
	unsigned long caller;
	unsigned long val;
	int cpu = smp_processor_id();
	int stuck = INIT_STUCK;

	STORE_CALLER(caller);

wlock_again:
	__asm__ __volatile__("ldstub [%1 + 3], %0" : "=r" (val) : "r" (&(rw->lock)));
	if(val) {
		while(rw->lock & 0xff) {
			if (!--stuck) {
				show_read(str, rw, caller);
				stuck = INIT_STUCK;
			}
			barrier();
		}
		goto wlock_again;
	}

	rw->reader_pc[cpu] = 0;
	barrier();
	rw->lock -= 0x1ff;
}

void _do_write_lock(rwlock_t *rw, char *str)
{
	unsigned long caller;
	unsigned long val;
	int cpu = smp_processor_id();
	int stuck = INIT_STUCK;

	STORE_CALLER(caller);

wlock_again:
	__asm__ __volatile__("ldstub [%1 + 3], %0" : "=r" (val) : "r" (&(rw->lock)));
	if(val) {
wlock_wait:
		while(rw->lock) {
			if (!--stuck) {
				show_write(str, rw, caller);
				stuck = INIT_STUCK;
			}
			barrier();
		}
		goto wlock_again;
	}

	if (rw->lock & ~0xff) {
		*(((unsigned char *)&rw->lock)+3) = 0;
		barrier();
		goto wlock_wait;
	}

	barrier();
	rw->owner_pc = (cpu & 3) | (caller & ~3);
}

void _do_write_unlock(rwlock_t *rw)
{
	rw->owner_pc = 0;
	barrier();
	rw->lock = 0;
}

#endif /* SPIN_LOCK_DEBUG */