lock-tracing-20020108
[openafs.git] / src / afs / lock.h
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 #ifndef __AFSLOCK_INCLUDE__
11 #define __AFSLOCK_INCLUDE__         1
12
13 /*
14  * (C) COPYRIGHT IBM CORPORATION 1987
15  * LICENSED MATERIALS - PROPERTY OF IBM
16  */
17
18 /* The following macros allow multi statement macros to be defined safely, i.e.
19    - the multi statement macro can be the object of an if statement;
20    - the call to the multi statement macro may be legally followed by a semi-colon.
21    BEGINMAC and ENDMAC have been tested with both the portable C compiler and
22    Hi-C.  Both compilers were from the Palo Alto 4.2BSD software releases, and
23    both optimized out the constant loop code.  For an example of the use
24    of BEGINMAC and ENDMAC, see the definition for ReleaseWriteLock, below.
25    An alternative to this, using "if(1)" for BEGINMAC is not used because it
26    may generate worse code with pcc, and may generate warning messages with hi-C.
27 */
28
29 #if     (defined(AFS_SUN5_ENV)) || defined(AFS_OSF_ENV)
30 #define AFS_NOBOZO_LOCK
31 #endif
32
33 #if !defined(AFS_OSF20_ENV) || defined(AFS_OSF30_ENV) || defined(AFS_OSF32_ENV)
34     /* We do not instrument locks on osf20 because the vcache structure
35     ** exceeds the maximim possible limit for a vnode.
36     */
37 #define INSTRUMENT_LOCKS
38 /* This is the max lock number in use. Please update it if you add any new
39  * lock numbers.
40  */
41 #define MAX_LOCK_NUMBER 700
42 #endif 
43
44 struct afs_bozoLock {
45     short count;    /* count of excl locks */
46     char flags;     /* bit 1: is anyone waiting? */
47     char spare;     /* for later */
48     char *proc;     /* process holding the lock, really a struct proc * */
49 };
50 #ifndef AFS_NOBOZO_LOCK
51 typedef struct afs_bozoLock afs_bozoLock_t;
52 #else
53 #ifdef  AFS_SUN5_ENV
54 typedef kmutex_t afs_bozoLock_t;
55 #else
56 typedef struct afs_bozoLock afs_bozoLock_t;
57 #endif
58 #define afs_BozonLock(lock, avc)
59 #define afs_BozonUnlock(lock, avc)
60 #define afs_BozonInit(lock, nm)
61 #define afs_CheckBozonLock(lock)                0
62 #define afs_CheckBozonLockBlocking(lock)        0
63 #endif
64
65 #define AFS_BOZONWAITING    1       /* someone is waiting for this lock */
66
67 #undef MObtainWriteLock         /* Defined also in ../rx/rx_machdep.h" */
68 #undef MReleaseWriteLock
69 #ifndef RXObtainWriteLock
70 #define RXObtainWriteLock(lock) ObtainWriteLock(lock)
71 #define RXReleaseWriteLock(lock) ReleaseWriteLock(lock)
72 #endif
73 #define MObtainReadLock(lock)   ObtainReadLock(lock)
74 #define MObtainWriteLock(lock,src)      ObtainWriteLock(lock,src)
75 #define MObtainSharedLock(lock,src)     ObtainSharedLock(lock,src)
76 #define MUpgradeSToWLock(lock,src)      UpgradeSToWLock(lock,src)
77 #define MConvertWToSLock(lock)  ConvertWToSLock(lock)
78 #define MReleaseReadLock(lock)  ReleaseReadLock(lock)
79 #define MReleaseWriteLock(lock) ReleaseWriteLock(lock)
80 #define MReleaseSharedLock(lock) ReleaseSharedLock(lock)
81
82 #define RWLOCK_INIT(lock, nm)   Lock_Init(lock)
83 #undef  LOCK_INIT
84 #define LOCK_INIT(lock, nm)     Lock_Init(lock)
85
86 #define BEGINMAC do {
87 #define ENDMAC   } while (0)
88
89 #if defined(AFS_SUN5_ENV) || defined(AFS_OBSD_ENV)
90 #define MyPidxx (curproc->p_pid)
91 #else
92 #if defined(AFS_AIX41_ENV)
93 extern tid_t thread_self();
94 #define MyPidxx thread_self()
95 #else /* AFS_AIX41_ENV */
96 #if defined(AFS_HPUX101_ENV)
97 #define MyPidxx ((int)p_pid(u.u_procp))
98 #else
99 #if defined(AFS_SGI64_ENV)
100 #if defined(AFS_SGI65_ENV)
101 #define MyPidxx proc_pid(curproc())
102 #else
103 #define MyPidxx current_pid()
104 #endif
105 #else /* AFS_SGI64_ENV */
106 #ifdef AFS_LINUX20_ENV
107 #define MyPidxx current->pid
108 #else
109 #if defined(AFS_DARWIN_ENV) || defined(AFS_FBSD_ENV)
110 #define MyPidxx (current_proc()->p_pid )
111 #else
112 #define MyPidxx (u.u_procp->p_pid )
113 #endif /* AFS_DARWIN_ENV */
114 #endif /* AFS_LINUX20_ENV */
115 #endif /* AFS_SGI64_ENV */
116 #endif /* AFS_HPUX101_ENV */
117 #endif /* AFS_AIX41_ENV */
118 #endif 
119
120 /* all locks wait on excl_locked except for READ_LOCK, which waits on readers_reading */
121 struct afs_lock {
122     unsigned char       wait_states;    /* type of lockers waiting */
123     unsigned char       excl_locked;    /* anyone have boosted, shared or write lock? */
124     unsigned short      readers_reading;/* # readers actually with read locks */
125     unsigned short      num_waiting;    /* probably need this soon */
126     unsigned short      spare;          /* not used now */
127     osi_timeval_t       time_waiting;   /* for statistics gathering */
128 #if defined(INSTRUMENT_LOCKS)
129     /* the following are useful for debugging 
130     ** the field 'src_indicator' is updated only by ObtainLock() and
131     ** only for writes/shared  locks. Hence, it indictes where in the
132     ** source code the shared/write lock was set.
133     */
134     unsigned int        pid_last_reader;/* proceess id of last reader */
135     unsigned int        pid_writer;     /* process id of writer, else 0 */
136     unsigned int        src_indicator;  /* third param to ObtainLock()*/
137 #endif /* INSTRUMENT_LOCKS */
138 };
139 typedef struct afs_lock afs_lock_t;
140 typedef struct afs_lock afs_rwlock_t;
141
142 #define READ_LOCK       1
143 #define WRITE_LOCK      2
144 #define SHARED_LOCK     4
145 /* this next is not a flag, but rather a parameter to Afs_Lock_Obtain */
146 #define BOOSTED_LOCK 6
147
148 /* next defines wait_states for which we wait on excl_locked */
149 #define EXCL_LOCKS (WRITE_LOCK|SHARED_LOCK)
150
151 #ifdef KERNEL
152 #include "icl.h"
153
154 extern int afs_trclock;
155
156 #define AFS_LOCK_TRACE_ENABLE 0
157 #if AFS_LOCK_TRACE_ENABLE
158 #define AFS_LOCK_TRACE(op, lock, type) \
159         if (afs_trclock) Afs_Lock_Trace(op, lock, type, __FILE__, __LINE__);
160 #else
161 #define AFS_LOCK_TRACE(op, lock, type)
162 #endif
163
164 #if defined(INSTRUMENT_LOCKS)
165
166 #define ObtainReadLock(lock)\
167   BEGINMAC  \
168         AFS_LOCK_TRACE(CM_TRACE_LOCKOBTAIN, lock, READ_LOCK);\
169         if (!((lock)->excl_locked & WRITE_LOCK)) \
170             ((lock)->readers_reading)++; \
171         else \
172             Afs_Lock_Obtain(lock, READ_LOCK); \
173         (lock)->pid_last_reader = MyPidxx; \
174    ENDMAC
175
176 #define ObtainWriteLock(lock, src)\
177   BEGINMAC  \
178         AFS_LOCK_TRACE(CM_TRACE_LOCKOBTAIN, lock, WRITE_LOCK);\
179         if (!(lock)->excl_locked && !(lock)->readers_reading)\
180             (lock) -> excl_locked = WRITE_LOCK;\
181         else\
182             Afs_Lock_Obtain(lock, WRITE_LOCK); \
183         (lock)->pid_writer = MyPidxx; \
184         (lock)->src_indicator = src;\
185    ENDMAC
186
187 #define NBObtainWriteLock(lock, src) (((lock)->excl_locked || (lock)->readers_reading) ? EWOULDBLOCK : (((lock) -> excl_locked = WRITE_LOCK), ((lock)->pid_writer = MyPidxx), ((lock)->src_indicator = src), 0))
188
189 #define ObtainSharedLock(lock, src)\
190   BEGINMAC  \
191         AFS_LOCK_TRACE(CM_TRACE_LOCKOBTAIN, lock, SHARED_LOCK);\
192         if (!(lock)->excl_locked)\
193             (lock) -> excl_locked = SHARED_LOCK;\
194         else\
195             Afs_Lock_Obtain(lock, SHARED_LOCK); \
196         (lock)->pid_writer = MyPidxx; \
197         (lock)->src_indicator = src;\
198    ENDMAC
199
200 #define NBObtainSharedLock(lock, src) (((lock)->excl_locked) ? EWOULDBLOCK : (((lock) -> excl_locked = SHARED_LOCK), ((lock)->pid_writer = MyPidxx), ((lock)->src_indicator = src), 0))
201
202 #define UpgradeSToWLock(lock, src)\
203   BEGINMAC  \
204         AFS_LOCK_TRACE(CM_TRACE_LOCKOBTAIN, lock, BOOSTED_LOCK);\
205         if (!(lock)->readers_reading)\
206             (lock)->excl_locked = WRITE_LOCK;\
207         else\
208             Afs_Lock_Obtain(lock, BOOSTED_LOCK); \
209         (lock)->pid_writer = MyPidxx; \
210         (lock)->src_indicator = src;\
211    ENDMAC
212
213 /* this must only be called with a WRITE or boosted SHARED lock! */
214 #define ConvertWToSLock(lock)\
215         BEGINMAC\
216         AFS_LOCK_TRACE(CM_TRACE_LOCKDOWN, lock, SHARED_LOCK);\
217             (lock)->excl_locked = SHARED_LOCK; \
218             if((lock)->wait_states) \
219                 Afs_Lock_ReleaseR(lock); \
220         ENDMAC
221
222 #define ConvertWToRLock(lock) \
223         BEGINMAC\
224         AFS_LOCK_TRACE(CM_TRACE_LOCKDOWN, lock, READ_LOCK);\
225             (lock)->excl_locked &= ~(SHARED_LOCK | WRITE_LOCK);\
226             ((lock)->readers_reading)++;\
227             (lock)->pid_last_reader = MyPidxx ; \
228             (lock)->pid_writer = 0;\
229             Afs_Lock_ReleaseR(lock);\
230         ENDMAC
231
232 #define ConvertSToRLock(lock) \
233         BEGINMAC\
234         AFS_LOCK_TRACE(CM_TRACE_LOCKDOWN, lock, READ_LOCK);\
235             (lock)->excl_locked &= ~(SHARED_LOCK | WRITE_LOCK);\
236             ((lock)->readers_reading)++;\
237             (lock)->pid_last_reader = MyPidxx ; \
238             (lock)->pid_writer = 0;\
239             Afs_Lock_ReleaseR(lock);\
240         ENDMAC
241
242 #define ReleaseReadLock(lock)\
243         BEGINMAC\
244         AFS_LOCK_TRACE(CM_TRACE_LOCKDONE, lock, READ_LOCK);\
245             if (!(--((lock)->readers_reading)) && (lock)->wait_states)\
246                 Afs_Lock_ReleaseW(lock) ; \
247         if ( (lock)->pid_last_reader == MyPidxx ) \
248                 (lock)->pid_last_reader =0;\
249         ENDMAC
250
251 #define ReleaseWriteLock(lock)\
252         BEGINMAC\
253         AFS_LOCK_TRACE(CM_TRACE_LOCKDONE, lock, WRITE_LOCK);\
254             (lock)->excl_locked &= ~WRITE_LOCK;\
255             if ((lock)->wait_states) Afs_Lock_ReleaseR(lock);\
256             (lock)->pid_writer=0; \
257         ENDMAC
258
259 /* can be used on shared or boosted (write) locks */
260 #define ReleaseSharedLock(lock)\
261         BEGINMAC\
262         AFS_LOCK_TRACE(CM_TRACE_LOCKDONE, lock, SHARED_LOCK);\
263             (lock)->excl_locked &= ~(SHARED_LOCK | WRITE_LOCK);\
264             if ((lock)->wait_states) Afs_Lock_ReleaseR(lock);\
265             (lock)->pid_writer=0; \
266         ENDMAC
267
268 #else /* INSTRUMENT_LOCKS */
269
270 #define ObtainReadLock(lock)\
271   BEGINMAC  \
272         AFS_LOCK_TRACE(CM_TRACE_LOCKOBTAIN, lock, READ_LOCK);\
273         if (!((lock)->excl_locked & WRITE_LOCK)) \
274             ((lock)->readers_reading)++; \
275         else \
276             Afs_Lock_Obtain(lock, READ_LOCK); \
277    ENDMAC
278
279 #define ObtainWriteLock(lock, src)\
280   BEGINMAC  \
281         AFS_LOCK_TRACE(CM_TRACE_LOCKOBTAIN, lock, WRITE_LOCK);\
282         if (!(lock)->excl_locked && !(lock)->readers_reading)\
283             (lock) -> excl_locked = WRITE_LOCK;\
284         else\
285             Afs_Lock_Obtain(lock, WRITE_LOCK); \
286    ENDMAC
287
288 #define NBObtainWriteLock(lock, src) (((lock)->excl_locked || (lock)->readers_reading) ? EWOULDBLOCK : (((lock) -> excl_locked = WRITE_LOCK),  0))
289
290 #define ObtainSharedLock(lock, src)\
291   BEGINMAC  \
292         AFS_LOCK_TRACE(CM_TRACE_LOCKOBTAIN, lock, SHARED_LOCK);\
293         if (!(lock)->excl_locked)\
294             (lock) -> excl_locked = SHARED_LOCK;\
295         else\
296             Afs_Lock_Obtain(lock, SHARED_LOCK); \
297    ENDMAC
298
299 #define NBObtainSharedLock(lock, src) (((lock)->excl_locked) ? EWOULDBLOCK : (((lock) -> excl_locked = SHARED_LOCK), 0))
300    
301 #define UpgradeSToWLock(lock, src)\
302   BEGINMAC  \
303         AFS_LOCK_TRACE(CM_TRACE_LOCKOBTAIN, lock, BOOSTED_LOCK);\
304         if (!(lock)->readers_reading)\
305             (lock)->excl_locked = WRITE_LOCK;\
306         else\
307             Afs_Lock_Obtain(lock, BOOSTED_LOCK); \
308    ENDMAC
309
310 /* this must only be called with a WRITE or boosted SHARED lock! */
311 #define ConvertWToSLock(lock)\
312         BEGINMAC\
313         AFS_LOCK_TRACE(CM_TRACE_LOCKDOWN, lock, SHARED_LOCK);\
314             (lock)->excl_locked = SHARED_LOCK; \
315             if((lock)->wait_states) \
316                 Afs_Lock_ReleaseR(lock); \
317         ENDMAC
318
319 #define ConvertWToRLock(lock) \
320         BEGINMAC\
321         AFS_LOCK_TRACE(CM_TRACE_LOCKDOWN, lock, READ_LOCK);\
322             (lock)->excl_locked &= ~(SHARED_LOCK | WRITE_LOCK);\
323             ((lock)->readers_reading)++;\
324             Afs_Lock_ReleaseR(lock);\
325         ENDMAC
326
327 #define ConvertSToRLock(lock) \
328         BEGINMAC\
329         AFS_LOCK_TRACE(CM_TRACE_LOCKDOWN, lock, READ_LOCK);\
330             (lock)->excl_locked &= ~(SHARED_LOCK | WRITE_LOCK);\
331             ((lock)->readers_reading)++;\
332             Afs_Lock_ReleaseR(lock);\
333         ENDMAC
334
335 #define ReleaseReadLock(lock)\
336         BEGINMAC\
337         AFS_LOCK_TRACE(CM_TRACE_LOCKDONE, lock, READ_LOCK);\
338             if (!(--((lock)->readers_reading)) && (lock)->wait_states)\
339                 Afs_Lock_ReleaseW(lock) ; \
340         ENDMAC
341
342 #define ReleaseWriteLock(lock)\
343         BEGINMAC\
344         AFS_LOCK_TRACE(CM_TRACE_LOCKDONE, lock, WRITE_LOCK);\
345             (lock)->excl_locked &= ~WRITE_LOCK;\
346             if ((lock)->wait_states) Afs_Lock_ReleaseR(lock);\
347         ENDMAC
348
349 /* can be used on shared or boosted (write) locks */
350 #define ReleaseSharedLock(lock)\
351         BEGINMAC\
352         AFS_LOCK_TRACE(CM_TRACE_LOCKDONE, lock, SHARED_LOCK);\
353             (lock)->excl_locked &= ~(SHARED_LOCK | WRITE_LOCK);\
354             if ((lock)->wait_states) Afs_Lock_ReleaseR(lock);\
355         ENDMAC
356
357 #endif /* INSTRUMENT_LOCKS */
358
359 /* I added this next macro to make sure it is safe to nuke a lock -- Mike K. */
360 #define LockWaiters(lock)\
361         ((int) ((lock)->num_waiting))
362
363 #define CheckLock(lock)\
364         ((lock)->excl_locked? (int) -1 : (int) (lock)->readers_reading)
365
366 #define WriteLocked(lock)\
367         ((lock)->excl_locked & WRITE_LOCK)
368 #endif
369
370 /*
371
372 You can also use the lock package for handling parent locks for independently-lockable sets of
373 small objects.  The concept here is that the parent lock is at the same level in the
374 locking hierarchy as the little locks, but certain restrictions apply.
375
376 The general usage pattern is as follows.  You have a set of entries to search.  When searching it, you
377 have a "scan" lock on the table.  If you find what you're looking for, you drop the lock down
378 to a "hold" lock, lock the entry, and release the parent lock.  If you don't find what
379 you're looking for, you create the entry, downgrade the "scan" lock to a "hold" lock,
380 lock the entry and unlock the parent.
381
382 To delete an item from the table, you initially obtain a "purge" lock on the parent.  Unlike all
383 of the other parent lock modes described herein, in order to obtain a "purge" lock mode, you
384 must have released all locks on any items in the table.  Once you have obtained the parent
385 lock in "purge" mode, you should check to see if the entry is locked.  If its not locked, you
386 are free to delete the entry, knowing that no one else can attempt to obtain a lock
387 on the entry while you have the purge lock held on the parent.  Unfortunately, if it *is* locked,
388 you can not lock it yourself and wait for the other dude to release it, since the entry's locker
389 may need to lock another entry before unlocking the entry you want (which would result in
390 deadlock).  Instead, then, you must release the parent lock, and try again "later" (see Lock_Wait
391 for assistance in waiting until later). Unfortunately, this is the best locking paradigm I've yet
392 come up with.
393
394 What are the advantages to this scheme?  First, the use of the parent lock ensures that
395 two people don't try to add the same entry at the same time or delete an entry while someone
396 else is adding it.  It also ensures that when one process is deleting an entry, no one else is
397 preparing to lock the entry.  Furthermore, when obtaining a lock on a little entry, you
398 are only holding a "hold" lock on the parent lock, so that others may come in and search
399 the table during this time.  Thus it will not hold up the system if a little entry takes
400 a great deal of time to free up.
401
402 Here's how to compute the compatibility matrix:
403
404 The invariants are:
405
406 add     no deletions, additions allowed, additions will be performed, will obtain little locks
407 hold    no deletions, additions allowed, no additions will be performed, will obtain little locks
408 purge   no deletions or additions allowed, deletions will be performed, don't obtain little locks
409
410 When we compute the locking matrix, we note that hold is compatible with hold and add.
411 Add is compatible only with hold.  purge is not compatible with anything.  This is the same
412 matrix as obtained by mapping add->S, hold->read and purge->write locks.  Thus we
413 can use the locks above to solve this problem, and we do.
414
415 */
416
417 #endif /* __AFSLOCK_INCLUDE__ */