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