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