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