1*0Sstevel@tonic-gate /*-
2*0Sstevel@tonic-gate * See the file LICENSE for redistribution information.
3*0Sstevel@tonic-gate *
4*0Sstevel@tonic-gate * Copyright (c) 1996, 1997, 1998
5*0Sstevel@tonic-gate * Sleepycat Software. All rights reserved.
6*0Sstevel@tonic-gate */
7*0Sstevel@tonic-gate
8*0Sstevel@tonic-gate #include "config.h"
9*0Sstevel@tonic-gate
10*0Sstevel@tonic-gate #ifndef lint
11*0Sstevel@tonic-gate static const char sccsid[] = "@(#)lock.c 10.61 (Sleepycat) 1/3/99";
12*0Sstevel@tonic-gate #endif /* not lint */
13*0Sstevel@tonic-gate
14*0Sstevel@tonic-gate #ifndef NO_SYSTEM_INCLUDES
15*0Sstevel@tonic-gate #include <sys/types.h>
16*0Sstevel@tonic-gate
17*0Sstevel@tonic-gate #include <errno.h>
18*0Sstevel@tonic-gate #include <string.h>
19*0Sstevel@tonic-gate #endif
20*0Sstevel@tonic-gate
21*0Sstevel@tonic-gate #include "db_int.h"
22*0Sstevel@tonic-gate #include "shqueue.h"
23*0Sstevel@tonic-gate #include "db_page.h"
24*0Sstevel@tonic-gate #include "db_shash.h"
25*0Sstevel@tonic-gate #include "lock.h"
26*0Sstevel@tonic-gate #include "db_am.h"
27*0Sstevel@tonic-gate #include "txn_auto.h"
28*0Sstevel@tonic-gate #include "txn_ext.h"
29*0Sstevel@tonic-gate #include "common_ext.h"
30*0Sstevel@tonic-gate
31*0Sstevel@tonic-gate static void __lock_checklocker __P((DB_LOCKTAB *, struct __db_lock *, int));
32*0Sstevel@tonic-gate static void __lock_freeobj __P((DB_LOCKTAB *, DB_LOCKOBJ *));
33*0Sstevel@tonic-gate static int __lock_get_internal __P((DB_LOCKTAB *, u_int32_t, DB_TXN *,
34*0Sstevel@tonic-gate u_int32_t, const DBT *, db_lockmode_t, struct __db_lock **));
35*0Sstevel@tonic-gate static int __lock_is_parent __P((u_int32_t, DB_TXN *));
36*0Sstevel@tonic-gate static int __lock_promote __P((DB_LOCKTAB *, DB_LOCKOBJ *));
37*0Sstevel@tonic-gate static int __lock_put_internal __P((DB_LOCKTAB *, struct __db_lock *, int));
38*0Sstevel@tonic-gate static void __lock_remove_waiter
39*0Sstevel@tonic-gate __P((DB_LOCKTAB *, DB_LOCKOBJ *, struct __db_lock *, db_status_t));
40*0Sstevel@tonic-gate static int __lock_vec_internal __P((DB_LOCKTAB *, u_int32_t, DB_TXN *,
41*0Sstevel@tonic-gate u_int32_t, DB_LOCKREQ *, int, DB_LOCKREQ **elistp));
42*0Sstevel@tonic-gate
43*0Sstevel@tonic-gate int
lock_id(lt,idp)44*0Sstevel@tonic-gate lock_id(lt, idp)
45*0Sstevel@tonic-gate DB_LOCKTAB *lt;
46*0Sstevel@tonic-gate u_int32_t *idp;
47*0Sstevel@tonic-gate {
48*0Sstevel@tonic-gate u_int32_t id;
49*0Sstevel@tonic-gate
50*0Sstevel@tonic-gate LOCK_PANIC_CHECK(lt);
51*0Sstevel@tonic-gate
52*0Sstevel@tonic-gate LOCK_LOCKREGION(lt);
53*0Sstevel@tonic-gate if (lt->region->id >= DB_LOCK_MAXID)
54*0Sstevel@tonic-gate lt->region->id = 0;
55*0Sstevel@tonic-gate id = ++lt->region->id;
56*0Sstevel@tonic-gate UNLOCK_LOCKREGION(lt);
57*0Sstevel@tonic-gate
58*0Sstevel@tonic-gate *idp = id;
59*0Sstevel@tonic-gate return (0);
60*0Sstevel@tonic-gate }
61*0Sstevel@tonic-gate
62*0Sstevel@tonic-gate int
lock_vec(lt,locker,flags,list,nlist,elistp)63*0Sstevel@tonic-gate lock_vec(lt, locker, flags, list, nlist, elistp)
64*0Sstevel@tonic-gate DB_LOCKTAB *lt;
65*0Sstevel@tonic-gate u_int32_t locker, flags;
66*0Sstevel@tonic-gate int nlist;
67*0Sstevel@tonic-gate DB_LOCKREQ *list, **elistp;
68*0Sstevel@tonic-gate {
69*0Sstevel@tonic-gate return (__lock_vec_internal(lt,
70*0Sstevel@tonic-gate locker, NULL, flags, list, nlist, elistp));
71*0Sstevel@tonic-gate }
72*0Sstevel@tonic-gate
73*0Sstevel@tonic-gate int
lock_tvec(lt,txn,flags,list,nlist,elistp)74*0Sstevel@tonic-gate lock_tvec(lt, txn, flags, list, nlist, elistp)
75*0Sstevel@tonic-gate DB_LOCKTAB *lt;
76*0Sstevel@tonic-gate DB_TXN *txn;
77*0Sstevel@tonic-gate u_int32_t flags;
78*0Sstevel@tonic-gate int nlist;
79*0Sstevel@tonic-gate DB_LOCKREQ *list, **elistp;
80*0Sstevel@tonic-gate {
81*0Sstevel@tonic-gate return (__lock_vec_internal(lt,
82*0Sstevel@tonic-gate txn->txnid, txn, flags, list, nlist, elistp));
83*0Sstevel@tonic-gate }
84*0Sstevel@tonic-gate
85*0Sstevel@tonic-gate static int
__lock_vec_internal(lt,locker,txn,flags,list,nlist,elistp)86*0Sstevel@tonic-gate __lock_vec_internal(lt, locker, txn, flags, list, nlist, elistp)
87*0Sstevel@tonic-gate DB_LOCKTAB *lt;
88*0Sstevel@tonic-gate u_int32_t locker;
89*0Sstevel@tonic-gate DB_TXN *txn;
90*0Sstevel@tonic-gate u_int32_t flags;
91*0Sstevel@tonic-gate int nlist;
92*0Sstevel@tonic-gate DB_LOCKREQ *list, **elistp;
93*0Sstevel@tonic-gate {
94*0Sstevel@tonic-gate struct __db_lock *lp;
95*0Sstevel@tonic-gate DB_LOCKOBJ *sh_obj, *sh_locker, *sh_parent;
96*0Sstevel@tonic-gate int i, ret, run_dd;
97*0Sstevel@tonic-gate
98*0Sstevel@tonic-gate LOCK_PANIC_CHECK(lt);
99*0Sstevel@tonic-gate
100*0Sstevel@tonic-gate /* Validate arguments. */
101*0Sstevel@tonic-gate if ((ret =
102*0Sstevel@tonic-gate __db_fchk(lt->dbenv, "lock_vec", flags, DB_LOCK_NOWAIT)) != 0)
103*0Sstevel@tonic-gate return (ret);
104*0Sstevel@tonic-gate
105*0Sstevel@tonic-gate LOCK_LOCKREGION(lt);
106*0Sstevel@tonic-gate
107*0Sstevel@tonic-gate if ((ret = __lock_validate_region(lt)) != 0) {
108*0Sstevel@tonic-gate UNLOCK_LOCKREGION(lt);
109*0Sstevel@tonic-gate return (ret);
110*0Sstevel@tonic-gate }
111*0Sstevel@tonic-gate
112*0Sstevel@tonic-gate ret = 0;
113*0Sstevel@tonic-gate for (i = 0; i < nlist && ret == 0; i++) {
114*0Sstevel@tonic-gate switch (list[i].op) {
115*0Sstevel@tonic-gate case DB_LOCK_GET:
116*0Sstevel@tonic-gate ret = __lock_get_internal(lt, locker, txn, flags,
117*0Sstevel@tonic-gate list[i].obj, list[i].mode, &lp);
118*0Sstevel@tonic-gate if (ret == 0) {
119*0Sstevel@tonic-gate list[i].lock = LOCK_TO_OFFSET(lt, lp);
120*0Sstevel@tonic-gate lt->region->nrequests++;
121*0Sstevel@tonic-gate }
122*0Sstevel@tonic-gate break;
123*0Sstevel@tonic-gate case DB_LOCK_INHERIT:
124*0Sstevel@tonic-gate /* Find the locker. */
125*0Sstevel@tonic-gate if ((ret = __lock_getobj(lt, locker,
126*0Sstevel@tonic-gate NULL, DB_LOCK_LOCKER, &sh_locker)) != 0)
127*0Sstevel@tonic-gate break;
128*0Sstevel@tonic-gate if (txn == NULL || txn->parent == NULL) {
129*0Sstevel@tonic-gate ret = EINVAL;
130*0Sstevel@tonic-gate break;
131*0Sstevel@tonic-gate }
132*0Sstevel@tonic-gate
133*0Sstevel@tonic-gate if ((ret = __lock_getobj(lt, txn->parent->txnid,
134*0Sstevel@tonic-gate NULL, DB_LOCK_LOCKER, &sh_parent)) != 0)
135*0Sstevel@tonic-gate break;
136*0Sstevel@tonic-gate
137*0Sstevel@tonic-gate /*
138*0Sstevel@tonic-gate * Traverse all the locks held by this locker. Remove
139*0Sstevel@tonic-gate * the locks from the locker's list and put them on the
140*0Sstevel@tonic-gate * parent's list.
141*0Sstevel@tonic-gate */
142*0Sstevel@tonic-gate for (lp = SH_LIST_FIRST(&sh_locker->heldby, __db_lock);
143*0Sstevel@tonic-gate lp != NULL;
144*0Sstevel@tonic-gate lp = SH_LIST_FIRST(&sh_locker->heldby, __db_lock)) {
145*0Sstevel@tonic-gate SH_LIST_REMOVE(lp, locker_links, __db_lock);
146*0Sstevel@tonic-gate SH_LIST_INSERT_HEAD(&sh_parent->heldby, lp,
147*0Sstevel@tonic-gate locker_links, __db_lock);
148*0Sstevel@tonic-gate lp->holder = txn->parent->txnid;
149*0Sstevel@tonic-gate }
150*0Sstevel@tonic-gate __lock_freeobj(lt, sh_locker);
151*0Sstevel@tonic-gate lt->region->nlockers--;
152*0Sstevel@tonic-gate break;
153*0Sstevel@tonic-gate case DB_LOCK_PUT:
154*0Sstevel@tonic-gate lp = OFFSET_TO_LOCK(lt, list[i].lock);
155*0Sstevel@tonic-gate if (lp->holder != locker) {
156*0Sstevel@tonic-gate ret = DB_LOCK_NOTHELD;
157*0Sstevel@tonic-gate break;
158*0Sstevel@tonic-gate }
159*0Sstevel@tonic-gate list[i].mode = lp->mode;
160*0Sstevel@tonic-gate
161*0Sstevel@tonic-gate ret = __lock_put_internal(lt, lp, 0);
162*0Sstevel@tonic-gate __lock_checklocker(lt, lp, 0);
163*0Sstevel@tonic-gate break;
164*0Sstevel@tonic-gate case DB_LOCK_PUT_ALL:
165*0Sstevel@tonic-gate /* Find the locker. */
166*0Sstevel@tonic-gate if ((ret = __lock_getobj(lt, locker,
167*0Sstevel@tonic-gate NULL, DB_LOCK_LOCKER, &sh_locker)) != 0)
168*0Sstevel@tonic-gate break;
169*0Sstevel@tonic-gate
170*0Sstevel@tonic-gate for (lp = SH_LIST_FIRST(&sh_locker->heldby, __db_lock);
171*0Sstevel@tonic-gate lp != NULL;
172*0Sstevel@tonic-gate lp = SH_LIST_FIRST(&sh_locker->heldby, __db_lock)) {
173*0Sstevel@tonic-gate if ((ret = __lock_put_internal(lt, lp, 1)) != 0)
174*0Sstevel@tonic-gate break;
175*0Sstevel@tonic-gate }
176*0Sstevel@tonic-gate __lock_freeobj(lt, sh_locker);
177*0Sstevel@tonic-gate lt->region->nlockers--;
178*0Sstevel@tonic-gate break;
179*0Sstevel@tonic-gate case DB_LOCK_PUT_OBJ:
180*0Sstevel@tonic-gate
181*0Sstevel@tonic-gate /* Look up the object in the hash table. */
182*0Sstevel@tonic-gate HASHLOOKUP(lt->hashtab, __db_lockobj, links,
183*0Sstevel@tonic-gate list[i].obj, sh_obj, lt->region->table_size,
184*0Sstevel@tonic-gate __lock_ohash, __lock_cmp);
185*0Sstevel@tonic-gate if (sh_obj == NULL) {
186*0Sstevel@tonic-gate ret = EINVAL;
187*0Sstevel@tonic-gate break;
188*0Sstevel@tonic-gate }
189*0Sstevel@tonic-gate /*
190*0Sstevel@tonic-gate * Release waiters first, because they won't cause
191*0Sstevel@tonic-gate * anyone else to be awakened. If we release the
192*0Sstevel@tonic-gate * lockers first, all the waiters get awakened
193*0Sstevel@tonic-gate * needlessly.
194*0Sstevel@tonic-gate */
195*0Sstevel@tonic-gate for (lp = SH_TAILQ_FIRST(&sh_obj->waiters, __db_lock);
196*0Sstevel@tonic-gate lp != NULL;
197*0Sstevel@tonic-gate lp = SH_TAILQ_FIRST(&sh_obj->waiters, __db_lock)) {
198*0Sstevel@tonic-gate lt->region->nreleases += lp->refcount;
199*0Sstevel@tonic-gate __lock_remove_waiter(lt, sh_obj, lp,
200*0Sstevel@tonic-gate DB_LSTAT_NOGRANT);
201*0Sstevel@tonic-gate __lock_checklocker(lt, lp, 1);
202*0Sstevel@tonic-gate }
203*0Sstevel@tonic-gate
204*0Sstevel@tonic-gate for (lp = SH_TAILQ_FIRST(&sh_obj->holders, __db_lock);
205*0Sstevel@tonic-gate lp != NULL;
206*0Sstevel@tonic-gate lp = SH_TAILQ_FIRST(&sh_obj->holders, __db_lock)) {
207*0Sstevel@tonic-gate
208*0Sstevel@tonic-gate lt->region->nreleases += lp->refcount;
209*0Sstevel@tonic-gate SH_LIST_REMOVE(lp, locker_links, __db_lock);
210*0Sstevel@tonic-gate SH_TAILQ_REMOVE(&sh_obj->holders, lp, links,
211*0Sstevel@tonic-gate __db_lock);
212*0Sstevel@tonic-gate lp->status = DB_LSTAT_FREE;
213*0Sstevel@tonic-gate SH_TAILQ_INSERT_HEAD(<->region->free_locks,
214*0Sstevel@tonic-gate lp, links, __db_lock);
215*0Sstevel@tonic-gate }
216*0Sstevel@tonic-gate
217*0Sstevel@tonic-gate /* Now free the object. */
218*0Sstevel@tonic-gate __lock_freeobj(lt, sh_obj);
219*0Sstevel@tonic-gate break;
220*0Sstevel@tonic-gate #ifdef DEBUG
221*0Sstevel@tonic-gate case DB_LOCK_DUMP:
222*0Sstevel@tonic-gate /* Find the locker. */
223*0Sstevel@tonic-gate if ((ret = __lock_getobj(lt, locker,
224*0Sstevel@tonic-gate NULL, DB_LOCK_LOCKER, &sh_locker)) != 0)
225*0Sstevel@tonic-gate break;
226*0Sstevel@tonic-gate
227*0Sstevel@tonic-gate for (lp = SH_LIST_FIRST(&sh_locker->heldby, __db_lock);
228*0Sstevel@tonic-gate lp != NULL;
229*0Sstevel@tonic-gate lp = SH_LIST_NEXT(lp, locker_links, __db_lock)) {
230*0Sstevel@tonic-gate __lock_printlock(lt, lp, 1);
231*0Sstevel@tonic-gate ret = EINVAL;
232*0Sstevel@tonic-gate }
233*0Sstevel@tonic-gate if (ret == 0) {
234*0Sstevel@tonic-gate __lock_freeobj(lt, sh_locker);
235*0Sstevel@tonic-gate lt->region->nlockers--;
236*0Sstevel@tonic-gate }
237*0Sstevel@tonic-gate break;
238*0Sstevel@tonic-gate #endif
239*0Sstevel@tonic-gate default:
240*0Sstevel@tonic-gate ret = EINVAL;
241*0Sstevel@tonic-gate break;
242*0Sstevel@tonic-gate }
243*0Sstevel@tonic-gate }
244*0Sstevel@tonic-gate
245*0Sstevel@tonic-gate if (lt->region->need_dd && lt->region->detect != DB_LOCK_NORUN) {
246*0Sstevel@tonic-gate run_dd = 1;
247*0Sstevel@tonic-gate lt->region->need_dd = 0;
248*0Sstevel@tonic-gate } else
249*0Sstevel@tonic-gate run_dd = 0;
250*0Sstevel@tonic-gate
251*0Sstevel@tonic-gate UNLOCK_LOCKREGION(lt);
252*0Sstevel@tonic-gate
253*0Sstevel@tonic-gate if (ret == 0 && run_dd)
254*0Sstevel@tonic-gate lock_detect(lt, 0, lt->region->detect);
255*0Sstevel@tonic-gate
256*0Sstevel@tonic-gate if (elistp && ret != 0)
257*0Sstevel@tonic-gate *elistp = &list[i - 1];
258*0Sstevel@tonic-gate return (ret);
259*0Sstevel@tonic-gate }
260*0Sstevel@tonic-gate
261*0Sstevel@tonic-gate int
lock_get(lt,locker,flags,obj,lock_mode,lock)262*0Sstevel@tonic-gate lock_get(lt, locker, flags, obj, lock_mode, lock)
263*0Sstevel@tonic-gate DB_LOCKTAB *lt;
264*0Sstevel@tonic-gate u_int32_t locker, flags;
265*0Sstevel@tonic-gate const DBT *obj;
266*0Sstevel@tonic-gate db_lockmode_t lock_mode;
267*0Sstevel@tonic-gate DB_LOCK *lock;
268*0Sstevel@tonic-gate {
269*0Sstevel@tonic-gate struct __db_lock *lockp;
270*0Sstevel@tonic-gate int ret;
271*0Sstevel@tonic-gate
272*0Sstevel@tonic-gate LOCK_PANIC_CHECK(lt);
273*0Sstevel@tonic-gate
274*0Sstevel@tonic-gate /* Validate arguments. */
275*0Sstevel@tonic-gate if ((ret = __db_fchk(lt->dbenv,
276*0Sstevel@tonic-gate "lock_get", flags, DB_LOCK_NOWAIT | DB_LOCK_UPGRADE)) != 0)
277*0Sstevel@tonic-gate return (ret);
278*0Sstevel@tonic-gate
279*0Sstevel@tonic-gate LOCK_LOCKREGION(lt);
280*0Sstevel@tonic-gate
281*0Sstevel@tonic-gate if ((ret = __lock_validate_region(lt)) == 0) {
282*0Sstevel@tonic-gate if (LF_ISSET(DB_LOCK_UPGRADE))
283*0Sstevel@tonic-gate lockp = OFFSET_TO_LOCK(lt, *lock);
284*0Sstevel@tonic-gate
285*0Sstevel@tonic-gate if ((ret = __lock_get_internal(lt,
286*0Sstevel@tonic-gate locker, NULL, flags, obj, lock_mode, &lockp)) == 0) {
287*0Sstevel@tonic-gate if (!LF_ISSET(DB_LOCK_UPGRADE))
288*0Sstevel@tonic-gate *lock = LOCK_TO_OFFSET(lt, lockp);
289*0Sstevel@tonic-gate lt->region->nrequests++;
290*0Sstevel@tonic-gate }
291*0Sstevel@tonic-gate }
292*0Sstevel@tonic-gate
293*0Sstevel@tonic-gate UNLOCK_LOCKREGION(lt);
294*0Sstevel@tonic-gate return (ret);
295*0Sstevel@tonic-gate }
296*0Sstevel@tonic-gate
297*0Sstevel@tonic-gate int
lock_tget(lt,txn,flags,obj,lock_mode,lock)298*0Sstevel@tonic-gate lock_tget(lt, txn, flags, obj, lock_mode, lock)
299*0Sstevel@tonic-gate DB_LOCKTAB *lt;
300*0Sstevel@tonic-gate DB_TXN *txn;
301*0Sstevel@tonic-gate u_int32_t flags;
302*0Sstevel@tonic-gate const DBT *obj;
303*0Sstevel@tonic-gate db_lockmode_t lock_mode;
304*0Sstevel@tonic-gate DB_LOCK *lock;
305*0Sstevel@tonic-gate {
306*0Sstevel@tonic-gate struct __db_lock *lockp;
307*0Sstevel@tonic-gate int ret;
308*0Sstevel@tonic-gate
309*0Sstevel@tonic-gate LOCK_PANIC_CHECK(lt);
310*0Sstevel@tonic-gate
311*0Sstevel@tonic-gate /* Validate arguments. */
312*0Sstevel@tonic-gate if ((ret = __db_fchk(lt->dbenv,
313*0Sstevel@tonic-gate "lock_get", flags, DB_LOCK_NOWAIT | DB_LOCK_UPGRADE)) != 0)
314*0Sstevel@tonic-gate return (ret);
315*0Sstevel@tonic-gate
316*0Sstevel@tonic-gate LOCK_LOCKREGION(lt);
317*0Sstevel@tonic-gate
318*0Sstevel@tonic-gate if ((ret = __lock_validate_region(lt)) == 0) {
319*0Sstevel@tonic-gate if (LF_ISSET(DB_LOCK_UPGRADE))
320*0Sstevel@tonic-gate lockp = OFFSET_TO_LOCK(lt, *lock);
321*0Sstevel@tonic-gate
322*0Sstevel@tonic-gate if ((ret = __lock_get_internal(lt,
323*0Sstevel@tonic-gate txn->txnid, txn, flags, obj, lock_mode, &lockp)) == 0) {
324*0Sstevel@tonic-gate if (!LF_ISSET(DB_LOCK_UPGRADE))
325*0Sstevel@tonic-gate *lock = LOCK_TO_OFFSET(lt, lockp);
326*0Sstevel@tonic-gate lt->region->nrequests++;
327*0Sstevel@tonic-gate }
328*0Sstevel@tonic-gate }
329*0Sstevel@tonic-gate
330*0Sstevel@tonic-gate UNLOCK_LOCKREGION(lt);
331*0Sstevel@tonic-gate return (ret);
332*0Sstevel@tonic-gate }
333*0Sstevel@tonic-gate int
lock_put(lt,lock)334*0Sstevel@tonic-gate lock_put(lt, lock)
335*0Sstevel@tonic-gate DB_LOCKTAB *lt;
336*0Sstevel@tonic-gate DB_LOCK lock;
337*0Sstevel@tonic-gate {
338*0Sstevel@tonic-gate struct __db_lock *lockp;
339*0Sstevel@tonic-gate int ret, run_dd;
340*0Sstevel@tonic-gate
341*0Sstevel@tonic-gate LOCK_PANIC_CHECK(lt);
342*0Sstevel@tonic-gate
343*0Sstevel@tonic-gate LOCK_LOCKREGION(lt);
344*0Sstevel@tonic-gate
345*0Sstevel@tonic-gate if ((ret = __lock_validate_region(lt)) != 0)
346*0Sstevel@tonic-gate return (ret);
347*0Sstevel@tonic-gate else {
348*0Sstevel@tonic-gate lockp = OFFSET_TO_LOCK(lt, lock);
349*0Sstevel@tonic-gate ret = __lock_put_internal(lt, lockp, 0);
350*0Sstevel@tonic-gate }
351*0Sstevel@tonic-gate
352*0Sstevel@tonic-gate __lock_checklocker(lt, lockp, 0);
353*0Sstevel@tonic-gate
354*0Sstevel@tonic-gate if (lt->region->need_dd && lt->region->detect != DB_LOCK_NORUN) {
355*0Sstevel@tonic-gate run_dd = 1;
356*0Sstevel@tonic-gate lt->region->need_dd = 0;
357*0Sstevel@tonic-gate } else
358*0Sstevel@tonic-gate run_dd = 0;
359*0Sstevel@tonic-gate
360*0Sstevel@tonic-gate UNLOCK_LOCKREGION(lt);
361*0Sstevel@tonic-gate
362*0Sstevel@tonic-gate if (ret == 0 && run_dd)
363*0Sstevel@tonic-gate lock_detect(lt, 0, lt->region->detect);
364*0Sstevel@tonic-gate
365*0Sstevel@tonic-gate return (ret);
366*0Sstevel@tonic-gate }
367*0Sstevel@tonic-gate
368*0Sstevel@tonic-gate static int
__lock_put_internal(lt,lockp,do_all)369*0Sstevel@tonic-gate __lock_put_internal(lt, lockp, do_all)
370*0Sstevel@tonic-gate DB_LOCKTAB *lt;
371*0Sstevel@tonic-gate struct __db_lock *lockp;
372*0Sstevel@tonic-gate int do_all;
373*0Sstevel@tonic-gate {
374*0Sstevel@tonic-gate DB_LOCKOBJ *sh_obj;
375*0Sstevel@tonic-gate int state_changed;
376*0Sstevel@tonic-gate
377*0Sstevel@tonic-gate if (lockp->refcount == 0 || (lockp->status != DB_LSTAT_HELD &&
378*0Sstevel@tonic-gate lockp->status != DB_LSTAT_WAITING) || lockp->obj == 0) {
379*0Sstevel@tonic-gate __db_err(lt->dbenv, "lock_put: invalid lock %lu",
380*0Sstevel@tonic-gate (u_long)((u_int8_t *)lockp - (u_int8_t *)lt->region));
381*0Sstevel@tonic-gate return (EINVAL);
382*0Sstevel@tonic-gate }
383*0Sstevel@tonic-gate
384*0Sstevel@tonic-gate if (do_all)
385*0Sstevel@tonic-gate lt->region->nreleases += lockp->refcount;
386*0Sstevel@tonic-gate else
387*0Sstevel@tonic-gate lt->region->nreleases++;
388*0Sstevel@tonic-gate if (do_all == 0 && lockp->refcount > 1) {
389*0Sstevel@tonic-gate lockp->refcount--;
390*0Sstevel@tonic-gate return (0);
391*0Sstevel@tonic-gate }
392*0Sstevel@tonic-gate
393*0Sstevel@tonic-gate /* Get the object associated with this lock. */
394*0Sstevel@tonic-gate sh_obj = (DB_LOCKOBJ *)((u_int8_t *)lockp + lockp->obj);
395*0Sstevel@tonic-gate
396*0Sstevel@tonic-gate /* Remove lock from locker list. */
397*0Sstevel@tonic-gate SH_LIST_REMOVE(lockp, locker_links, __db_lock);
398*0Sstevel@tonic-gate
399*0Sstevel@tonic-gate /* Remove this lock from its holders/waitlist. */
400*0Sstevel@tonic-gate if (lockp->status != DB_LSTAT_HELD)
401*0Sstevel@tonic-gate __lock_remove_waiter(lt, sh_obj, lockp, DB_LSTAT_FREE);
402*0Sstevel@tonic-gate else
403*0Sstevel@tonic-gate SH_TAILQ_REMOVE(&sh_obj->holders, lockp, links, __db_lock);
404*0Sstevel@tonic-gate
405*0Sstevel@tonic-gate state_changed = __lock_promote(lt, sh_obj);
406*0Sstevel@tonic-gate
407*0Sstevel@tonic-gate /* Check if object should be reclaimed. */
408*0Sstevel@tonic-gate if (SH_TAILQ_FIRST(&sh_obj->holders, __db_lock) == NULL) {
409*0Sstevel@tonic-gate HASHREMOVE_EL(lt->hashtab, __db_lockobj,
410*0Sstevel@tonic-gate links, sh_obj, lt->region->table_size, __lock_lhash);
411*0Sstevel@tonic-gate if (sh_obj->lockobj.size > sizeof(sh_obj->objdata))
412*0Sstevel@tonic-gate __db_shalloc_free(lt->mem,
413*0Sstevel@tonic-gate SH_DBT_PTR(&sh_obj->lockobj));
414*0Sstevel@tonic-gate SH_TAILQ_INSERT_HEAD(<->region->free_objs, sh_obj, links,
415*0Sstevel@tonic-gate __db_lockobj);
416*0Sstevel@tonic-gate state_changed = 1;
417*0Sstevel@tonic-gate }
418*0Sstevel@tonic-gate
419*0Sstevel@tonic-gate /* Free lock. */
420*0Sstevel@tonic-gate lockp->status = DB_LSTAT_FREE;
421*0Sstevel@tonic-gate SH_TAILQ_INSERT_HEAD(<->region->free_locks, lockp, links, __db_lock);
422*0Sstevel@tonic-gate
423*0Sstevel@tonic-gate /*
424*0Sstevel@tonic-gate * If we did not promote anyone; we need to run the deadlock
425*0Sstevel@tonic-gate * detector again.
426*0Sstevel@tonic-gate */
427*0Sstevel@tonic-gate if (state_changed == 0)
428*0Sstevel@tonic-gate lt->region->need_dd = 1;
429*0Sstevel@tonic-gate
430*0Sstevel@tonic-gate return (0);
431*0Sstevel@tonic-gate }
432*0Sstevel@tonic-gate
433*0Sstevel@tonic-gate static int
__lock_get_internal(lt,locker,txn,flags,obj,lock_mode,lockp)434*0Sstevel@tonic-gate __lock_get_internal(lt, locker, txn, flags, obj, lock_mode, lockp)
435*0Sstevel@tonic-gate DB_LOCKTAB *lt;
436*0Sstevel@tonic-gate u_int32_t locker, flags;
437*0Sstevel@tonic-gate DB_TXN *txn;
438*0Sstevel@tonic-gate const DBT *obj;
439*0Sstevel@tonic-gate db_lockmode_t lock_mode;
440*0Sstevel@tonic-gate struct __db_lock **lockp;
441*0Sstevel@tonic-gate {
442*0Sstevel@tonic-gate struct __db_lock *newl, *lp;
443*0Sstevel@tonic-gate DB_LOCKOBJ *sh_obj, *sh_locker;
444*0Sstevel@tonic-gate DB_LOCKREGION *lrp;
445*0Sstevel@tonic-gate size_t newl_off;
446*0Sstevel@tonic-gate int ihold, no_dd, ret;
447*0Sstevel@tonic-gate
448*0Sstevel@tonic-gate no_dd = ret = 0;
449*0Sstevel@tonic-gate
450*0Sstevel@tonic-gate /*
451*0Sstevel@tonic-gate * Check that lock mode is valid.
452*0Sstevel@tonic-gate */
453*0Sstevel@tonic-gate lrp = lt->region;
454*0Sstevel@tonic-gate if ((u_int32_t)lock_mode >= lrp->nmodes) {
455*0Sstevel@tonic-gate __db_err(lt->dbenv,
456*0Sstevel@tonic-gate "lock_get: invalid lock mode %lu\n", (u_long)lock_mode);
457*0Sstevel@tonic-gate return (EINVAL);
458*0Sstevel@tonic-gate }
459*0Sstevel@tonic-gate
460*0Sstevel@tonic-gate /* Allocate a new lock. Optimize for the common case of a grant. */
461*0Sstevel@tonic-gate if ((newl = SH_TAILQ_FIRST(&lrp->free_locks, __db_lock)) == NULL) {
462*0Sstevel@tonic-gate if ((ret = __lock_grow_region(lt, DB_LOCK_LOCK, 0)) != 0)
463*0Sstevel@tonic-gate return (ret);
464*0Sstevel@tonic-gate lrp = lt->region;
465*0Sstevel@tonic-gate newl = SH_TAILQ_FIRST(&lrp->free_locks, __db_lock);
466*0Sstevel@tonic-gate }
467*0Sstevel@tonic-gate newl_off = LOCK_TO_OFFSET(lt, newl);
468*0Sstevel@tonic-gate
469*0Sstevel@tonic-gate /* Optimize for common case of granting a lock. */
470*0Sstevel@tonic-gate SH_TAILQ_REMOVE(&lrp->free_locks, newl, links, __db_lock);
471*0Sstevel@tonic-gate
472*0Sstevel@tonic-gate newl->mode = lock_mode;
473*0Sstevel@tonic-gate newl->status = DB_LSTAT_HELD;
474*0Sstevel@tonic-gate newl->holder = locker;
475*0Sstevel@tonic-gate newl->refcount = 1;
476*0Sstevel@tonic-gate
477*0Sstevel@tonic-gate if ((ret = __lock_getobj(lt, 0, obj, DB_LOCK_OBJTYPE, &sh_obj)) != 0)
478*0Sstevel@tonic-gate return (ret);
479*0Sstevel@tonic-gate
480*0Sstevel@tonic-gate lrp = lt->region; /* getobj might have grown */
481*0Sstevel@tonic-gate newl = OFFSET_TO_LOCK(lt, newl_off);
482*0Sstevel@tonic-gate
483*0Sstevel@tonic-gate /* Now make new lock point to object */
484*0Sstevel@tonic-gate newl->obj = SH_PTR_TO_OFF(newl, sh_obj);
485*0Sstevel@tonic-gate
486*0Sstevel@tonic-gate /*
487*0Sstevel@tonic-gate * Now we have a lock and an object and we need to see if we should
488*0Sstevel@tonic-gate * grant the lock. We use a FIFO ordering so we can only grant a
489*0Sstevel@tonic-gate * new lock if it does not conflict with anyone on the holders list
490*0Sstevel@tonic-gate * OR anyone on the waiters list. The reason that we don't grant if
491*0Sstevel@tonic-gate * there's a conflict is that this can lead to starvation (a writer
492*0Sstevel@tonic-gate * waiting on a popularly read item will never be granted). The
493*0Sstevel@tonic-gate * downside of this is that a waiting reader can prevent an upgrade
494*0Sstevel@tonic-gate * from reader to writer, which is not uncommon.
495*0Sstevel@tonic-gate *
496*0Sstevel@tonic-gate * There is one exception to the no-conflict rule. If a lock is held
497*0Sstevel@tonic-gate * by the requesting locker AND the new lock does not conflict with
498*0Sstevel@tonic-gate * any other holders, then we grant the lock. The most common place
499*0Sstevel@tonic-gate * this happens is when the holder has a WRITE lock and a READ lock
500*0Sstevel@tonic-gate * request comes in for the same locker. If we do not grant the read
501*0Sstevel@tonic-gate * lock, then we guarantee deadlock.
502*0Sstevel@tonic-gate *
503*0Sstevel@tonic-gate * In case of conflict, we put the new lock on the end of the waiters
504*0Sstevel@tonic-gate * list, unless we are upgrading in which case the locker goes on the
505*0Sstevel@tonic-gate * front of the list.
506*0Sstevel@tonic-gate */
507*0Sstevel@tonic-gate ihold = 0;
508*0Sstevel@tonic-gate for (lp = SH_TAILQ_FIRST(&sh_obj->holders, __db_lock);
509*0Sstevel@tonic-gate lp != NULL;
510*0Sstevel@tonic-gate lp = SH_TAILQ_NEXT(lp, links, __db_lock)) {
511*0Sstevel@tonic-gate if (locker == lp->holder ||
512*0Sstevel@tonic-gate __lock_is_parent(lp->holder, txn)) {
513*0Sstevel@tonic-gate if (lp->mode == lock_mode &&
514*0Sstevel@tonic-gate lp->status == DB_LSTAT_HELD) {
515*0Sstevel@tonic-gate if (LF_ISSET(DB_LOCK_UPGRADE))
516*0Sstevel@tonic-gate goto upgrade;
517*0Sstevel@tonic-gate
518*0Sstevel@tonic-gate /*
519*0Sstevel@tonic-gate * Lock is held, so we can increment the
520*0Sstevel@tonic-gate * reference count and return this lock.
521*0Sstevel@tonic-gate */
522*0Sstevel@tonic-gate lp->refcount++;
523*0Sstevel@tonic-gate *lockp = lp;
524*0Sstevel@tonic-gate SH_TAILQ_INSERT_HEAD(&lrp->free_locks,
525*0Sstevel@tonic-gate newl, links, __db_lock);
526*0Sstevel@tonic-gate return (0);
527*0Sstevel@tonic-gate } else
528*0Sstevel@tonic-gate ihold = 1;
529*0Sstevel@tonic-gate } else if (CONFLICTS(lt, lp->mode, lock_mode))
530*0Sstevel@tonic-gate break;
531*0Sstevel@tonic-gate }
532*0Sstevel@tonic-gate
533*0Sstevel@tonic-gate /*
534*0Sstevel@tonic-gate * If we are upgrading, then there are two scenarios. Either
535*0Sstevel@tonic-gate * we had no conflicts, so we can do the upgrade. Or, there
536*0Sstevel@tonic-gate * is a conflict and we should wait at the HEAD of the waiters
537*0Sstevel@tonic-gate * list.
538*0Sstevel@tonic-gate */
539*0Sstevel@tonic-gate if (LF_ISSET(DB_LOCK_UPGRADE)) {
540*0Sstevel@tonic-gate if (lp == NULL)
541*0Sstevel@tonic-gate goto upgrade;
542*0Sstevel@tonic-gate
543*0Sstevel@tonic-gate /* There was a conflict, wait. */
544*0Sstevel@tonic-gate SH_TAILQ_INSERT_HEAD(&sh_obj->waiters, newl, links, __db_lock);
545*0Sstevel@tonic-gate goto wait;
546*0Sstevel@tonic-gate }
547*0Sstevel@tonic-gate
548*0Sstevel@tonic-gate if (lp == NULL && !ihold)
549*0Sstevel@tonic-gate for (lp = SH_TAILQ_FIRST(&sh_obj->waiters, __db_lock);
550*0Sstevel@tonic-gate lp != NULL;
551*0Sstevel@tonic-gate lp = SH_TAILQ_NEXT(lp, links, __db_lock)) {
552*0Sstevel@tonic-gate if (CONFLICTS(lt, lp->mode, lock_mode) &&
553*0Sstevel@tonic-gate locker != lp->holder)
554*0Sstevel@tonic-gate break;
555*0Sstevel@tonic-gate }
556*0Sstevel@tonic-gate if (lp == NULL)
557*0Sstevel@tonic-gate SH_TAILQ_INSERT_TAIL(&sh_obj->holders, newl, links);
558*0Sstevel@tonic-gate else if (!(flags & DB_LOCK_NOWAIT))
559*0Sstevel@tonic-gate SH_TAILQ_INSERT_TAIL(&sh_obj->waiters, newl, links);
560*0Sstevel@tonic-gate else {
561*0Sstevel@tonic-gate /* Free the lock and return an error. */
562*0Sstevel@tonic-gate newl->status = DB_LSTAT_FREE;
563*0Sstevel@tonic-gate SH_TAILQ_INSERT_HEAD(&lrp->free_locks, newl, links, __db_lock);
564*0Sstevel@tonic-gate return (DB_LOCK_NOTGRANTED);
565*0Sstevel@tonic-gate }
566*0Sstevel@tonic-gate
567*0Sstevel@tonic-gate /*
568*0Sstevel@tonic-gate * Now, insert the lock onto its locker's list. If the locker does
569*0Sstevel@tonic-gate * not currently hold any locks, there's no reason to run a deadlock
570*0Sstevel@tonic-gate * detector, save that information.
571*0Sstevel@tonic-gate */
572*0Sstevel@tonic-gate if ((ret =
573*0Sstevel@tonic-gate __lock_getobj(lt, locker, NULL, DB_LOCK_LOCKER, &sh_locker)) != 0)
574*0Sstevel@tonic-gate return (ret);
575*0Sstevel@tonic-gate no_dd = SH_LIST_FIRST(&sh_locker->heldby, __db_lock) == NULL;
576*0Sstevel@tonic-gate
577*0Sstevel@tonic-gate lrp = lt->region;
578*0Sstevel@tonic-gate SH_LIST_INSERT_HEAD(&sh_locker->heldby, newl, locker_links, __db_lock);
579*0Sstevel@tonic-gate
580*0Sstevel@tonic-gate if (lp != NULL) {
581*0Sstevel@tonic-gate /*
582*0Sstevel@tonic-gate * This is really a blocker for the process, so initialize it
583*0Sstevel@tonic-gate * set. That way the current process will block when it tries
584*0Sstevel@tonic-gate * to get it and the waking process will release it.
585*0Sstevel@tonic-gate */
586*0Sstevel@tonic-gate wait: (void)__db_mutex_init(&newl->mutex,
587*0Sstevel@tonic-gate MUTEX_LOCK_OFFSET(lt->region, &newl->mutex));
588*0Sstevel@tonic-gate (void)__db_mutex_lock(&newl->mutex, lt->reginfo.fd);
589*0Sstevel@tonic-gate
590*0Sstevel@tonic-gate newl->status = DB_LSTAT_WAITING;
591*0Sstevel@tonic-gate lrp->nconflicts++;
592*0Sstevel@tonic-gate
593*0Sstevel@tonic-gate /*
594*0Sstevel@tonic-gate * We are about to wait; must release the region mutex. Then,
595*0Sstevel@tonic-gate * when we wakeup, we need to reacquire the region mutex before
596*0Sstevel@tonic-gate * continuing.
597*0Sstevel@tonic-gate */
598*0Sstevel@tonic-gate if (lrp->detect == DB_LOCK_NORUN)
599*0Sstevel@tonic-gate lt->region->need_dd = 1;
600*0Sstevel@tonic-gate UNLOCK_LOCKREGION(lt);
601*0Sstevel@tonic-gate
602*0Sstevel@tonic-gate /*
603*0Sstevel@tonic-gate * We are about to wait; before waiting, see if the deadlock
604*0Sstevel@tonic-gate * detector should be run.
605*0Sstevel@tonic-gate */
606*0Sstevel@tonic-gate if (lrp->detect != DB_LOCK_NORUN && !no_dd)
607*0Sstevel@tonic-gate (void)lock_detect(lt, 0, lrp->detect);
608*0Sstevel@tonic-gate
609*0Sstevel@tonic-gate (void)__db_mutex_lock(&newl->mutex, lt->reginfo.fd);
610*0Sstevel@tonic-gate
611*0Sstevel@tonic-gate LOCK_LOCKREGION(lt);
612*0Sstevel@tonic-gate if (newl->status != DB_LSTAT_PENDING) {
613*0Sstevel@tonic-gate /*
614*0Sstevel@tonic-gate * If this lock errored due to a deadlock, then
615*0Sstevel@tonic-gate * we have waiters that require promotion.
616*0Sstevel@tonic-gate */
617*0Sstevel@tonic-gate if (newl->status == DB_LSTAT_ABORTED)
618*0Sstevel@tonic-gate (void)__lock_promote(lt, sh_obj);
619*0Sstevel@tonic-gate /* Return to free list. */
620*0Sstevel@tonic-gate __lock_checklocker(lt, newl, 0);
621*0Sstevel@tonic-gate SH_TAILQ_INSERT_HEAD(&lrp->free_locks, newl, links,
622*0Sstevel@tonic-gate __db_lock);
623*0Sstevel@tonic-gate switch (newl->status) {
624*0Sstevel@tonic-gate case DB_LSTAT_ABORTED:
625*0Sstevel@tonic-gate ret = DB_LOCK_DEADLOCK;
626*0Sstevel@tonic-gate break;
627*0Sstevel@tonic-gate case DB_LSTAT_NOGRANT:
628*0Sstevel@tonic-gate ret = DB_LOCK_NOTGRANTED;
629*0Sstevel@tonic-gate break;
630*0Sstevel@tonic-gate default:
631*0Sstevel@tonic-gate ret = EINVAL;
632*0Sstevel@tonic-gate break;
633*0Sstevel@tonic-gate }
634*0Sstevel@tonic-gate newl->status = DB_LSTAT_FREE;
635*0Sstevel@tonic-gate newl = NULL;
636*0Sstevel@tonic-gate } else if (LF_ISSET(DB_LOCK_UPGRADE)) {
637*0Sstevel@tonic-gate /*
638*0Sstevel@tonic-gate * The lock that was just granted got put on the
639*0Sstevel@tonic-gate * holders list. Since we're upgrading some other
640*0Sstevel@tonic-gate * lock, we've got to remove it here.
641*0Sstevel@tonic-gate */
642*0Sstevel@tonic-gate SH_TAILQ_REMOVE(&sh_obj->holders,
643*0Sstevel@tonic-gate newl, links, __db_lock);
644*0Sstevel@tonic-gate goto upgrade;
645*0Sstevel@tonic-gate } else
646*0Sstevel@tonic-gate newl->status = DB_LSTAT_HELD;
647*0Sstevel@tonic-gate }
648*0Sstevel@tonic-gate
649*0Sstevel@tonic-gate *lockp = newl;
650*0Sstevel@tonic-gate return (ret);
651*0Sstevel@tonic-gate
652*0Sstevel@tonic-gate upgrade:
653*0Sstevel@tonic-gate /*
654*0Sstevel@tonic-gate * This was an upgrade, so return the new lock to the free list and
655*0Sstevel@tonic-gate * upgrade the mode.
656*0Sstevel@tonic-gate */
657*0Sstevel@tonic-gate (*lockp)->mode = lock_mode;
658*0Sstevel@tonic-gate newl->status = DB_LSTAT_FREE;
659*0Sstevel@tonic-gate SH_TAILQ_INSERT_HEAD(&lrp->free_locks, newl, links, __db_lock);
660*0Sstevel@tonic-gate return (0);
661*0Sstevel@tonic-gate }
662*0Sstevel@tonic-gate
663*0Sstevel@tonic-gate /*
664*0Sstevel@tonic-gate * __lock_is_locked --
665*0Sstevel@tonic-gate *
666*0Sstevel@tonic-gate * PUBLIC: int __lock_is_locked
667*0Sstevel@tonic-gate * PUBLIC: __P((DB_LOCKTAB *, u_int32_t, DBT *, db_lockmode_t));
668*0Sstevel@tonic-gate */
669*0Sstevel@tonic-gate int
__lock_is_locked(lt,locker,dbt,mode)670*0Sstevel@tonic-gate __lock_is_locked(lt, locker, dbt, mode)
671*0Sstevel@tonic-gate DB_LOCKTAB *lt;
672*0Sstevel@tonic-gate u_int32_t locker;
673*0Sstevel@tonic-gate DBT *dbt;
674*0Sstevel@tonic-gate db_lockmode_t mode;
675*0Sstevel@tonic-gate {
676*0Sstevel@tonic-gate struct __db_lock *lp;
677*0Sstevel@tonic-gate DB_LOCKOBJ *sh_obj;
678*0Sstevel@tonic-gate DB_LOCKREGION *lrp;
679*0Sstevel@tonic-gate
680*0Sstevel@tonic-gate lrp = lt->region;
681*0Sstevel@tonic-gate
682*0Sstevel@tonic-gate /* Look up the object in the hash table. */
683*0Sstevel@tonic-gate HASHLOOKUP(lt->hashtab, __db_lockobj, links,
684*0Sstevel@tonic-gate dbt, sh_obj, lrp->table_size, __lock_ohash, __lock_cmp);
685*0Sstevel@tonic-gate if (sh_obj == NULL)
686*0Sstevel@tonic-gate return (0);
687*0Sstevel@tonic-gate
688*0Sstevel@tonic-gate for (lp = SH_TAILQ_FIRST(&sh_obj->holders, __db_lock);
689*0Sstevel@tonic-gate lp != NULL;
690*0Sstevel@tonic-gate lp = SH_TAILQ_FIRST(&sh_obj->holders, __db_lock)) {
691*0Sstevel@tonic-gate if (lp->holder == locker && lp->mode == mode)
692*0Sstevel@tonic-gate return (1);
693*0Sstevel@tonic-gate }
694*0Sstevel@tonic-gate
695*0Sstevel@tonic-gate return (0);
696*0Sstevel@tonic-gate }
697*0Sstevel@tonic-gate
698*0Sstevel@tonic-gate /*
699*0Sstevel@tonic-gate * __lock_printlock --
700*0Sstevel@tonic-gate *
701*0Sstevel@tonic-gate * PUBLIC: void __lock_printlock __P((DB_LOCKTAB *, struct __db_lock *, int));
702*0Sstevel@tonic-gate */
703*0Sstevel@tonic-gate void
__lock_printlock(lt,lp,ispgno)704*0Sstevel@tonic-gate __lock_printlock(lt, lp, ispgno)
705*0Sstevel@tonic-gate DB_LOCKTAB *lt;
706*0Sstevel@tonic-gate struct __db_lock *lp;
707*0Sstevel@tonic-gate int ispgno;
708*0Sstevel@tonic-gate {
709*0Sstevel@tonic-gate DB_LOCKOBJ *lockobj;
710*0Sstevel@tonic-gate db_pgno_t pgno;
711*0Sstevel@tonic-gate size_t obj;
712*0Sstevel@tonic-gate u_int8_t *ptr;
713*0Sstevel@tonic-gate const char *mode, *status;
714*0Sstevel@tonic-gate
715*0Sstevel@tonic-gate switch (lp->mode) {
716*0Sstevel@tonic-gate case DB_LOCK_IREAD:
717*0Sstevel@tonic-gate mode = "IREAD";
718*0Sstevel@tonic-gate break;
719*0Sstevel@tonic-gate case DB_LOCK_IWR:
720*0Sstevel@tonic-gate mode = "IWR";
721*0Sstevel@tonic-gate break;
722*0Sstevel@tonic-gate case DB_LOCK_IWRITE:
723*0Sstevel@tonic-gate mode = "IWRITE";
724*0Sstevel@tonic-gate break;
725*0Sstevel@tonic-gate case DB_LOCK_NG:
726*0Sstevel@tonic-gate mode = "NG";
727*0Sstevel@tonic-gate break;
728*0Sstevel@tonic-gate case DB_LOCK_READ:
729*0Sstevel@tonic-gate mode = "READ";
730*0Sstevel@tonic-gate break;
731*0Sstevel@tonic-gate case DB_LOCK_WRITE:
732*0Sstevel@tonic-gate mode = "WRITE";
733*0Sstevel@tonic-gate break;
734*0Sstevel@tonic-gate default:
735*0Sstevel@tonic-gate mode = "UNKNOWN";
736*0Sstevel@tonic-gate break;
737*0Sstevel@tonic-gate }
738*0Sstevel@tonic-gate switch (lp->status) {
739*0Sstevel@tonic-gate case DB_LSTAT_ABORTED:
740*0Sstevel@tonic-gate status = "ABORT";
741*0Sstevel@tonic-gate break;
742*0Sstevel@tonic-gate case DB_LSTAT_ERR:
743*0Sstevel@tonic-gate status = "ERROR";
744*0Sstevel@tonic-gate break;
745*0Sstevel@tonic-gate case DB_LSTAT_FREE:
746*0Sstevel@tonic-gate status = "FREE";
747*0Sstevel@tonic-gate break;
748*0Sstevel@tonic-gate case DB_LSTAT_HELD:
749*0Sstevel@tonic-gate status = "HELD";
750*0Sstevel@tonic-gate break;
751*0Sstevel@tonic-gate case DB_LSTAT_NOGRANT:
752*0Sstevel@tonic-gate status = "NONE";
753*0Sstevel@tonic-gate break;
754*0Sstevel@tonic-gate case DB_LSTAT_WAITING:
755*0Sstevel@tonic-gate status = "WAIT";
756*0Sstevel@tonic-gate break;
757*0Sstevel@tonic-gate case DB_LSTAT_PENDING:
758*0Sstevel@tonic-gate status = "PENDING";
759*0Sstevel@tonic-gate break;
760*0Sstevel@tonic-gate default:
761*0Sstevel@tonic-gate status = "UNKNOWN";
762*0Sstevel@tonic-gate break;
763*0Sstevel@tonic-gate }
764*0Sstevel@tonic-gate printf("\t%lx\t%s\t%lu\t%s\t",
765*0Sstevel@tonic-gate (u_long)lp->holder, mode, (u_long)lp->refcount, status);
766*0Sstevel@tonic-gate
767*0Sstevel@tonic-gate lockobj = (DB_LOCKOBJ *)((u_int8_t *)lp + lp->obj);
768*0Sstevel@tonic-gate ptr = SH_DBT_PTR(&lockobj->lockobj);
769*0Sstevel@tonic-gate if (ispgno) {
770*0Sstevel@tonic-gate /* Assume this is a DBT lock. */
771*0Sstevel@tonic-gate memcpy(&pgno, ptr, sizeof(db_pgno_t));
772*0Sstevel@tonic-gate printf("page %lu\n", (u_long)pgno);
773*0Sstevel@tonic-gate } else {
774*0Sstevel@tonic-gate obj = (u_int8_t *)lp + lp->obj - (u_int8_t *)lt->region;
775*0Sstevel@tonic-gate printf("0x%lx ", (u_long)obj);
776*0Sstevel@tonic-gate __db_pr(ptr, lockobj->lockobj.size);
777*0Sstevel@tonic-gate printf("\n");
778*0Sstevel@tonic-gate }
779*0Sstevel@tonic-gate }
780*0Sstevel@tonic-gate
781*0Sstevel@tonic-gate /*
782*0Sstevel@tonic-gate * PUBLIC: int __lock_getobj __P((DB_LOCKTAB *,
783*0Sstevel@tonic-gate * PUBLIC: u_int32_t, const DBT *, u_int32_t type, DB_LOCKOBJ **));
784*0Sstevel@tonic-gate */
785*0Sstevel@tonic-gate int
__lock_getobj(lt,locker,dbt,type,objp)786*0Sstevel@tonic-gate __lock_getobj(lt, locker, dbt, type, objp)
787*0Sstevel@tonic-gate DB_LOCKTAB *lt;
788*0Sstevel@tonic-gate u_int32_t locker, type;
789*0Sstevel@tonic-gate const DBT *dbt;
790*0Sstevel@tonic-gate DB_LOCKOBJ **objp;
791*0Sstevel@tonic-gate {
792*0Sstevel@tonic-gate DB_LOCKREGION *lrp;
793*0Sstevel@tonic-gate DB_LOCKOBJ *sh_obj;
794*0Sstevel@tonic-gate u_int32_t obj_size;
795*0Sstevel@tonic-gate int ret;
796*0Sstevel@tonic-gate void *p, *src;
797*0Sstevel@tonic-gate
798*0Sstevel@tonic-gate lrp = lt->region;
799*0Sstevel@tonic-gate
800*0Sstevel@tonic-gate /* Look up the object in the hash table. */
801*0Sstevel@tonic-gate if (type == DB_LOCK_OBJTYPE) {
802*0Sstevel@tonic-gate HASHLOOKUP(lt->hashtab, __db_lockobj, links, dbt, sh_obj,
803*0Sstevel@tonic-gate lrp->table_size, __lock_ohash, __lock_cmp);
804*0Sstevel@tonic-gate obj_size = dbt->size;
805*0Sstevel@tonic-gate } else {
806*0Sstevel@tonic-gate HASHLOOKUP(lt->hashtab, __db_lockobj, links, locker,
807*0Sstevel@tonic-gate sh_obj, lrp->table_size, __lock_locker_hash,
808*0Sstevel@tonic-gate __lock_locker_cmp);
809*0Sstevel@tonic-gate obj_size = sizeof(locker);
810*0Sstevel@tonic-gate }
811*0Sstevel@tonic-gate
812*0Sstevel@tonic-gate /*
813*0Sstevel@tonic-gate * If we found the object, then we can just return it. If
814*0Sstevel@tonic-gate * we didn't find the object, then we need to create it.
815*0Sstevel@tonic-gate */
816*0Sstevel@tonic-gate if (sh_obj == NULL) {
817*0Sstevel@tonic-gate /* Create new object and then insert it into hash table. */
818*0Sstevel@tonic-gate if ((sh_obj =
819*0Sstevel@tonic-gate SH_TAILQ_FIRST(&lrp->free_objs, __db_lockobj)) == NULL) {
820*0Sstevel@tonic-gate if ((ret = __lock_grow_region(lt, DB_LOCK_OBJ, 0)) != 0)
821*0Sstevel@tonic-gate return (ret);
822*0Sstevel@tonic-gate lrp = lt->region;
823*0Sstevel@tonic-gate sh_obj = SH_TAILQ_FIRST(&lrp->free_objs, __db_lockobj);
824*0Sstevel@tonic-gate }
825*0Sstevel@tonic-gate
826*0Sstevel@tonic-gate /*
827*0Sstevel@tonic-gate * If we can fit this object in the structure, do so instead
828*0Sstevel@tonic-gate * of shalloc-ing space for it.
829*0Sstevel@tonic-gate */
830*0Sstevel@tonic-gate if (obj_size <= sizeof(sh_obj->objdata))
831*0Sstevel@tonic-gate p = sh_obj->objdata;
832*0Sstevel@tonic-gate else
833*0Sstevel@tonic-gate if ((ret =
834*0Sstevel@tonic-gate __db_shalloc(lt->mem, obj_size, 0, &p)) != 0) {
835*0Sstevel@tonic-gate if ((ret = __lock_grow_region(lt,
836*0Sstevel@tonic-gate DB_LOCK_MEM, obj_size)) != 0)
837*0Sstevel@tonic-gate return (ret);
838*0Sstevel@tonic-gate lrp = lt->region;
839*0Sstevel@tonic-gate /* Reacquire the head of the list. */
840*0Sstevel@tonic-gate sh_obj = SH_TAILQ_FIRST(&lrp->free_objs,
841*0Sstevel@tonic-gate __db_lockobj);
842*0Sstevel@tonic-gate (void)__db_shalloc(lt->mem, obj_size, 0, &p);
843*0Sstevel@tonic-gate }
844*0Sstevel@tonic-gate
845*0Sstevel@tonic-gate src = type == DB_LOCK_OBJTYPE ? dbt->data : (void *)&locker;
846*0Sstevel@tonic-gate memcpy(p, src, obj_size);
847*0Sstevel@tonic-gate
848*0Sstevel@tonic-gate sh_obj->type = type;
849*0Sstevel@tonic-gate SH_TAILQ_REMOVE(&lrp->free_objs, sh_obj, links, __db_lockobj);
850*0Sstevel@tonic-gate
851*0Sstevel@tonic-gate SH_TAILQ_INIT(&sh_obj->waiters);
852*0Sstevel@tonic-gate if (type == DB_LOCK_LOCKER)
853*0Sstevel@tonic-gate SH_LIST_INIT(&sh_obj->heldby);
854*0Sstevel@tonic-gate else
855*0Sstevel@tonic-gate SH_TAILQ_INIT(&sh_obj->holders);
856*0Sstevel@tonic-gate sh_obj->lockobj.size = obj_size;
857*0Sstevel@tonic-gate sh_obj->lockobj.off = SH_PTR_TO_OFF(&sh_obj->lockobj, p);
858*0Sstevel@tonic-gate
859*0Sstevel@tonic-gate HASHINSERT(lt->hashtab,
860*0Sstevel@tonic-gate __db_lockobj, links, sh_obj, lrp->table_size, __lock_lhash);
861*0Sstevel@tonic-gate
862*0Sstevel@tonic-gate if (type == DB_LOCK_LOCKER)
863*0Sstevel@tonic-gate lrp->nlockers++;
864*0Sstevel@tonic-gate }
865*0Sstevel@tonic-gate
866*0Sstevel@tonic-gate *objp = sh_obj;
867*0Sstevel@tonic-gate return (0);
868*0Sstevel@tonic-gate }
869*0Sstevel@tonic-gate
870*0Sstevel@tonic-gate /*
871*0Sstevel@tonic-gate * Any lock on the waitlist has a process waiting for it. Therefore, we
872*0Sstevel@tonic-gate * can't return the lock to the freelist immediately. Instead, we can
873*0Sstevel@tonic-gate * remove the lock from the list of waiters, set the status field of the
874*0Sstevel@tonic-gate * lock, and then let the process waking up return the lock to the
875*0Sstevel@tonic-gate * free list.
876*0Sstevel@tonic-gate */
877*0Sstevel@tonic-gate static void
__lock_remove_waiter(lt,sh_obj,lockp,status)878*0Sstevel@tonic-gate __lock_remove_waiter(lt, sh_obj, lockp, status)
879*0Sstevel@tonic-gate DB_LOCKTAB *lt;
880*0Sstevel@tonic-gate DB_LOCKOBJ *sh_obj;
881*0Sstevel@tonic-gate struct __db_lock *lockp;
882*0Sstevel@tonic-gate db_status_t status;
883*0Sstevel@tonic-gate {
884*0Sstevel@tonic-gate SH_TAILQ_REMOVE(&sh_obj->waiters, lockp, links, __db_lock);
885*0Sstevel@tonic-gate lockp->status = status;
886*0Sstevel@tonic-gate
887*0Sstevel@tonic-gate /* Wake whoever is waiting on this lock. */
888*0Sstevel@tonic-gate (void)__db_mutex_unlock(&lockp->mutex, lt->reginfo.fd);
889*0Sstevel@tonic-gate }
890*0Sstevel@tonic-gate
891*0Sstevel@tonic-gate static void
__lock_checklocker(lt,lockp,do_remove)892*0Sstevel@tonic-gate __lock_checklocker(lt, lockp, do_remove)
893*0Sstevel@tonic-gate DB_LOCKTAB *lt;
894*0Sstevel@tonic-gate struct __db_lock *lockp;
895*0Sstevel@tonic-gate int do_remove;
896*0Sstevel@tonic-gate {
897*0Sstevel@tonic-gate DB_LOCKOBJ *sh_locker;
898*0Sstevel@tonic-gate
899*0Sstevel@tonic-gate if (do_remove)
900*0Sstevel@tonic-gate SH_LIST_REMOVE(lockp, locker_links, __db_lock);
901*0Sstevel@tonic-gate
902*0Sstevel@tonic-gate /* if the locker list is NULL, free up the object. */
903*0Sstevel@tonic-gate if (__lock_getobj(lt, lockp->holder, NULL, DB_LOCK_LOCKER, &sh_locker)
904*0Sstevel@tonic-gate == 0 && SH_LIST_FIRST(&sh_locker->heldby, __db_lock) == NULL) {
905*0Sstevel@tonic-gate __lock_freeobj(lt, sh_locker);
906*0Sstevel@tonic-gate lt->region->nlockers--;
907*0Sstevel@tonic-gate }
908*0Sstevel@tonic-gate }
909*0Sstevel@tonic-gate
910*0Sstevel@tonic-gate static void
__lock_freeobj(lt,obj)911*0Sstevel@tonic-gate __lock_freeobj(lt, obj)
912*0Sstevel@tonic-gate DB_LOCKTAB *lt;
913*0Sstevel@tonic-gate DB_LOCKOBJ *obj;
914*0Sstevel@tonic-gate {
915*0Sstevel@tonic-gate HASHREMOVE_EL(lt->hashtab,
916*0Sstevel@tonic-gate __db_lockobj, links, obj, lt->region->table_size, __lock_lhash);
917*0Sstevel@tonic-gate if (obj->lockobj.size > sizeof(obj->objdata))
918*0Sstevel@tonic-gate __db_shalloc_free(lt->mem, SH_DBT_PTR(&obj->lockobj));
919*0Sstevel@tonic-gate SH_TAILQ_INSERT_HEAD(<->region->free_objs, obj, links, __db_lockobj);
920*0Sstevel@tonic-gate }
921*0Sstevel@tonic-gate
922*0Sstevel@tonic-gate /*
923*0Sstevel@tonic-gate * __lock_downgrade --
924*0Sstevel@tonic-gate * Used by the concurrent access product to downgrade write locks
925*0Sstevel@tonic-gate * back to iwrite locks.
926*0Sstevel@tonic-gate *
927*0Sstevel@tonic-gate * PUBLIC: int __lock_downgrade __P((DB_LOCKTAB *,
928*0Sstevel@tonic-gate * PUBLIC: DB_LOCK, db_lockmode_t, u_int32_t));
929*0Sstevel@tonic-gate */
930*0Sstevel@tonic-gate int
__lock_downgrade(lt,lock,new_mode,flags)931*0Sstevel@tonic-gate __lock_downgrade(lt, lock, new_mode, flags)
932*0Sstevel@tonic-gate DB_LOCKTAB *lt;
933*0Sstevel@tonic-gate DB_LOCK lock;
934*0Sstevel@tonic-gate db_lockmode_t new_mode;
935*0Sstevel@tonic-gate u_int32_t flags;
936*0Sstevel@tonic-gate {
937*0Sstevel@tonic-gate struct __db_lock *lockp;
938*0Sstevel@tonic-gate DB_LOCKOBJ *obj;
939*0Sstevel@tonic-gate int ret;
940*0Sstevel@tonic-gate
941*0Sstevel@tonic-gate COMPQUIET(flags, 0);
942*0Sstevel@tonic-gate LOCK_PANIC_CHECK(lt);
943*0Sstevel@tonic-gate LOCK_LOCKREGION(lt);
944*0Sstevel@tonic-gate
945*0Sstevel@tonic-gate if ((ret = __lock_validate_region(lt)) == 0) {
946*0Sstevel@tonic-gate lockp = OFFSET_TO_LOCK(lt, lock);
947*0Sstevel@tonic-gate lockp->mode = new_mode;
948*0Sstevel@tonic-gate
949*0Sstevel@tonic-gate /* Get the object associated with this lock. */
950*0Sstevel@tonic-gate obj = (DB_LOCKOBJ *)((u_int8_t *)lockp + lockp->obj);
951*0Sstevel@tonic-gate (void)__lock_promote(lt, obj);
952*0Sstevel@tonic-gate ++lt->region->nreleases;
953*0Sstevel@tonic-gate }
954*0Sstevel@tonic-gate
955*0Sstevel@tonic-gate UNLOCK_LOCKREGION(lt);
956*0Sstevel@tonic-gate
957*0Sstevel@tonic-gate return (ret);
958*0Sstevel@tonic-gate }
959*0Sstevel@tonic-gate
960*0Sstevel@tonic-gate /*
961*0Sstevel@tonic-gate * __lock_promote --
962*0Sstevel@tonic-gate *
963*0Sstevel@tonic-gate * Look through the waiters and holders lists and decide which (if any)
964*0Sstevel@tonic-gate * locks can be promoted. Promote any that are eligible.
965*0Sstevel@tonic-gate */
966*0Sstevel@tonic-gate static int
__lock_promote(lt,obj)967*0Sstevel@tonic-gate __lock_promote(lt, obj)
968*0Sstevel@tonic-gate DB_LOCKTAB *lt;
969*0Sstevel@tonic-gate DB_LOCKOBJ *obj;
970*0Sstevel@tonic-gate {
971*0Sstevel@tonic-gate struct __db_lock *lp_w, *lp_h, *next_waiter;
972*0Sstevel@tonic-gate int state_changed, waiter_is_txn;
973*0Sstevel@tonic-gate
974*0Sstevel@tonic-gate /*
975*0Sstevel@tonic-gate * We need to do lock promotion. We also need to determine if
976*0Sstevel@tonic-gate * we're going to need to run the deadlock detector again. If
977*0Sstevel@tonic-gate * we release locks, and there are waiters, but no one gets promoted,
978*0Sstevel@tonic-gate * then we haven't fundamentally changed the lockmgr state, so
979*0Sstevel@tonic-gate * we may still have a deadlock and we have to run again. However,
980*0Sstevel@tonic-gate * if there were no waiters, or we actually promoted someone, then
981*0Sstevel@tonic-gate * we are OK and we don't have to run it immediately.
982*0Sstevel@tonic-gate *
983*0Sstevel@tonic-gate * During promotion, we look for state changes so we can return
984*0Sstevel@tonic-gate * this information to the caller.
985*0Sstevel@tonic-gate */
986*0Sstevel@tonic-gate for (lp_w = SH_TAILQ_FIRST(&obj->waiters, __db_lock),
987*0Sstevel@tonic-gate state_changed = lp_w == NULL;
988*0Sstevel@tonic-gate lp_w != NULL;
989*0Sstevel@tonic-gate lp_w = next_waiter) {
990*0Sstevel@tonic-gate waiter_is_txn = TXN_IS_HOLDING(lp_w);
991*0Sstevel@tonic-gate next_waiter = SH_TAILQ_NEXT(lp_w, links, __db_lock);
992*0Sstevel@tonic-gate for (lp_h = SH_TAILQ_FIRST(&obj->holders, __db_lock);
993*0Sstevel@tonic-gate lp_h != NULL;
994*0Sstevel@tonic-gate lp_h = SH_TAILQ_NEXT(lp_h, links, __db_lock)) {
995*0Sstevel@tonic-gate if (CONFLICTS(lt, lp_h->mode, lp_w->mode) &&
996*0Sstevel@tonic-gate lp_h->holder != lp_w->holder &&
997*0Sstevel@tonic-gate !(waiter_is_txn &&
998*0Sstevel@tonic-gate TXN_IS_HOLDING(lp_h) &&
999*0Sstevel@tonic-gate __txn_is_ancestor(lt->dbenv->tx_info,
1000*0Sstevel@tonic-gate lp_h->txnoff, lp_w->txnoff)))
1001*0Sstevel@tonic-gate break;
1002*0Sstevel@tonic-gate }
1003*0Sstevel@tonic-gate if (lp_h != NULL) /* Found a conflict. */
1004*0Sstevel@tonic-gate break;
1005*0Sstevel@tonic-gate
1006*0Sstevel@tonic-gate /* No conflict, promote the waiting lock. */
1007*0Sstevel@tonic-gate SH_TAILQ_REMOVE(&obj->waiters, lp_w, links, __db_lock);
1008*0Sstevel@tonic-gate lp_w->status = DB_LSTAT_PENDING;
1009*0Sstevel@tonic-gate SH_TAILQ_INSERT_TAIL(&obj->holders, lp_w, links);
1010*0Sstevel@tonic-gate
1011*0Sstevel@tonic-gate /* Wake up waiter. */
1012*0Sstevel@tonic-gate (void)__db_mutex_unlock(&lp_w->mutex, lt->reginfo.fd);
1013*0Sstevel@tonic-gate state_changed = 1;
1014*0Sstevel@tonic-gate }
1015*0Sstevel@tonic-gate
1016*0Sstevel@tonic-gate return (state_changed);
1017*0Sstevel@tonic-gate }
1018*0Sstevel@tonic-gate
1019*0Sstevel@tonic-gate static int
__lock_is_parent(locker,txn)1020*0Sstevel@tonic-gate __lock_is_parent(locker, txn)
1021*0Sstevel@tonic-gate u_int32_t locker;
1022*0Sstevel@tonic-gate DB_TXN *txn;
1023*0Sstevel@tonic-gate {
1024*0Sstevel@tonic-gate DB_TXN *t;
1025*0Sstevel@tonic-gate
1026*0Sstevel@tonic-gate if (txn == NULL)
1027*0Sstevel@tonic-gate return (0);
1028*0Sstevel@tonic-gate
1029*0Sstevel@tonic-gate for (t = txn->parent; t != NULL; t = t->parent)
1030*0Sstevel@tonic-gate if (t->txnid == locker)
1031*0Sstevel@tonic-gate return (1);
1032*0Sstevel@tonic-gate
1033*0Sstevel@tonic-gate return (0);
1034*0Sstevel@tonic-gate }
1035