33565093f365081b7a045d1f31f02bf22207f538
[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 572
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 #if defined(INSTRUMENT_LOCKS)
156
157 #define ObtainReadLock(lock)\
158   BEGINMAC  \
159 /*       if (afs_trclock) {icl_Trace2(cm_iclSetp, CM_TRACE_LOCKOBTAIN, ICL_TYPE_POINTER, (long)lock, ICL_TYPE_LONG, (long)READ_LOCK);} */ \
160         if (!((lock)->excl_locked & WRITE_LOCK)) \
161             ((lock)->readers_reading)++; \
162         else \
163             Afs_Lock_Obtain(lock, READ_LOCK); \
164         (lock)->pid_last_reader = MyPidxx; \
165    ENDMAC
166
167 #define ObtainWriteLock(lock, src)\
168   BEGINMAC  \
169 /*       if (afs_trclock) {icl_Trace2(cm_iclSetp, CM_TRACE_LOCKOBTAIN, ICL_TYPE_POINTER, (long)lock, ICL_TYPE_LONG, (long)WRITE_LOCK);} */ \
170         if (!(lock)->excl_locked && !(lock)->readers_reading)\
171             (lock) -> excl_locked = WRITE_LOCK;\
172         else\
173             Afs_Lock_Obtain(lock, WRITE_LOCK); \
174         (lock)->pid_writer = MyPidxx; \
175         (lock)->src_indicator = src;\
176    ENDMAC
177
178 #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))
179
180 #define ObtainSharedLock(lock, src)\
181   BEGINMAC  \
182 /*       if (afs_trclock) {icl_Trace2(cm_iclSetp, CM_TRACE_LOCKOBTAIN, ICL_TYPE_POINTER, (long)lock, ICL_TYPE_LONG, (long)SHARED_LOCK);} */ \
183         if (!(lock)->excl_locked)\
184             (lock) -> excl_locked = SHARED_LOCK;\
185         else\
186             Afs_Lock_Obtain(lock, SHARED_LOCK); \
187         (lock)->pid_writer = MyPidxx; \
188         (lock)->src_indicator = src;\
189    ENDMAC
190
191 #define NBObtainSharedLock(lock, src) (((lock)->excl_locked) ? EWOULDBLOCK : (((lock) -> excl_locked = SHARED_LOCK), ((lock)->pid_writer = MyPidxx), ((lock)->src_indicator = src), 0))
192
193 #define UpgradeSToWLock(lock, src)\
194   BEGINMAC  \
195 /*       if (afs_trclock) {icl_Trace2(cm_iclSetp, CM_TRACE_LOCKOBTAIN, ICL_TYPE_POINTER, (long)lock, ICL_TYPE_LONG, (long)BOOSTED_LOCK);} */ \
196         if (!(lock)->readers_reading)\
197             (lock)->excl_locked = WRITE_LOCK;\
198         else\
199             Afs_Lock_Obtain(lock, BOOSTED_LOCK); \
200         (lock)->pid_writer = MyPidxx; \
201         (lock)->src_indicator = src;\
202    ENDMAC
203
204 /* this must only be called with a WRITE or boosted SHARED lock! */
205 #define ConvertWToSLock(lock)\
206         BEGINMAC\
207 /*       if (afs_trclock) {icl_Trace2(cm_iclSetp, CM_TRACE_LOCKDOWN, ICL_TYPE_POINTER, (long)lock, ICL_TYPE_LONG, (long)SHARED_LOCK);} */ \
208             (lock)->excl_locked = SHARED_LOCK; \
209             if((lock)->wait_states) \
210                 Afs_Lock_ReleaseR(lock); \
211         ENDMAC
212
213 #define ConvertWToRLock(lock) \
214         BEGINMAC\
215 /*       if (afs_trclock) {icl_Trace2(cm_iclSetp, CM_TRACE_LOCKDOWN, ICL_TYPE_POINTER, (long)lock, ICL_TYPE_LONG, (long)READ_LOCK);} */ \
216             (lock)->excl_locked &= ~(SHARED_LOCK | WRITE_LOCK);\
217             ((lock)->readers_reading)++;\
218             (lock)->pid_last_reader = MyPidxx ; \
219             (lock)->pid_writer = 0;\
220             Afs_Lock_ReleaseR(lock);\
221         ENDMAC
222
223 #define ConvertSToRLock(lock) \
224         BEGINMAC\
225 /*       if (afs_trclock) {icl_Trace2(cm_iclSetp, CM_TRACE_LOCKDOWN, ICL_TYPE_POINTER, (long)lock, ICL_TYPE_LONG, (long)READ_LOCK);} */ \
226             (lock)->excl_locked &= ~(SHARED_LOCK | WRITE_LOCK);\
227             ((lock)->readers_reading)++;\
228             (lock)->pid_last_reader = MyPidxx ; \
229             (lock)->pid_writer = 0;\
230             Afs_Lock_ReleaseR(lock);\
231         ENDMAC
232
233 #define ReleaseReadLock(lock)\
234         BEGINMAC\
235 /*       if (afs_trclock) {icl_Trace2(cm_iclSetp, CM_TRACE_LOCKDONE, ICL_TYPE_POINTER, (long)lock, ICL_TYPE_LONG, (long)READ_LOCK);} */ \
236             if (!(--((lock)->readers_reading)) && (lock)->wait_states)\
237                 Afs_Lock_ReleaseW(lock) ; \
238         if ( (lock)->pid_last_reader == MyPidxx ) \
239                 (lock)->pid_last_reader =0;\
240         ENDMAC
241
242 #define ReleaseWriteLock(lock)\
243         BEGINMAC\
244 /*       if (afs_trclock) {icl_Trace2(cm_iclSetp, CM_TRACE_LOCKDONE, ICL_TYPE_POINTER, (long)lock, ICL_TYPE_LONG, (long)WRITE_LOCK);} */ \
245             (lock)->excl_locked &= ~WRITE_LOCK;\
246             if ((lock)->wait_states) Afs_Lock_ReleaseR(lock);\
247             (lock)->pid_writer=0; \
248         ENDMAC
249
250 /* can be used on shared or boosted (write) locks */
251 #define ReleaseSharedLock(lock)\
252         BEGINMAC\
253 /*       if (afs_trclock) {icl_Trace2(cm_iclSetp, CM_TRACE_LOCKDONE, ICL_TYPE_POINTER, (long)lock, ICL_TYPE_LONG, (long)SHARED_LOCK);} */ \
254             (lock)->excl_locked &= ~(SHARED_LOCK | WRITE_LOCK);\
255             if ((lock)->wait_states) Afs_Lock_ReleaseR(lock);\
256             (lock)->pid_writer=0; \
257         ENDMAC
258
259 #else /* INSTRUMENT_LOCKS */
260
261 #define ObtainReadLock(lock)\
262   BEGINMAC  \
263 /*       if (afs_trclock) {icl_Trace2(cm_iclSetp, CM_TRACE_LOCKOBTAIN, ICL_TYPE_POINTER, (long)lock, ICL_TYPE_LONG, (long)READ_LOCK);} */ \
264         if (!((lock)->excl_locked & WRITE_LOCK)) \
265             ((lock)->readers_reading)++; \
266         else \
267             Afs_Lock_Obtain(lock, READ_LOCK); \
268    ENDMAC
269
270 #define ObtainWriteLock(lock, src)\
271   BEGINMAC  \
272 /*       if (afs_trclock) {icl_Trace2(cm_iclSetp, CM_TRACE_LOCKOBTAIN, ICL_TYPE_POINTER, (long)lock, ICL_TYPE_LONG, (long)WRITE_LOCK);} */ \
273         if (!(lock)->excl_locked && !(lock)->readers_reading)\
274             (lock) -> excl_locked = WRITE_LOCK;\
275         else\
276             Afs_Lock_Obtain(lock, WRITE_LOCK); \
277    ENDMAC
278
279 #define NBObtainWriteLock(lock, src) (((lock)->excl_locked || (lock)->readers_reading) ? EWOULDBLOCK : (((lock) -> excl_locked = WRITE_LOCK),  0))
280
281 #define ObtainSharedLock(lock, src)\
282   BEGINMAC  \
283 /*       if (afs_trclock) {icl_Trace2(cm_iclSetp, CM_TRACE_LOCKOBTAIN, ICL_TYPE_POINTER, (long)lock, ICL_TYPE_LONG, (long)SHARED_LOCK);} */ \
284         if (!(lock)->excl_locked)\
285             (lock) -> excl_locked = SHARED_LOCK;\
286         else\
287             Afs_Lock_Obtain(lock, SHARED_LOCK); \
288    ENDMAC
289
290 #define NBObtainSharedLock(lock, src) (((lock)->excl_locked) ? EWOULDBLOCK : (((lock) -> excl_locked = SHARED_LOCK), 0))
291    
292 #define UpgradeSToWLock(lock, src)\
293   BEGINMAC  \
294 /*       if (afs_trclock) {icl_Trace2(cm_iclSetp, CM_TRACE_LOCKOBTAIN, ICL_TYPE_POINTER, (long)lock, ICL_TYPE_LONG, (long)BOOSTED_LOCK);} */ \
295         if (!(lock)->readers_reading)\
296             (lock)->excl_locked = WRITE_LOCK;\
297         else\
298             Afs_Lock_Obtain(lock, BOOSTED_LOCK); \
299    ENDMAC
300
301 /* this must only be called with a WRITE or boosted SHARED lock! */
302 #define ConvertWToSLock(lock)\
303         BEGINMAC\
304 /*       if (afs_trclock) {icl_Trace2(cm_iclSetp, CM_TRACE_LOCKDOWN, ICL_TYPE_POINTER, (long)lock, ICL_TYPE_LONG, (long)SHARED_LOCK);} */ \
305             (lock)->excl_locked = SHARED_LOCK; \
306             if((lock)->wait_states) \
307                 Afs_Lock_ReleaseR(lock); \
308         ENDMAC
309
310 #define ConvertWToRLock(lock) \
311         BEGINMAC\
312 /*       if (afs_trclock) {icl_Trace2(cm_iclSetp, CM_TRACE_LOCKDOWN, ICL_TYPE_POINTER, (long)lock, ICL_TYPE_LONG, (long)READ_LOCK);} */ \
313             (lock)->excl_locked &= ~(SHARED_LOCK | WRITE_LOCK);\
314             ((lock)->readers_reading)++;\
315             Afs_Lock_ReleaseR(lock);\
316         ENDMAC
317
318 #define ConvertSToRLock(lock) \
319         BEGINMAC\
320 /*       if (afs_trclock) {icl_Trace2(cm_iclSetp, CM_TRACE_LOCKDOWN, ICL_TYPE_POINTER, (long)lock, ICL_TYPE_LONG, (long)READ_LOCK);} */ \
321             (lock)->excl_locked &= ~(SHARED_LOCK | WRITE_LOCK);\
322             ((lock)->readers_reading)++;\
323             Afs_Lock_ReleaseR(lock);\
324         ENDMAC
325
326 #define ReleaseReadLock(lock)\
327         BEGINMAC\
328 /*       if (afs_trclock) {icl_Trace2(cm_iclSetp, CM_TRACE_LOCKDONE, ICL_TYPE_POINTER, (long)lock, ICL_TYPE_LONG, (long)READ_LOCK);} */ \
329             if (!(--((lock)->readers_reading)) && (lock)->wait_states)\
330                 Afs_Lock_ReleaseW(lock) ; \
331         ENDMAC
332
333 #define ReleaseWriteLock(lock)\
334         BEGINMAC\
335 /*       if (afs_trclock) {icl_Trace2(cm_iclSetp, CM_TRACE_LOCKDONE, ICL_TYPE_POINTER, (long)lock, ICL_TYPE_LONG, (long)WRITE_LOCK);} */ \
336             (lock)->excl_locked &= ~WRITE_LOCK;\
337             if ((lock)->wait_states) Afs_Lock_ReleaseR(lock);\
338         ENDMAC
339
340 /* can be used on shared or boosted (write) locks */
341 #define ReleaseSharedLock(lock)\
342         BEGINMAC\
343 /*       if (afs_trclock) {icl_Trace2(cm_iclSetp, CM_TRACE_LOCKDONE, ICL_TYPE_POINTER, (long)lock, ICL_TYPE_LONG, (long)SHARED_LOCK);} */ \
344             (lock)->excl_locked &= ~(SHARED_LOCK | WRITE_LOCK);\
345             if ((lock)->wait_states) Afs_Lock_ReleaseR(lock);\
346         ENDMAC
347
348 #endif /* INSTRUMENT_LOCKS */
349
350 /* I added this next macro to make sure it is safe to nuke a lock -- Mike K. */
351 #define LockWaiters(lock)\
352         ((int) ((lock)->num_waiting))
353
354 #define CheckLock(lock)\
355         ((lock)->excl_locked? (int) -1 : (int) (lock)->readers_reading)
356
357 #define WriteLocked(lock)\
358         ((lock)->excl_locked & WRITE_LOCK)
359 #endif
360
361 /*
362
363 You can also use the lock package for handling parent locks for independently-lockable sets of
364 small objects.  The concept here is that the parent lock is at the same level in the
365 locking hierarchy as the little locks, but certain restrictions apply.
366
367 The general usage pattern is as follows.  You have a set of entries to search.  When searching it, you
368 have a "scan" lock on the table.  If you find what you're looking for, you drop the lock down
369 to a "hold" lock, lock the entry, and release the parent lock.  If you don't find what
370 you're looking for, you create the entry, downgrade the "scan" lock to a "hold" lock,
371 lock the entry and unlock the parent.
372
373 To delete an item from the table, you initially obtain a "purge" lock on the parent.  Unlike all
374 of the other parent lock modes described herein, in order to obtain a "purge" lock mode, you
375 must have released all locks on any items in the table.  Once you have obtained the parent
376 lock in "purge" mode, you should check to see if the entry is locked.  If its not locked, you
377 are free to delete the entry, knowing that no one else can attempt to obtain a lock
378 on the entry while you have the purge lock held on the parent.  Unfortunately, if it *is* locked,
379 you can not lock it yourself and wait for the other dude to release it, since the entry's locker
380 may need to lock another entry before unlocking the entry you want (which would result in
381 deadlock).  Instead, then, you must release the parent lock, and try again "later" (see Lock_Wait
382 for assistance in waiting until later). Unfortunately, this is the best locking paradigm I've yet
383 come up with.
384
385 What are the advantages to this scheme?  First, the use of the parent lock ensures that
386 two people don't try to add the same entry at the same time or delete an entry while someone
387 else is adding it.  It also ensures that when one process is deleting an entry, no one else is
388 preparing to lock the entry.  Furthermore, when obtaining a lock on a little entry, you
389 are only holding a "hold" lock on the parent lock, so that others may come in and search
390 the table during this time.  Thus it will not hold up the system if a little entry takes
391 a great deal of time to free up.
392
393 Here's how to compute the compatibility matrix:
394
395 The invariants are:
396
397 add     no deletions, additions allowed, additions will be performed, will obtain little locks
398 hold    no deletions, additions allowed, no additions will be performed, will obtain little locks
399 purge   no deletions or additions allowed, deletions will be performed, don't obtain little locks
400
401 When we compute the locking matrix, we note that hold is compatible with hold and add.
402 Add is compatible only with hold.  purge is not compatible with anything.  This is the same
403 matrix as obtained by mapping add->S, hold->read and purge->write locks.  Thus we
404 can use the locks above to solve this problem, and we do.
405
406 */
407
408 #endif /* __AFSLOCK_INCLUDE__ */