1/*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 1996-2009 Oracle. All rights reserved. 5 * 6 * $Id$ 7 */ 8 9#include "db_config.h" 10 11#include "db_int.h" 12#include "dbinc/lock.h" 13#include "dbinc/log.h" 14 15/* 16 * __lock_id_pp -- 17 * ENV->lock_id pre/post processing. 18 * 19 * PUBLIC: int __lock_id_pp __P((DB_ENV *, u_int32_t *)); 20 */ 21int 22__lock_id_pp(dbenv, idp) 23 DB_ENV *dbenv; 24 u_int32_t *idp; 25{ 26 DB_THREAD_INFO *ip; 27 ENV *env; 28 int ret; 29 30 env = dbenv->env; 31 32 ENV_REQUIRES_CONFIG(env, 33 env->lk_handle, "DB_ENV->lock_id", DB_INIT_LOCK); 34 35 ENV_ENTER(env, ip); 36 REPLICATION_WRAP(env, (__lock_id(env, idp, NULL)), 0, ret); 37 ENV_LEAVE(env, ip); 38 return (ret); 39} 40 41/* 42 * __lock_id -- 43 * ENV->lock_id. 44 * 45 * PUBLIC: int __lock_id __P((ENV *, u_int32_t *, DB_LOCKER **)); 46 */ 47int 48__lock_id(env, idp, lkp) 49 ENV *env; 50 u_int32_t *idp; 51 DB_LOCKER **lkp; 52{ 53 DB_LOCKER *lk; 54 DB_LOCKREGION *region; 55 DB_LOCKTAB *lt; 56 u_int32_t id, *ids; 57 int nids, ret; 58 59 lk = NULL; 60 lt = env->lk_handle; 61 region = lt->reginfo.primary; 62 id = DB_LOCK_INVALIDID; 63 ret = 0; 64 65 id = DB_LOCK_INVALIDID; 66 lk = NULL; 67 68 LOCK_LOCKERS(env, region); 69 70 /* 71 * Allocate a new lock id. If we wrap around then we find the minimum 72 * currently in use and make sure we can stay below that. This code is 73 * similar to code in __txn_begin_int for recovering txn ids. 74 * 75 * Our current valid range can span the maximum valid value, so check 76 * for it and wrap manually. 77 */ 78 if (region->lock_id == DB_LOCK_MAXID && 79 region->cur_maxid != DB_LOCK_MAXID) 80 region->lock_id = DB_LOCK_INVALIDID; 81 if (region->lock_id == region->cur_maxid) { 82 if ((ret = __os_malloc(env, 83 sizeof(u_int32_t) * region->nlockers, &ids)) != 0) 84 goto err; 85 nids = 0; 86 SH_TAILQ_FOREACH(lk, ®ion->lockers, ulinks, __db_locker) 87 ids[nids++] = lk->id; 88 region->lock_id = DB_LOCK_INVALIDID; 89 region->cur_maxid = DB_LOCK_MAXID; 90 if (nids != 0) 91 __db_idspace(ids, nids, 92 ®ion->lock_id, ®ion->cur_maxid); 93 __os_free(env, ids); 94 } 95 id = ++region->lock_id; 96 97 /* Allocate a locker for this id. */ 98 ret = __lock_getlocker_int(lt, id, 1, &lk); 99 100err: UNLOCK_LOCKERS(env, region); 101 102 if (idp != NULL) 103 *idp = id; 104 if (lkp != NULL) 105 *lkp = lk; 106 107 return (ret); 108} 109 110/* 111 * __lock_set_thread_id -- 112 * Set the thread_id in an existing locker. 113 * PUBLIC: void __lock_set_thread_id __P((void *, pid_t, db_threadid_t)); 114 */ 115void 116__lock_set_thread_id(lref_arg, pid, tid) 117 void *lref_arg; 118 pid_t pid; 119 db_threadid_t tid; 120{ 121 DB_LOCKER *lref; 122 123 lref = lref_arg; 124 lref->pid = pid; 125 lref->tid = tid; 126} 127 128/* 129 * __lock_id_free_pp -- 130 * ENV->lock_id_free pre/post processing. 131 * 132 * PUBLIC: int __lock_id_free_pp __P((DB_ENV *, u_int32_t)); 133 */ 134int 135__lock_id_free_pp(dbenv, id) 136 DB_ENV *dbenv; 137 u_int32_t id; 138{ 139 DB_LOCKER *sh_locker; 140 DB_LOCKREGION *region; 141 DB_LOCKTAB *lt; 142 DB_THREAD_INFO *ip; 143 ENV *env; 144 int handle_check, ret, t_ret; 145 146 env = dbenv->env; 147 148 ENV_REQUIRES_CONFIG(env, 149 env->lk_handle, "DB_ENV->lock_id_free", DB_INIT_LOCK); 150 151 ENV_ENTER(env, ip); 152 153 /* Check for replication block. */ 154 handle_check = IS_ENV_REPLICATED(env); 155 if (handle_check && (ret = __env_rep_enter(env, 0)) != 0) { 156 handle_check = 0; 157 goto err; 158 } 159 160 lt = env->lk_handle; 161 region = lt->reginfo.primary; 162 163 LOCK_LOCKERS(env, region); 164 if ((ret = 165 __lock_getlocker_int(env->lk_handle, id, 0, &sh_locker)) == 0) { 166 if (sh_locker != NULL) 167 ret = __lock_freelocker(lt, region, sh_locker); 168 else { 169 __db_errx(env, "Unknown locker id: %lx", (u_long)id); 170 ret = EINVAL; 171 } 172 } 173 UNLOCK_LOCKERS(env, region); 174 175 if (handle_check && (t_ret = __env_db_rep_exit(env)) != 0 && ret == 0) 176 ret = t_ret; 177 178err: ENV_LEAVE(env, ip); 179 return (ret); 180} 181 182/* 183 * __lock_id_free -- 184 * Free a locker id. 185 * 186 * PUBLIC: int __lock_id_free __P((ENV *, DB_LOCKER *)); 187 */ 188int 189__lock_id_free(env, sh_locker) 190 ENV *env; 191 DB_LOCKER *sh_locker; 192{ 193 DB_LOCKREGION *region; 194 DB_LOCKTAB *lt; 195 int ret; 196 197 lt = env->lk_handle; 198 region = lt->reginfo.primary; 199 ret = 0; 200 201 if (sh_locker->nlocks != 0) { 202 __db_errx(env, "Locker still has locks"); 203 ret = EINVAL; 204 goto err; 205 } 206 207 LOCK_LOCKERS(env, region); 208 ret = __lock_freelocker(lt, region, sh_locker); 209 UNLOCK_LOCKERS(env, region); 210 211err: 212 return (ret); 213} 214 215/* 216 * __lock_id_set -- 217 * Set the current locker ID and current maximum unused ID (for 218 * testing purposes only). 219 * 220 * PUBLIC: int __lock_id_set __P((ENV *, u_int32_t, u_int32_t)); 221 */ 222int 223__lock_id_set(env, cur_id, max_id) 224 ENV *env; 225 u_int32_t cur_id, max_id; 226{ 227 DB_LOCKREGION *region; 228 DB_LOCKTAB *lt; 229 230 ENV_REQUIRES_CONFIG(env, 231 env->lk_handle, "lock_id_set", DB_INIT_LOCK); 232 233 lt = env->lk_handle; 234 region = lt->reginfo.primary; 235 region->lock_id = cur_id; 236 region->cur_maxid = max_id; 237 238 return (0); 239} 240 241/* 242 * __lock_getlocker -- 243 * Get a locker in the locker hash table. The create parameter 244 * indicates if the locker should be created if it doesn't exist in 245 * the table. 246 * 247 * This must be called with the locker mutex lock if create == 1. 248 * 249 * PUBLIC: int __lock_getlocker __P((DB_LOCKTAB *, 250 * PUBLIC: u_int32_t, int, DB_LOCKER **)); 251 * PUBLIC: int __lock_getlocker_int __P((DB_LOCKTAB *, 252 * PUBLIC: u_int32_t, int, DB_LOCKER **)); 253 */ 254int 255__lock_getlocker(lt, locker, create, retp) 256 DB_LOCKTAB *lt; 257 u_int32_t locker; 258 int create; 259 DB_LOCKER **retp; 260{ 261 DB_LOCKREGION *region; 262 ENV *env; 263 int ret; 264 265 COMPQUIET(region, NULL); 266 env = lt->env; 267 region = lt->reginfo.primary; 268 269 LOCK_LOCKERS(env, region); 270 ret = __lock_getlocker_int(lt, locker, create, retp); 271 UNLOCK_LOCKERS(env, region); 272 273 return (ret); 274} 275 276int 277__lock_getlocker_int(lt, locker, create, retp) 278 DB_LOCKTAB *lt; 279 u_int32_t locker; 280 int create; 281 DB_LOCKER **retp; 282{ 283 DB_LOCKER *sh_locker; 284 DB_LOCKREGION *region; 285 ENV *env; 286 u_int32_t indx; 287 288 env = lt->env; 289 region = lt->reginfo.primary; 290 291 LOCKER_HASH(lt, region, locker, indx); 292 293 /* 294 * If we find the locker, then we can just return it. If we don't find 295 * the locker, then we need to create it. 296 */ 297 SH_TAILQ_FOREACH(sh_locker, <->locker_tab[indx], links, __db_locker) 298 if (sh_locker->id == locker) 299 break; 300 if (sh_locker == NULL && create) { 301 /* Create new locker and then insert it into hash table. */ 302 if ((sh_locker = SH_TAILQ_FIRST( 303 ®ion->free_lockers, __db_locker)) == NULL) 304 return (__lock_nomem(env, "locker entries")); 305 SH_TAILQ_REMOVE( 306 ®ion->free_lockers, sh_locker, links, __db_locker); 307 ++region->nlockers; 308#ifdef HAVE_STATISTICS 309 if (region->nlockers > region->stat.st_maxnlockers) 310 region->stat.st_maxnlockers = region->nlockers; 311#endif 312 sh_locker->id = locker; 313 env->dbenv->thread_id( 314 env->dbenv, &sh_locker->pid, &sh_locker->tid); 315 sh_locker->dd_id = 0; 316 sh_locker->master_locker = INVALID_ROFF; 317 sh_locker->parent_locker = INVALID_ROFF; 318 SH_LIST_INIT(&sh_locker->child_locker); 319 sh_locker->flags = 0; 320 SH_LIST_INIT(&sh_locker->heldby); 321 sh_locker->nlocks = 0; 322 sh_locker->nwrites = 0; 323 sh_locker->lk_timeout = 0; 324 timespecclear(&sh_locker->tx_expire); 325 timespecclear(&sh_locker->lk_expire); 326 327 SH_TAILQ_INSERT_HEAD( 328 <->locker_tab[indx], sh_locker, links, __db_locker); 329 SH_TAILQ_INSERT_HEAD(®ion->lockers, 330 sh_locker, ulinks, __db_locker); 331 } 332 333 *retp = sh_locker; 334 return (0); 335} 336 337/* 338 * __lock_addfamilylocker 339 * Put a locker entry in for a child transaction. 340 * 341 * PUBLIC: int __lock_addfamilylocker __P((ENV *, u_int32_t, u_int32_t)); 342 */ 343int 344__lock_addfamilylocker(env, pid, id) 345 ENV *env; 346 u_int32_t pid, id; 347{ 348 DB_LOCKER *lockerp, *mlockerp; 349 DB_LOCKREGION *region; 350 DB_LOCKTAB *lt; 351 int ret; 352 353 COMPQUIET(region, NULL); 354 lt = env->lk_handle; 355 region = lt->reginfo.primary; 356 LOCK_LOCKERS(env, region); 357 358 /* get/create the parent locker info */ 359 if ((ret = __lock_getlocker_int(lt, pid, 1, &mlockerp)) != 0) 360 goto err; 361 362 /* 363 * We assume that only one thread can manipulate 364 * a single transaction family. 365 * Therefore the master locker cannot go away while 366 * we manipulate it, nor can another child in the 367 * family be created at the same time. 368 */ 369 if ((ret = __lock_getlocker_int(lt, id, 1, &lockerp)) != 0) 370 goto err; 371 372 /* Point to our parent. */ 373 lockerp->parent_locker = R_OFFSET(<->reginfo, mlockerp); 374 375 /* See if this locker is the family master. */ 376 if (mlockerp->master_locker == INVALID_ROFF) 377 lockerp->master_locker = R_OFFSET(<->reginfo, mlockerp); 378 else { 379 lockerp->master_locker = mlockerp->master_locker; 380 mlockerp = R_ADDR(<->reginfo, mlockerp->master_locker); 381 } 382 383 /* 384 * Link the child at the head of the master's list. 385 * The guess is when looking for deadlock that 386 * the most recent child is the one thats blocked. 387 */ 388 SH_LIST_INSERT_HEAD( 389 &mlockerp->child_locker, lockerp, child_link, __db_locker); 390 391err: UNLOCK_LOCKERS(env, region); 392 393 return (ret); 394} 395 396/* 397 * __lock_freefamilylocker 398 * Remove a locker from the hash table and its family. 399 * 400 * This must be called without the locker bucket locked. 401 * 402 * PUBLIC: int __lock_freefamilylocker __P((DB_LOCKTAB *, DB_LOCKER *)); 403 */ 404int 405__lock_freefamilylocker(lt, sh_locker) 406 DB_LOCKTAB *lt; 407 DB_LOCKER *sh_locker; 408{ 409 DB_LOCKREGION *region; 410 ENV *env; 411 int ret; 412 413 env = lt->env; 414 region = lt->reginfo.primary; 415 416 if (sh_locker == NULL) 417 return (0); 418 419 LOCK_LOCKERS(env, region); 420 421 if (SH_LIST_FIRST(&sh_locker->heldby, __db_lock) != NULL) { 422 ret = EINVAL; 423 __db_errx(env, "Freeing locker with locks"); 424 goto err; 425 } 426 427 /* If this is part of a family, we must fix up its links. */ 428 if (sh_locker->master_locker != INVALID_ROFF) 429 SH_LIST_REMOVE(sh_locker, child_link, __db_locker); 430 431 ret = __lock_freelocker(lt, region, sh_locker); 432 433err: UNLOCK_LOCKERS(env, region); 434 return (ret); 435} 436 437/* 438 * __lock_freelocker 439 * Common code for deleting a locker; must be called with the 440 * locker bucket locked. 441 * 442 * PUBLIC: int __lock_freelocker 443 * PUBLIC: __P((DB_LOCKTAB *, DB_LOCKREGION *, DB_LOCKER *)); 444 */ 445int 446__lock_freelocker(lt, region, sh_locker) 447 DB_LOCKTAB *lt; 448 DB_LOCKREGION *region; 449 DB_LOCKER *sh_locker; 450 451{ 452 u_int32_t indx; 453 LOCKER_HASH(lt, region, sh_locker->id, indx); 454 SH_TAILQ_REMOVE(<->locker_tab[indx], sh_locker, links, __db_locker); 455 SH_TAILQ_INSERT_HEAD( 456 ®ion->free_lockers, sh_locker, links, __db_locker); 457 SH_TAILQ_REMOVE(®ion->lockers, sh_locker, ulinks, __db_locker); 458 region->nlockers--; 459 return (0); 460} 461