xref: /plan9-contrib/sys/src/9k/port/taslock.c (revision 5ddf0a198754ce69c27849817f497bfa5af41422)
1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 
7 #include "../port/edf.h"
8 
9 uvlong maxlockcycles;
10 uvlong maxilockcycles;
11 ulong maxlockpc;
12 ulong maxilockpc;
13 
14 struct
15 {
16 	ulong	locks;
17 	ulong	glare;
18 	ulong	inglare;
19 } lockstats;
20 
21 static void
dumplockmem(char * tag,Lock * l)22 dumplockmem(char *tag, Lock *l)
23 {
24 	uchar *cp;
25 	int i;
26 
27 	iprint("%s: ", tag);
28 	cp = (uchar*)l;
29 	for(i = 0; i < 64; i++)
30 		iprint("%2.2ux ", cp[i]);
31 	iprint("\n");
32 }
33 
34 void
lockloop(Lock * l,uintptr pc)35 lockloop(Lock *l, uintptr pc)
36 {
37 	Proc *p;
38 
39 	p = l->p;
40 	print("lock %#p loop key %#ux pc %#p held by pc %#p proc %d\n",
41 		l, l->key, pc, l->pc, p ? p->pid : 0);
42 	dumpaproc(up);
43 	if(p != nil)
44 		dumpaproc(p);
45 }
46 
47 int
lock(Lock * l)48 lock(Lock *l)
49 {
50 	int i;
51 	uintptr pc;
52 
53 	pc = getcallerpc(&l);
54 
55 	lockstats.locks++;
56 	if(up)
57 		ainc(&up->nlocks);	/* prevent being scheded */
58 	if(TAS(&l->key) == 0){
59 		if(up)
60 			up->lastlock = l;
61 		l->pc = pc;
62 		l->p = up;
63 		l->isilock = 0;
64 		l->m = m;
65 #ifdef LOCKCYCLES
66 		cycles(&l->lockcycles);
67 #endif
68 		return 0;
69 	}
70 	if(up)
71 		adec(&up->nlocks);
72 
73 	lockstats.glare++;
74 	for(;;){
75 		lockstats.inglare++;
76 		i = 0;
77 		while(l->key){
78 			if(sys->nonline < 2 && up && up->edf && (up->edf->flags & Admitted)){
79 				/*
80 				 * Priority inversion, yield on a uniprocessor; on a
81 				 * multiprocessor, the other processor will unlock
82 				 */
83 				print("inversion %#p pc %#p proc %d held by pc %#p proc %d\n",
84 					l, pc, up ? up->pid : 0, l->pc, l->p ? l->p->pid : 0);
85 				up->edf->d = todget(nil);	/* yield to process with lock */
86 			}
87 			if(i++ > 100000000){
88 				i = 0;
89 				lockloop(l, pc);
90 			}
91 		}
92 		if(up)
93 			ainc(&up->nlocks);
94 		if(TAS(&l->key) == 0){
95 			if(up)
96 				up->lastlock = l;
97 			l->pc = pc;
98 			l->p = up;
99 			l->isilock = 0;
100 			l->m = m;
101 #ifdef LOCKCYCLES
102 			cycles(&l->lockcycles);
103 #endif
104 			return 1;
105 		}
106 		if(up)
107 			adec(&up->nlocks);
108 	}
109 }
110 
111 void
ilock(Lock * l)112 ilock(Lock *l)
113 {
114 	Mreg s;
115 	uintptr pc;
116 
117 	pc = getcallerpc(&l);
118 	lockstats.locks++;
119 
120 	s = splhi();
121 	if(TAS(&l->key) != 0){
122 		lockstats.glare++;
123 		/*
124 		 * Cannot also check l->pc, l->m, or l->isilock here
125 		 * because they might just not be set yet, or
126 		 * (for pc and m) the lock might have just been unlocked.
127 		 */
128 		for(;;){
129 			lockstats.inglare++;
130 			splx(s);
131 			while(l->key)
132 				;
133 			s = splhi();
134 			if(TAS(&l->key) == 0)
135 				goto acquire;
136 		}
137 	}
138 acquire:
139 	m->ilockdepth++;
140 	m->ilockpc = pc;
141 	if(up)
142 		up->lastilock = l;
143 	l->sr = s;
144 	l->pc = pc;
145 	l->p = up;
146 	l->isilock = 1;
147 	l->m = m;
148 #ifdef LOCKCYCLES
149 	cycles(&l->lockcycles);
150 #endif
151 }
152 
153 int
canlock(Lock * l)154 canlock(Lock *l)
155 {
156 	if(up)
157 		ainc(&up->nlocks);
158 	if(TAS(&l->key)){
159 		if(up)
160 			adec(&up->nlocks);
161 		return 0;
162 	}
163 
164 	if(up)
165 		up->lastlock = l;
166 	l->pc = getcallerpc(&l);
167 	l->p = up;
168 	l->m = m;
169 	l->isilock = 0;
170 #ifdef LOCKCYCLES
171 	cycles(&l->lockcycles);
172 #endif
173 	return 1;
174 }
175 
176 void
unlock(Lock * l)177 unlock(Lock *l)
178 {
179 #ifdef LOCKCYCLES
180 	uvlong x;
181 	cycles(&x);
182 	l->lockcycles = x - l->lockcycles;
183 	if(l->lockcycles > maxlockcycles){
184 		maxlockcycles = l->lockcycles;
185 		maxlockpc = l->pc;
186 	}
187 #endif
188 
189 	if(l->key == 0)
190 		print("unlock: not locked: pc %#p\n", getcallerpc(&l));
191 	if(l->isilock)
192 		print("unlock of ilock: pc %#p, held by %#p\n", getcallerpc(&l), l->pc);
193 	if(l->p != up)
194 		print("unlock: up changed: pc %#p, acquired at pc %#p, lock p %#p, unlock up %#p\n", getcallerpc(&l), l->pc, l->p, up);
195 	l->m = nil;
196 	l->key = 0;
197 	coherence();
198 
199 	if(up && adec(&up->nlocks) == 0 && up->delaysched && islo()){
200 		/*
201 		 * Call sched if the need arose while locks were held
202 		 * But, don't do it from interrupt routines, hence the islo() test
203 		 */
204 		sched();
205 	}
206 }
207 
208 void
iunlock(Lock * l)209 iunlock(Lock *l)
210 {
211 	Mreg s;
212 
213 #ifdef LOCKCYCLES
214 	uvlong x;
215 	cycles(&x);
216 	l->lockcycles = x - l->lockcycles;
217 	if(l->lockcycles > maxilockcycles){
218 		maxilockcycles = l->lockcycles;
219 		maxilockpc = l->pc;
220 	}
221 #endif
222 
223 	if(l->key == 0)
224 		print("iunlock: not locked: pc %#p\n", getcallerpc(&l));
225 	if(!l->isilock)
226 		print("iunlock of lock: pc %#p, held by %#p\n", getcallerpc(&l), l->pc);
227 	if(islo())
228 		print("iunlock while lo: pc %#p, held by %#p\n", getcallerpc(&l), l->pc);
229 	if(l->m != m){
230 		print("iunlock by cpu%d, locked by cpu%d: pc %#p, held by %#p\n",
231 			m->machno, l->m->machno, getcallerpc(&l), l->pc);
232 	}
233 
234 	s = l->sr;
235 	l->m = nil;
236 	l->key = 0;
237 	coherence();
238 	m->ilockdepth--;
239 	if(up)
240 		up->lastilock = nil;
241 	splx(s);
242 }
243