2567e4ecde34deed5dec7e125b3c6a8d05113541
[openafs.git] / src / ubik / lock.c
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 #include <afsconfig.h>
11 #include <afs/param.h>
12
13 #include <roken.h>
14
15 #include <sys/types.h>
16 #include <stdarg.h>
17 #include <errno.h>
18
19 #ifndef AFS_NT40_ENV
20 #include <sys/file.h>
21 #endif
22
23 #include <lock.h>
24 #include <rx/xdr.h>
25
26 #define UBIK_INTERNALS 1
27 #include "ubik.h"
28 #include "ubik_int.h"
29
30 /*! \file
31  * Locks hang off of each transaction, with all the transaction hanging off of
32  * the appropriate dbase.  This package expects to be used in a two-phase locking
33  * protocol, so it doesn't provide a way to release anything but all of the locks in the
34  * transaction.
35  *
36  * At present, it doesn't support the setting of more than one byte-position lock at a time, that is
37  * the length field must be 1.  This doesn't mean that a single transaction can't set more than
38  * one lock, however.
39  *
40  * It is the responsibility of the user to avoid deadlock by setting locks in a partial order.
41  *
42  * #EWOULDBLOCK has been replaced in this file by #EAGAIN. Many Unix's but not
43  * all (eg. HP) do not replace #EWOULDBLOCK with #EAGAIN. The bad news is this
44  * goes over the wire. The good news is that the code path is never triggered
45  * as it requires ulock_getLock to be called with await = 0. And ulock_SetLock
46  * isn't even used in this code base. Since NT doesn't have a native
47  * #EAGAIN, we are replacing all instances of #EWOULDBLOCK with #EAGAIN.
48  *
49  */
50
51 #define WouldReadBlock(lock)\
52   ((((lock)->excl_locked & WRITE_LOCK) || (lock)->wait_states) ? 0 : 1)
53 #define WouldWriteBlock(lock)\
54   ((((lock)->excl_locked & WRITE_LOCK) || (lock)->readers_reading) ? 0 : 1)
55
56 struct Lock rwlock;
57 int rwlockinit = 1;
58
59 /*!
60  * \brief Set a transaction lock.
61  * \param atype is #LOCKREAD or #LOCKWRITE.
62  * \param await is TRUE if you want to wait for the lock instead of returning
63  * #EWOULDBLOCK.
64  *
65  * \note The #DBHOLD lock must be held.
66  */
67 extern int
68 ulock_getLock(struct ubik_trans *atrans, int atype, int await)
69 {
70     struct ubik_dbase *dbase = atrans->dbase;
71
72     /* On first pass, initialize the lock */
73     if (rwlockinit) {
74         Lock_Init(&rwlock);
75         rwlockinit = 0;
76     }
77
78     if ((atype != LOCKREAD) && (atype != LOCKWRITE))
79         return EINVAL;
80
81     if (atrans->flags & TRDONE)
82         return UDONE;
83
84     if (atype != LOCKREAD && (atrans->flags & TRREADWRITE)) {
85         return EINVAL;
86     }
87
88     if (atrans->locktype != 0) {
89         ubik_print("Ubik: Internal Error: attempted to take lock twice\n");
90         abort();
91     }
92
93 /*
94  *ubik_print("Ubik: DEBUG: Thread 0x%x request %s lock\n", lwp_cpptr,
95  *           ((atype == LOCKREAD) ? "READ" : "WRITE"));
96  */
97
98     /* Check if the lock would would block */
99     if (!await && !(atrans->flags & TRREADWRITE)) {
100         if (atype == LOCKREAD) {
101             if (WouldReadBlock(&rwlock))
102                 return EAGAIN;
103         } else {
104             if (WouldWriteBlock(&rwlock))
105                 return EAGAIN;
106         }
107     }
108
109     /* Create new lock record and add to spec'd transaction:
110      * #if defined(UBIK_PAUSE)
111      * * locktype.  Before doing that, set TRSETLOCK,
112      * * to tell udisk_end that another thread (us) is waiting.
113      * #else
114      * * locktype. This field also tells us if the thread is
115      * * waiting for a lock: It will be equal to LOCKWAIT.
116      * #endif
117      */
118 #if defined(UBIK_PAUSE)
119     if (atrans->flags & TRSETLOCK) {
120         printf("Ubik: Internal Error: TRSETLOCK already set?\n");
121         return EBUSY;
122     }
123     atrans->flags |= TRSETLOCK;
124 #else
125     atrans->locktype = LOCKWAIT;
126 #endif /* UBIK_PAUSE */
127     DBRELE(dbase);
128     if (atrans->flags & TRREADWRITE) {
129         /* noop; don't actually lock anything for TRREADWRITE */
130     } else if (atype == LOCKREAD) {
131         ObtainReadLock(&rwlock);
132     } else {
133         ObtainWriteLock(&rwlock);
134     }
135     DBHOLD(dbase);
136     atrans->locktype = atype;
137 #if defined(UBIK_PAUSE)
138     atrans->flags &= ~TRSETLOCK;
139 #if 0
140     /* We don't do this here, because this can only happen in SDISK_Lock,
141      *  and there's already code there to catch this condition.
142      */
143     if (atrans->flags & TRSTALE) {
144         udisk_end(atrans);
145         return UINTERNAL;
146     }
147 #endif
148 #endif /* UBIK_PAUSE */
149
150 /*
151  *ubik_print("Ubik: DEBUG: Thread 0x%x took %s lock\n", lwp_cpptr,
152  *           ((atype == LOCKREAD) ? "READ" : "WRITE"));
153  */
154     return 0;
155 }
156
157 /*!
158  * \brief Release the transaction lock.
159  */
160 void
161 ulock_relLock(struct ubik_trans *atrans)
162 {
163     if (rwlockinit)
164         return;
165
166     if (atrans->locktype == LOCKWRITE && (atrans->flags & TRREADWRITE)) {
167         ubik_print("Ubik: Internal Error: unlocking write lock with "
168                    "TRREADWRITE?\n");
169         abort();
170     }
171
172     if (atrans->flags & TRREADWRITE) {
173         /* noop, TRREADWRITE means we don't actually lock anything */
174     } else if (atrans->locktype == LOCKREAD) {
175         ReleaseReadLock(&rwlock);
176     } else if (atrans->locktype == LOCKWRITE) {
177         ReleaseWriteLock(&rwlock);
178     }
179
180 /*
181  *ubik_print("Ubik: DEBUG: Thread 0x%x %s unlock\n", lwp_cpptr,
182  *           ((atrans->locktype == LOCKREAD) ? "READ" : "WRITE"));
183  */
184
185     atrans->locktype = 0;
186     return;
187 }
188
189 /*!
190  * \brief debugging hooks
191  */
192 void
193 ulock_Debug(struct ubik_debug *aparm)
194 {
195     if (rwlockinit) {
196         aparm->anyReadLocks = 0;
197         aparm->anyWriteLocks = 0;
198     } else {
199         aparm->anyReadLocks = rwlock.readers_reading;
200         aparm->anyWriteLocks = ((rwlock.excl_locked == WRITE_LOCK) ? 1 : 0);
201     }
202 }