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