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