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