3b3608c2aa89e7ff5b2e5238f3af5e7cb297a05d
[openafs.git] / src / afs / afs_lock.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 /*******************************************************************\
11 *                                                                   *
12 *       Information Technology Center                               *
13 *       Carnegie-Mellon University                                  *
14 *                                                                   *
15 *                                                                   *
16 *                                                                   *
17 \*******************************************************************/
18
19
20 /*
21         Locking routines for Vice.
22
23 */
24
25 #include <afsconfig.h>
26 #include "afs/param.h"
27
28
29 #include "afs/sysincludes.h"    /* Standard vendor system headers */
30 #include "afsincludes.h"        /* Afs-based standard headers */
31 #include "afs/afs_stats.h"      /* afs statistics */
32
33 /* probably needed if lock_trace is enabled - should ifdef */
34 int afs_trclock = 0;
35
36 void Lock_ReleaseR(struct afs_lock *lock);
37 void Lock_ReleaseW(struct afs_lock *lock);
38
39 void
40 Lock_Init(register struct afs_lock *lock)
41 {
42
43     AFS_STATCNT(Lock_Init);
44     lock->readers_reading = 0;
45     lock->excl_locked = 0;
46     lock->wait_states = 0;
47     lock->num_waiting = 0;
48 #if defined(INSTRUMENT_LOCKS)
49     lock->pid_last_reader = 0;
50     lock->pid_writer = 0;
51     lock->src_indicator = 0;
52 #endif /* INSTRUMENT_LOCKS */
53     lock->time_waiting.tv_sec = 0;
54     lock->time_waiting.tv_usec = 0;
55 }
56 \f
57 void
58 ObtainLock(register struct afs_lock *lock, int how,
59            unsigned int src_indicator)
60 {
61     switch (how) {
62     case READ_LOCK:
63         if (!((lock)->excl_locked & WRITE_LOCK))
64             (lock)->readers_reading++;
65         else
66             Afs_Lock_Obtain(lock, READ_LOCK);
67 #if defined(INSTRUMENT_LOCKS)
68         (lock)->pid_last_reader = MyPidxx;
69 #endif /* INSTRUMENT_LOCKS */
70         break;
71     case WRITE_LOCK:
72         if (!(lock)->excl_locked && !(lock)->readers_reading)
73             (lock)->excl_locked = WRITE_LOCK;
74         else
75             Afs_Lock_Obtain(lock, WRITE_LOCK);
76 #if defined(INSTRUMENT_LOCKS)
77         (lock)->pid_writer = MyPidxx;
78         (lock)->src_indicator = src_indicator;
79 #endif /* INSTRUMENT_LOCKS */
80         break;
81     case SHARED_LOCK:
82         if (!(lock)->excl_locked)
83             (lock)->excl_locked = SHARED_LOCK;
84         else
85             Afs_Lock_Obtain(lock, SHARED_LOCK);
86 #if defined(INSTRUMENT_LOCKS)
87         (lock)->pid_writer = MyPidxx;
88         (lock)->src_indicator = src_indicator;
89 #endif /* INSTRUMENT_LOCKS */
90         break;
91     }
92 }
93
94 void
95 ReleaseLock(register struct afs_lock *lock, int how)
96 {
97     if (how == READ_LOCK) {
98         if (!--lock->readers_reading && lock->wait_states) {
99 #if defined(INSTRUMENT_LOCKS)
100             if (lock->pid_last_reader == MyPidxx)
101                 lock->pid_last_reader = 0;
102 #endif /* INSTRUMENT_LOCKS */
103             Afs_Lock_ReleaseW(lock);
104         }
105     } else if (how == WRITE_LOCK) {
106         lock->excl_locked &= ~WRITE_LOCK;
107 #if defined(INSTRUMENT_LOCKS)
108         lock->pid_writer = 0;
109 #endif /* INSTRUMENT_LOCKS */
110         if (lock->wait_states)
111             Afs_Lock_ReleaseR(lock);
112     } else if (how == SHARED_LOCK) {
113         lock->excl_locked &= ~(SHARED_LOCK | WRITE_LOCK);
114 #if defined(INSTRUMENT_LOCKS)
115         lock->pid_writer = 0;
116 #endif /* INSTRUMENT_LOCKS */
117         if (lock->wait_states)
118             Afs_Lock_ReleaseR(lock);
119     }
120 }
121
122 void
123 Afs_Lock_Obtain(register struct afs_lock *lock, int how)
124 {
125     osi_timeval_t tt1, tt2, et;
126     afs_uint32 us;
127
128     AFS_STATCNT(Lock_Obtain);
129
130     AFS_ASSERT_GLOCK();
131     osi_GetuTime(&tt1);
132
133     switch (how) {
134
135     case READ_LOCK:
136         lock->num_waiting++;
137         do {
138             lock->wait_states |= READ_LOCK;
139             afs_osi_Sleep(&lock->readers_reading);
140         } while (lock->excl_locked & WRITE_LOCK);
141         lock->num_waiting--;
142         lock->readers_reading++;
143         break;
144
145     case WRITE_LOCK:
146         lock->num_waiting++;
147         do {
148             lock->wait_states |= WRITE_LOCK;
149             afs_osi_Sleep(&lock->excl_locked);
150         } while (lock->excl_locked || lock->readers_reading);
151         lock->num_waiting--;
152         lock->excl_locked = WRITE_LOCK;
153         break;
154
155     case SHARED_LOCK:
156         lock->num_waiting++;
157         do {
158             lock->wait_states |= SHARED_LOCK;
159             afs_osi_Sleep(&lock->excl_locked);
160         } while (lock->excl_locked);
161         lock->num_waiting--;
162         lock->excl_locked = SHARED_LOCK;
163         break;
164
165     case BOOSTED_LOCK:
166         lock->num_waiting++;
167         do {
168             lock->wait_states |= WRITE_LOCK;
169             afs_osi_Sleep(&lock->excl_locked);
170         } while (lock->readers_reading);
171         lock->num_waiting--;
172         lock->excl_locked = WRITE_LOCK;
173         break;
174
175     default:
176         osi_Panic("afs locktype");
177     }
178
179     osi_GetuTime(&tt2);
180     afs_stats_GetDiff(et, tt1, tt2);
181     afs_stats_AddTo((lock->time_waiting), et);
182     us = (et.tv_sec << 20) + et.tv_usec;
183
184     if (afs_trclock) {
185         afs_Trace3(afs_iclSetp, CM_TRACE_LOCKSLEPT, ICL_TYPE_INT32, us,
186                    ICL_TYPE_POINTER, lock, ICL_TYPE_INT32, how);
187     }
188 }
189
190 /* release a lock, giving preference to new readers */
191 void
192 Afs_Lock_ReleaseR(register struct afs_lock *lock)
193 {
194     AFS_STATCNT(Lock_ReleaseR);
195     AFS_ASSERT_GLOCK();
196     if (lock->wait_states & READ_LOCK) {
197         lock->wait_states &= ~READ_LOCK;
198         afs_osi_Wakeup(&lock->readers_reading);
199     } else {
200         lock->wait_states &= ~EXCL_LOCKS;
201         afs_osi_Wakeup(&lock->excl_locked);
202     }
203 }
204
205 /* release a lock, giving preference to new writers */
206 void
207 Afs_Lock_ReleaseW(register struct afs_lock *lock)
208 {
209     AFS_STATCNT(Lock_ReleaseW);
210     AFS_ASSERT_GLOCK();
211     if (lock->wait_states & EXCL_LOCKS) {
212         lock->wait_states &= ~EXCL_LOCKS;
213         afs_osi_Wakeup(&lock->excl_locked);
214     } else {
215         lock->wait_states &= ~READ_LOCK;
216         afs_osi_Wakeup(&lock->readers_reading);
217     }
218 }
219
220 /*
221 Wait for some change in the lock status.
222 void Lock_Wait(register struct afs_lock *lock)
223 {
224     AFS_STATCNT(Lock_Wait);
225     if (lock->readers_reading || lock->excl_locked) return 1;
226     lock->wait_states |= READ_LOCK;
227     afs_osi_Sleep(&lock->readers_reading);
228     return 0;
229 }
230 */
231
232 /* These next guys exist to provide an interface to drop a lock atomically with
233  * blocking.  They're trivial to do in a non-preemptive LWP environment.
234  */
235
236 /* release a write lock and sleep on an address, atomically */
237 void
238 afs_osi_SleepR(register char *addr, register struct afs_lock *alock)
239 {
240     AFS_STATCNT(osi_SleepR);
241     ReleaseReadLock(alock);
242     afs_osi_Sleep(addr);
243 }
244
245 /* release a write lock and sleep on an address, atomically */
246 void
247 afs_osi_SleepW(register char *addr, register struct afs_lock *alock)
248 {
249     AFS_STATCNT(osi_SleepW);
250     ReleaseWriteLock(alock);
251     afs_osi_Sleep(addr);
252 }
253
254 /* release a write lock and sleep on an address, atomically */
255 void
256 afs_osi_SleepS(register char *addr, register struct afs_lock *alock)
257 {
258     AFS_STATCNT(osi_SleepS);
259     ReleaseSharedLock(alock);
260     afs_osi_Sleep(addr);
261 }
262
263
264 #ifndef AFS_NOBOZO_LOCK
265 /* operations on locks that don't mind if we lock the same thing twice.  I'd like to dedicate
266     this function to Sun Microsystems' Version 4.0 virtual memory system, without
267     which this wouldn't have been necessary */
268 void
269 afs_BozonLock(struct afs_bozoLock *alock, struct vcache *avc)
270 {
271     AFS_STATCNT(afs_BozonLock);
272     while (1) {
273         if (alock->count == 0) {
274             /* lock not held, we win */
275             alock->proc = afs_int_to_pointer(MyPidxx2Pid(MyPidxx));
276             alock->count = 1;
277             return;
278         } else if (alock->proc == afs_int_to_pointer(MyPidxx2Pid(MyPidxx))) {
279             /* lock is held, but by us, so we win anyway */
280             alock->count++;
281             return;
282         } else {
283             /* lock is held, and not by us; we wait */
284             alock->flags |= AFS_BOZONWAITING;
285             afs_osi_Sleep(alock);
286         }
287     }
288 }
289
290 /* releasing the same type of lock as defined above */
291 void
292 afs_BozonUnlock(struct afs_bozoLock *alock, struct vcache *avc)
293 {
294     AFS_STATCNT(afs_BozonUnlock);
295     if (alock->count <= 0)
296         osi_Panic("BozoUnlock");
297     if ((--alock->count) == 0) {
298         if (alock->flags & AFS_BOZONWAITING) {
299             alock->flags &= ~AFS_BOZONWAITING;
300             afs_osi_Wakeup(alock);
301         }
302     }
303 }
304
305 void
306 afs_BozonInit(struct afs_bozoLock *alock, struct vcache *avc)
307 {
308     AFS_STATCNT(afs_BozonInit);
309     alock->count = 0;
310     alock->flags = 0;
311     alock->proc = NULL;
312 }
313
314 int
315 afs_CheckBozonLock(struct afs_bozoLock *alock)
316 {
317     AFS_STATCNT(afs_CheckBozonLock);
318     if (alock->count || (alock->flags & AFS_BOZONWAITING))
319         return 1;
320     return 0;
321 }
322
323 int
324 afs_CheckBozonLockBlocking(struct afs_bozoLock *alock)
325 {
326     AFS_STATCNT(afs_CheckBozonLockBlocking);
327     if (alock->count || (alock->flags & AFS_BOZONWAITING))
328         if (alock->proc != afs_int_to_pointer(MyPidxx2Pid(MyPidxx)))
329             return 1;
330     return 0;
331 }
332 #endif
333
334 /* Not static - used conditionally if lock tracing is enabled */
335 int
336 Afs_Lock_Trace(int op, struct afs_lock *alock, int type, char *file, int line)
337 {
338     int traceok;
339     struct afs_icl_log *tlp;
340     struct afs_icl_set *tsp;
341
342     if (!afs_trclock)
343         return 1;
344     if ((alock) == &afs_icl_lock)
345         return 1;
346
347     ObtainReadLock(&afs_icl_lock);
348     traceok = 1;
349     for (tlp = afs_icl_allLogs; tlp; tlp = tlp->nextp)
350         if ((alock) == &tlp->lock)
351             traceok = 0;
352     for (tsp = afs_icl_allSets; tsp; tsp = tsp->nextp)
353         if ((alock) == &tsp->lock)
354             traceok = 0;
355     ReleaseReadLock(&afs_icl_lock);
356     if (!traceok)
357         return 1;
358
359     afs_Trace4(afs_iclSetp, op, ICL_TYPE_STRING, (long)file, ICL_TYPE_INT32,
360                (long)line, ICL_TYPE_POINTER, (long)alock, ICL_TYPE_LONG,
361                (long)type);
362     return 0;
363 }