2 * Copyright 2000, International Business Machines Corporation and others.
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
10 #ifndef __AFSLOCK_INCLUDE__
11 #define __AFSLOCK_INCLUDE__ 1
14 * (C) COPYRIGHT IBM CORPORATION 1987
15 * LICENSED MATERIALS - PROPERTY OF IBM
18 /* The following macros allow multi statement macros to be defined safely, i.e.
19 - the multi statement macro can be the object of an if statement;
20 - the call to the multi statement macro may be legally followed by a semi-colon.
21 BEGINMAC and ENDMAC have been tested with both the portable C compiler and
22 Hi-C. Both compilers were from the Palo Alto 4.2BSD software releases, and
23 both optimized out the constant loop code. For an example of the use
24 of BEGINMAC and ENDMAC, see the definition for ReleaseWriteLock, below.
25 An alternative to this, using "if(1)" for BEGINMAC is not used because it
26 may generate worse code with pcc, and may generate warning messages with hi-C.
29 #if (defined(AFS_SUN5_ENV)) || defined(AFS_OSF_ENV)
30 #define AFS_NOBOZO_LOCK
33 #if !defined(AFS_OSF20_ENV) || defined(AFS_OSF30_ENV) || defined(AFS_OSF32_ENV)
34 /* We do not instrument locks on osf20 because the vcache structure
35 ** exceeds the maximim possible limit for a vnode.
37 #define INSTRUMENT_LOCKS
38 /* This is the max lock number in use. Please update it if you add any new
41 #define MAX_LOCK_NUMBER 700
45 short count; /* count of excl locks */
46 char flags; /* bit 1: is anyone waiting? */
47 char spare; /* for later */
48 char *proc; /* process holding the lock, really a struct proc * */
50 #ifndef AFS_NOBOZO_LOCK
51 typedef struct afs_bozoLock afs_bozoLock_t;
54 typedef kmutex_t afs_bozoLock_t;
56 typedef struct afs_bozoLock afs_bozoLock_t;
58 #define afs_BozonLock(lock, avc)
59 #define afs_BozonUnlock(lock, avc)
60 #define afs_BozonInit(lock, nm)
61 #define afs_CheckBozonLock(lock) 0
62 #define afs_CheckBozonLockBlocking(lock) 0
65 #define AFS_BOZONWAITING 1 /* someone is waiting for this lock */
67 #undef MObtainWriteLock /* Defined also in ../rx/rx_machdep.h" */
68 #undef MReleaseWriteLock
69 #ifndef RXObtainWriteLock
70 #define RXObtainWriteLock(lock) ObtainWriteLock(lock)
71 #define RXReleaseWriteLock(lock) ReleaseWriteLock(lock)
73 #define MObtainReadLock(lock) ObtainReadLock(lock)
74 #define MObtainWriteLock(lock,src) ObtainWriteLock(lock,src)
75 #define MObtainSharedLock(lock,src) ObtainSharedLock(lock,src)
76 #define MUpgradeSToWLock(lock,src) UpgradeSToWLock(lock,src)
77 #define MConvertWToSLock(lock) ConvertWToSLock(lock)
78 #define MReleaseReadLock(lock) ReleaseReadLock(lock)
79 #define MReleaseWriteLock(lock) ReleaseWriteLock(lock)
80 #define MReleaseSharedLock(lock) ReleaseSharedLock(lock)
82 #define RWLOCK_INIT(lock, nm) Lock_Init(lock)
84 #define LOCK_INIT(lock, nm) Lock_Init(lock)
87 #define ENDMAC } while (0)
89 #if defined(AFS_SUN5_ENV) || defined(AFS_OBSD_ENV)
90 #define MyPidxx (curproc->p_pid)
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))
99 #if defined(AFS_SGI64_ENV)
100 #if defined(AFS_SGI65_ENV)
101 #define MyPidxx proc_pid(curproc())
103 #define MyPidxx current_pid()
105 #else /* AFS_SGI64_ENV */
106 #ifdef AFS_LINUX20_ENV
107 #define MyPidxx current->pid
109 #if defined(AFS_DARWIN_ENV)
110 #define MyPidxx (current_proc()->p_pid )
112 #if defined(AFS_FBSD_ENV)
113 #define MyPidxx (curproc->p_pid )
115 #define MyPidxx (u.u_procp->p_pid )
116 #endif /* AFS_FBSD_ENV */
117 #endif /* AFS_DARWIN_ENV */
118 #endif /* AFS_LINUX20_ENV */
119 #endif /* AFS_SGI64_ENV */
120 #endif /* AFS_HPUX101_ENV */
121 #endif /* AFS_AIX41_ENV */
124 /* all locks wait on excl_locked except for READ_LOCK, which waits on readers_reading */
126 unsigned char wait_states; /* type of lockers waiting */
127 unsigned char excl_locked; /* anyone have boosted, shared or write lock? */
128 unsigned short readers_reading;/* # readers actually with read locks */
129 unsigned short num_waiting; /* probably need this soon */
130 unsigned short spare; /* not used now */
131 osi_timeval_t time_waiting; /* for statistics gathering */
132 #if defined(INSTRUMENT_LOCKS)
133 /* the following are useful for debugging
134 ** the field 'src_indicator' is updated only by ObtainLock() and
135 ** only for writes/shared locks. Hence, it indictes where in the
136 ** source code the shared/write lock was set.
138 unsigned int pid_last_reader;/* proceess id of last reader */
139 unsigned int pid_writer; /* process id of writer, else 0 */
140 unsigned int src_indicator; /* third param to ObtainLock()*/
141 #endif /* INSTRUMENT_LOCKS */
143 typedef struct afs_lock afs_lock_t;
144 typedef struct afs_lock afs_rwlock_t;
148 #define SHARED_LOCK 4
149 /* this next is not a flag, but rather a parameter to Afs_Lock_Obtain */
150 #define BOOSTED_LOCK 6
152 /* next defines wait_states for which we wait on excl_locked */
153 #define EXCL_LOCKS (WRITE_LOCK|SHARED_LOCK)
158 extern int afs_trclock;
160 #define AFS_LOCK_TRACE_ENABLE 0
161 #if AFS_LOCK_TRACE_ENABLE
162 #define AFS_LOCK_TRACE(op, lock, type) \
163 if (afs_trclock) Afs_Lock_Trace(op, lock, type, __FILE__, __LINE__);
165 #define AFS_LOCK_TRACE(op, lock, type)
168 #if defined(INSTRUMENT_LOCKS)
170 #define ObtainReadLock(lock)\
172 AFS_LOCK_TRACE(CM_TRACE_LOCKOBTAIN, lock, READ_LOCK);\
173 if (!((lock)->excl_locked & WRITE_LOCK)) \
174 ((lock)->readers_reading)++; \
176 Afs_Lock_Obtain(lock, READ_LOCK); \
177 (lock)->pid_last_reader = MyPidxx; \
180 #define ObtainWriteLock(lock, src)\
182 AFS_LOCK_TRACE(CM_TRACE_LOCKOBTAIN, lock, WRITE_LOCK);\
183 if (!(lock)->excl_locked && !(lock)->readers_reading)\
184 (lock) -> excl_locked = WRITE_LOCK;\
186 Afs_Lock_Obtain(lock, WRITE_LOCK); \
187 (lock)->pid_writer = MyPidxx; \
188 (lock)->src_indicator = src;\
191 #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))
193 #define ObtainSharedLock(lock, src)\
195 AFS_LOCK_TRACE(CM_TRACE_LOCKOBTAIN, lock, SHARED_LOCK);\
196 if (!(lock)->excl_locked)\
197 (lock) -> excl_locked = SHARED_LOCK;\
199 Afs_Lock_Obtain(lock, SHARED_LOCK); \
200 (lock)->pid_writer = MyPidxx; \
201 (lock)->src_indicator = src;\
204 #define NBObtainSharedLock(lock, src) (((lock)->excl_locked) ? EWOULDBLOCK : (((lock) -> excl_locked = SHARED_LOCK), ((lock)->pid_writer = MyPidxx), ((lock)->src_indicator = src), 0))
206 #define UpgradeSToWLock(lock, src)\
208 AFS_LOCK_TRACE(CM_TRACE_LOCKOBTAIN, lock, BOOSTED_LOCK);\
209 if (!(lock)->readers_reading)\
210 (lock)->excl_locked = WRITE_LOCK;\
212 Afs_Lock_Obtain(lock, BOOSTED_LOCK); \
213 (lock)->pid_writer = MyPidxx; \
214 (lock)->src_indicator = src;\
217 /* this must only be called with a WRITE or boosted SHARED lock! */
218 #define ConvertWToSLock(lock)\
220 AFS_LOCK_TRACE(CM_TRACE_LOCKDOWN, lock, SHARED_LOCK);\
221 (lock)->excl_locked = SHARED_LOCK; \
222 if((lock)->wait_states) \
223 Afs_Lock_ReleaseR(lock); \
226 #define ConvertWToRLock(lock) \
228 AFS_LOCK_TRACE(CM_TRACE_LOCKDOWN, lock, READ_LOCK);\
229 (lock)->excl_locked &= ~(SHARED_LOCK | WRITE_LOCK);\
230 ((lock)->readers_reading)++;\
231 (lock)->pid_last_reader = MyPidxx ; \
232 (lock)->pid_writer = 0;\
233 Afs_Lock_ReleaseR(lock);\
236 #define ConvertSToRLock(lock) \
238 AFS_LOCK_TRACE(CM_TRACE_LOCKDOWN, lock, READ_LOCK);\
239 (lock)->excl_locked &= ~(SHARED_LOCK | WRITE_LOCK);\
240 ((lock)->readers_reading)++;\
241 (lock)->pid_last_reader = MyPidxx ; \
242 (lock)->pid_writer = 0;\
243 Afs_Lock_ReleaseR(lock);\
246 #define ReleaseReadLock(lock)\
248 AFS_LOCK_TRACE(CM_TRACE_LOCKDONE, lock, READ_LOCK);\
249 if (!(--((lock)->readers_reading)) && (lock)->wait_states)\
250 Afs_Lock_ReleaseW(lock) ; \
251 if ( (lock)->pid_last_reader == MyPidxx ) \
252 (lock)->pid_last_reader =0;\
255 #define ReleaseWriteLock(lock)\
257 AFS_LOCK_TRACE(CM_TRACE_LOCKDONE, lock, WRITE_LOCK);\
258 (lock)->excl_locked &= ~WRITE_LOCK;\
259 if ((lock)->wait_states) Afs_Lock_ReleaseR(lock);\
260 (lock)->pid_writer=0; \
263 /* can be used on shared or boosted (write) locks */
264 #define ReleaseSharedLock(lock)\
266 AFS_LOCK_TRACE(CM_TRACE_LOCKDONE, lock, SHARED_LOCK);\
267 (lock)->excl_locked &= ~(SHARED_LOCK | WRITE_LOCK);\
268 if ((lock)->wait_states) Afs_Lock_ReleaseR(lock);\
269 (lock)->pid_writer=0; \
272 #else /* INSTRUMENT_LOCKS */
274 #define ObtainReadLock(lock)\
276 AFS_LOCK_TRACE(CM_TRACE_LOCKOBTAIN, lock, READ_LOCK);\
277 if (!((lock)->excl_locked & WRITE_LOCK)) \
278 ((lock)->readers_reading)++; \
280 Afs_Lock_Obtain(lock, READ_LOCK); \
283 #define ObtainWriteLock(lock, src)\
285 AFS_LOCK_TRACE(CM_TRACE_LOCKOBTAIN, lock, WRITE_LOCK);\
286 if (!(lock)->excl_locked && !(lock)->readers_reading)\
287 (lock) -> excl_locked = WRITE_LOCK;\
289 Afs_Lock_Obtain(lock, WRITE_LOCK); \
292 #define NBObtainWriteLock(lock, src) (((lock)->excl_locked || (lock)->readers_reading) ? EWOULDBLOCK : (((lock) -> excl_locked = WRITE_LOCK), 0))
294 #define ObtainSharedLock(lock, src)\
296 AFS_LOCK_TRACE(CM_TRACE_LOCKOBTAIN, lock, SHARED_LOCK);\
297 if (!(lock)->excl_locked)\
298 (lock) -> excl_locked = SHARED_LOCK;\
300 Afs_Lock_Obtain(lock, SHARED_LOCK); \
303 #define NBObtainSharedLock(lock, src) (((lock)->excl_locked) ? EWOULDBLOCK : (((lock) -> excl_locked = SHARED_LOCK), 0))
305 #define UpgradeSToWLock(lock, src)\
307 AFS_LOCK_TRACE(CM_TRACE_LOCKOBTAIN, lock, BOOSTED_LOCK);\
308 if (!(lock)->readers_reading)\
309 (lock)->excl_locked = WRITE_LOCK;\
311 Afs_Lock_Obtain(lock, BOOSTED_LOCK); \
314 /* this must only be called with a WRITE or boosted SHARED lock! */
315 #define ConvertWToSLock(lock)\
317 AFS_LOCK_TRACE(CM_TRACE_LOCKDOWN, lock, SHARED_LOCK);\
318 (lock)->excl_locked = SHARED_LOCK; \
319 if((lock)->wait_states) \
320 Afs_Lock_ReleaseR(lock); \
323 #define ConvertWToRLock(lock) \
325 AFS_LOCK_TRACE(CM_TRACE_LOCKDOWN, lock, READ_LOCK);\
326 (lock)->excl_locked &= ~(SHARED_LOCK | WRITE_LOCK);\
327 ((lock)->readers_reading)++;\
328 Afs_Lock_ReleaseR(lock);\
331 #define ConvertSToRLock(lock) \
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);\
339 #define ReleaseReadLock(lock)\
341 AFS_LOCK_TRACE(CM_TRACE_LOCKDONE, lock, READ_LOCK);\
342 if (!(--((lock)->readers_reading)) && (lock)->wait_states)\
343 Afs_Lock_ReleaseW(lock) ; \
346 #define ReleaseWriteLock(lock)\
348 AFS_LOCK_TRACE(CM_TRACE_LOCKDONE, lock, WRITE_LOCK);\
349 (lock)->excl_locked &= ~WRITE_LOCK;\
350 if ((lock)->wait_states) Afs_Lock_ReleaseR(lock);\
353 /* can be used on shared or boosted (write) locks */
354 #define ReleaseSharedLock(lock)\
356 AFS_LOCK_TRACE(CM_TRACE_LOCKDONE, lock, SHARED_LOCK);\
357 (lock)->excl_locked &= ~(SHARED_LOCK | WRITE_LOCK);\
358 if ((lock)->wait_states) Afs_Lock_ReleaseR(lock);\
361 #endif /* INSTRUMENT_LOCKS */
363 /* I added this next macro to make sure it is safe to nuke a lock -- Mike K. */
364 #define LockWaiters(lock)\
365 ((int) ((lock)->num_waiting))
367 #define CheckLock(lock)\
368 ((lock)->excl_locked? (int) -1 : (int) (lock)->readers_reading)
370 #define WriteLocked(lock)\
371 ((lock)->excl_locked & WRITE_LOCK)
376 You can also use the lock package for handling parent locks for independently-lockable sets of
377 small objects. The concept here is that the parent lock is at the same level in the
378 locking hierarchy as the little locks, but certain restrictions apply.
380 The general usage pattern is as follows. You have a set of entries to search. When searching it, you
381 have a "scan" lock on the table. If you find what you're looking for, you drop the lock down
382 to a "hold" lock, lock the entry, and release the parent lock. If you don't find what
383 you're looking for, you create the entry, downgrade the "scan" lock to a "hold" lock,
384 lock the entry and unlock the parent.
386 To delete an item from the table, you initially obtain a "purge" lock on the parent. Unlike all
387 of the other parent lock modes described herein, in order to obtain a "purge" lock mode, you
388 must have released all locks on any items in the table. Once you have obtained the parent
389 lock in "purge" mode, you should check to see if the entry is locked. If its not locked, you
390 are free to delete the entry, knowing that no one else can attempt to obtain a lock
391 on the entry while you have the purge lock held on the parent. Unfortunately, if it *is* locked,
392 you can not lock it yourself and wait for the other dude to release it, since the entry's locker
393 may need to lock another entry before unlocking the entry you want (which would result in
394 deadlock). Instead, then, you must release the parent lock, and try again "later" (see Lock_Wait
395 for assistance in waiting until later). Unfortunately, this is the best locking paradigm I've yet
398 What are the advantages to this scheme? First, the use of the parent lock ensures that
399 two people don't try to add the same entry at the same time or delete an entry while someone
400 else is adding it. It also ensures that when one process is deleting an entry, no one else is
401 preparing to lock the entry. Furthermore, when obtaining a lock on a little entry, you
402 are only holding a "hold" lock on the parent lock, so that others may come in and search
403 the table during this time. Thus it will not hold up the system if a little entry takes
404 a great deal of time to free up.
406 Here's how to compute the compatibility matrix:
410 add no deletions, additions allowed, additions will be performed, will obtain little locks
411 hold no deletions, additions allowed, no additions will be performed, will obtain little locks
412 purge no deletions or additions allowed, deletions will be performed, don't obtain little locks
414 When we compute the locking matrix, we note that hold is compatible with hold and add.
415 Add is compatible only with hold. purge is not compatible with anything. This is the same
416 matrix as obtained by mapping add->S, hold->read and purge->write locks. Thus we
417 can use the locks above to solve this problem, and we do.
421 #endif /* __AFSLOCK_INCLUDE__ */