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