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