1 /*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 1996, 2013 Oracle and/or its affiliates. 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 static int __lock_freelocker_int
16 __P((DB_LOCKTAB *, DB_LOCKREGION *, DB_LOCKER *, int));
17
18 /*
19 * __lock_id_pp --
20 * ENV->lock_id pre/post processing.
21 *
22 * PUBLIC: int __lock_id_pp __P((DB_ENV *, u_int32_t *));
23 */
24 int
__lock_id_pp(dbenv,idp)25 __lock_id_pp(dbenv, idp)
26 DB_ENV *dbenv;
27 u_int32_t *idp;
28 {
29 DB_THREAD_INFO *ip;
30 ENV *env;
31 int ret;
32
33 env = dbenv->env;
34
35 ENV_REQUIRES_CONFIG(env,
36 env->lk_handle, "DB_ENV->lock_id", DB_INIT_LOCK);
37
38 ENV_ENTER(env, ip);
39 REPLICATION_WRAP(env, (__lock_id(env, idp, NULL)), 0, ret);
40 ENV_LEAVE(env, ip);
41 return (ret);
42 }
43
44 /*
45 * __lock_id --
46 * ENV->lock_id.
47 *
48 * PUBLIC: int __lock_id __P((ENV *, u_int32_t *, DB_LOCKER **));
49 */
50 int
__lock_id(env,idp,lkp)51 __lock_id(env, idp, lkp)
52 ENV *env;
53 u_int32_t *idp;
54 DB_LOCKER **lkp;
55 {
56 DB_LOCKER *lk;
57 DB_LOCKREGION *region;
58 DB_LOCKTAB *lt;
59 u_int32_t id, *ids;
60 int nids, ret;
61
62 lk = NULL;
63 lt = env->lk_handle;
64 region = lt->reginfo.primary;
65 id = DB_LOCK_INVALIDID;
66 ret = 0;
67
68 id = DB_LOCK_INVALIDID;
69 lk = NULL;
70
71 LOCK_LOCKERS(env, region);
72
73 /*
74 * Allocate a new lock id. If we wrap around then we find the minimum
75 * currently in use and make sure we can stay below that. This code is
76 * similar to code in __txn_begin_int for recovering txn ids.
77 *
78 * Our current valid range can span the maximum valid value, so check
79 * for it and wrap manually.
80 */
81 if (region->lock_id == DB_LOCK_MAXID &&
82 region->cur_maxid != DB_LOCK_MAXID)
83 region->lock_id = DB_LOCK_INVALIDID;
84 if (region->lock_id == region->cur_maxid) {
85 if ((ret = __os_malloc(env,
86 sizeof(u_int32_t) * region->nlockers, &ids)) != 0)
87 goto err;
88 nids = 0;
89 SH_TAILQ_FOREACH(lk, ®ion->lockers, ulinks, __db_locker)
90 ids[nids++] = lk->id;
91 region->lock_id = DB_LOCK_INVALIDID;
92 region->cur_maxid = DB_LOCK_MAXID;
93 if (nids != 0)
94 __db_idspace(ids, nids,
95 ®ion->lock_id, ®ion->cur_maxid);
96 __os_free(env, ids);
97 }
98 id = ++region->lock_id;
99
100 /* Allocate a locker for this id. */
101 ret = __lock_getlocker_int(lt, id, 1, &lk);
102
103 err: UNLOCK_LOCKERS(env, region);
104
105 if (idp != NULL)
106 *idp = id;
107 if (lkp != NULL)
108 *lkp = lk;
109
110 return (ret);
111 }
112
113 /*
114 * __lock_set_thread_id --
115 * Set the thread_id in an existing locker.
116 * PUBLIC: void __lock_set_thread_id __P((void *, pid_t, db_threadid_t));
117 */
118 void
__lock_set_thread_id(lref_arg,pid,tid)119 __lock_set_thread_id(lref_arg, pid, tid)
120 void *lref_arg;
121 pid_t pid;
122 db_threadid_t tid;
123 {
124 DB_LOCKER *lref;
125
126 lref = lref_arg;
127 lref->pid = pid;
128 lref->tid = tid;
129 }
130
131 /*
132 * __lock_id_free_pp --
133 * ENV->lock_id_free pre/post processing.
134 *
135 * PUBLIC: int __lock_id_free_pp __P((DB_ENV *, u_int32_t));
136 */
137 int
__lock_id_free_pp(dbenv,id)138 __lock_id_free_pp(dbenv, id)
139 DB_ENV *dbenv;
140 u_int32_t id;
141 {
142 DB_LOCKER *sh_locker;
143 DB_LOCKREGION *region;
144 DB_LOCKTAB *lt;
145 DB_THREAD_INFO *ip;
146 ENV *env;
147 int handle_check, ret, t_ret;
148
149 env = dbenv->env;
150
151 ENV_REQUIRES_CONFIG(env,
152 env->lk_handle, "DB_ENV->lock_id_free", DB_INIT_LOCK);
153
154 ENV_ENTER(env, ip);
155
156 /* Check for replication block. */
157 handle_check = IS_ENV_REPLICATED(env);
158 if (handle_check && (ret = __env_rep_enter(env, 0)) != 0) {
159 handle_check = 0;
160 goto err;
161 }
162
163 lt = env->lk_handle;
164 region = lt->reginfo.primary;
165
166 LOCK_LOCKERS(env, region);
167 if ((ret =
168 __lock_getlocker_int(env->lk_handle, id, 0, &sh_locker)) == 0) {
169 if (sh_locker != NULL)
170 ret = __lock_freelocker_int(lt, region, sh_locker, 1);
171 else {
172 __db_errx(env, DB_STR_A("2045",
173 "Unknown locker id: %lx", "%lx"), (u_long)id);
174 ret = EINVAL;
175 }
176 }
177 UNLOCK_LOCKERS(env, region);
178
179 if (handle_check && (t_ret = __env_db_rep_exit(env)) != 0 && ret == 0)
180 ret = t_ret;
181
182 err: ENV_LEAVE(env, ip);
183 return (ret);
184 }
185
186 /*
187 * __lock_id_free --
188 * Free a locker id.
189 *
190 * PUBLIC: int __lock_id_free __P((ENV *, DB_LOCKER *));
191 */
192 int
__lock_id_free(env,sh_locker)193 __lock_id_free(env, sh_locker)
194 ENV *env;
195 DB_LOCKER *sh_locker;
196 {
197 DB_LOCKREGION *region;
198 DB_LOCKTAB *lt;
199 int ret;
200
201 lt = env->lk_handle;
202 region = lt->reginfo.primary;
203 ret = 0;
204
205 if (sh_locker->nlocks != 0) {
206 __db_errx(env, DB_STR("2046",
207 "Locker still has locks"));
208 ret = EINVAL;
209 goto err;
210 }
211
212 LOCK_LOCKERS(env, region);
213 ret = __lock_freelocker_int(lt, region, sh_locker, 1);
214 UNLOCK_LOCKERS(env, region);
215
216 err: return (ret);
217 }
218
219 /*
220 * __lock_id_set --
221 * Set the current locker ID and current maximum unused ID (for
222 * testing purposes only).
223 *
224 * PUBLIC: int __lock_id_set __P((ENV *, u_int32_t, u_int32_t));
225 */
226 int
__lock_id_set(env,cur_id,max_id)227 __lock_id_set(env, cur_id, max_id)
228 ENV *env;
229 u_int32_t cur_id, max_id;
230 {
231 DB_LOCKREGION *region;
232 DB_LOCKTAB *lt;
233
234 ENV_REQUIRES_CONFIG(env,
235 env->lk_handle, "lock_id_set", DB_INIT_LOCK);
236
237 lt = env->lk_handle;
238 region = lt->reginfo.primary;
239 region->lock_id = cur_id;
240 region->cur_maxid = max_id;
241
242 return (0);
243 }
244
245 /*
246 * __lock_getlocker --
247 * Get a locker in the locker hash table. The create parameter
248 * indicates if the locker should be created if it doesn't exist in
249 * the table.
250 *
251 * This must be called with the locker mutex lock if create == 1.
252 *
253 * PUBLIC: int __lock_getlocker __P((DB_LOCKTAB *,
254 * PUBLIC: u_int32_t, int, DB_LOCKER **));
255 * PUBLIC: int __lock_getlocker_int __P((DB_LOCKTAB *,
256 * PUBLIC: u_int32_t, int, DB_LOCKER **));
257 */
258 int
__lock_getlocker(lt,locker,create,retp)259 __lock_getlocker(lt, locker, create, retp)
260 DB_LOCKTAB *lt;
261 u_int32_t locker;
262 int create;
263 DB_LOCKER **retp;
264 {
265 DB_LOCKREGION *region;
266 ENV *env;
267 int ret;
268
269 COMPQUIET(region, NULL);
270 env = lt->env;
271 region = lt->reginfo.primary;
272
273 LOCK_LOCKERS(env, region);
274 ret = __lock_getlocker_int(lt, locker, create, retp);
275 UNLOCK_LOCKERS(env, region);
276
277 return (ret);
278 }
279
280 int
__lock_getlocker_int(lt,locker,create,retp)281 __lock_getlocker_int(lt, locker, create, retp)
282 DB_LOCKTAB *lt;
283 u_int32_t locker;
284 int create;
285 DB_LOCKER **retp;
286 {
287 DB_LOCKER *sh_locker;
288 DB_LOCKREGION *region;
289 DB_THREAD_INFO *ip;
290 ENV *env;
291 db_mutex_t mutex;
292 u_int32_t i, indx, nlockers;
293 int ret;
294
295 env = lt->env;
296 region = lt->reginfo.primary;
297
298 LOCKER_HASH(lt, region, locker, indx);
299
300 /*
301 * If we find the locker, then we can just return it. If we don't find
302 * the locker, then we need to create it.
303 */
304 SH_TAILQ_FOREACH(sh_locker, <->locker_tab[indx], links, __db_locker)
305 if (sh_locker->id == locker)
306 break;
307 if (sh_locker == NULL && create) {
308 nlockers = 0;
309 /* Create new locker and then insert it into hash table. */
310 if ((ret = __mutex_alloc(env, MTX_LOGICAL_LOCK,
311 DB_MUTEX_LOGICAL_LOCK | DB_MUTEX_SELF_BLOCK,
312 &mutex)) != 0)
313 return (ret);
314 else
315 MUTEX_LOCK(env, mutex);
316 if ((sh_locker = SH_TAILQ_FIRST(
317 ®ion->free_lockers, __db_locker)) == NULL) {
318 nlockers = region->stat.st_lockers >> 2;
319 /* Just in case. */
320 if (nlockers == 0)
321 nlockers = 1;
322 if (region->stat.st_maxlockers != 0 &&
323 region->stat.st_maxlockers <
324 region->stat.st_lockers + nlockers)
325 nlockers = region->stat.st_maxlockers -
326 region->stat.st_lockers;
327 /*
328 * Don't hold lockers when getting the region,
329 * we could deadlock. When creating a locker
330 * there is no race since the id allocation
331 * is synchronized.
332 */
333 UNLOCK_LOCKERS(env, region);
334 LOCK_REGION_LOCK(env);
335 /*
336 * If the max memory is not sized for max objects,
337 * allocate as much as possible.
338 */
339 F_SET(<->reginfo, REGION_TRACKED);
340 while (__env_alloc(<->reginfo, nlockers *
341 sizeof(struct __db_locker), &sh_locker) != 0)
342 if ((nlockers >> 1) == 0)
343 break;
344 F_CLR(<->reginfo, REGION_TRACKED);
345 LOCK_REGION_UNLOCK(lt->env);
346 LOCK_LOCKERS(env, region);
347 for (i = 0; i < nlockers; i++) {
348 SH_TAILQ_INSERT_HEAD(®ion->free_lockers,
349 sh_locker, links, __db_locker);
350 sh_locker++;
351 }
352 if (nlockers == 0)
353 return (__lock_nomem(env, "locker entries"));
354 region->stat.st_lockers += nlockers;
355 sh_locker = SH_TAILQ_FIRST(
356 ®ion->free_lockers, __db_locker);
357 }
358 SH_TAILQ_REMOVE(
359 ®ion->free_lockers, sh_locker, links, __db_locker);
360 ++region->nlockers;
361 #ifdef HAVE_STATISTICS
362 STAT_PERFMON2(env, lock, nlockers, region->nlockers, locker);
363 if (region->nlockers > region->stat.st_maxnlockers)
364 STAT_SET(env, lock, maxnlockers,
365 region->stat.st_maxnlockers,
366 region->nlockers, locker);
367 #endif
368 sh_locker->id = locker;
369 env->dbenv->thread_id(
370 env->dbenv, &sh_locker->pid, &sh_locker->tid);
371 sh_locker->mtx_locker = mutex;
372 sh_locker->dd_id = 0;
373 sh_locker->master_locker = INVALID_ROFF;
374 sh_locker->parent_locker = INVALID_ROFF;
375 SH_LIST_INIT(&sh_locker->child_locker);
376 sh_locker->flags = 0;
377 SH_LIST_INIT(&sh_locker->heldby);
378 sh_locker->nlocks = 0;
379 sh_locker->nwrites = 0;
380 sh_locker->priority = DB_LOCK_DEFPRIORITY;
381 sh_locker->lk_timeout = 0;
382 timespecclear(&sh_locker->tx_expire);
383 timespecclear(&sh_locker->lk_expire);
384
385 SH_TAILQ_INSERT_HEAD(
386 <->locker_tab[indx], sh_locker, links, __db_locker);
387 SH_TAILQ_INSERT_HEAD(®ion->lockers,
388 sh_locker, ulinks, __db_locker);
389 ENV_GET_THREAD_INFO(env, ip);
390 #ifdef DIAGNOSTIC
391 if (ip != NULL)
392 ip->dbth_locker = R_OFFSET(<->reginfo, sh_locker);
393 #endif
394 }
395
396 *retp = sh_locker;
397 return (0);
398 }
399
400 /*
401 * __lock_addfamilylocker
402 * Put a locker entry in for a child transaction.
403 *
404 * PUBLIC: int __lock_addfamilylocker __P((ENV *,
405 * PUBLIC: u_int32_t, u_int32_t, u_int32_t));
406 */
407 int
__lock_addfamilylocker(env,pid,id,is_family)408 __lock_addfamilylocker(env, pid, id, is_family)
409 ENV *env;
410 u_int32_t pid, id, is_family;
411 {
412 DB_LOCKER *lockerp, *mlockerp;
413 DB_LOCKREGION *region;
414 DB_LOCKTAB *lt;
415 int ret;
416
417 COMPQUIET(region, NULL);
418 lt = env->lk_handle;
419 region = lt->reginfo.primary;
420 LOCK_LOCKERS(env, region);
421
422 /* get/create the parent locker info */
423 if ((ret = __lock_getlocker_int(lt, pid, 1, &mlockerp)) != 0)
424 goto err;
425
426 /*
427 * We assume that only one thread can manipulate
428 * a single transaction family.
429 * Therefore the master locker cannot go away while
430 * we manipulate it, nor can another child in the
431 * family be created at the same time.
432 */
433 if ((ret = __lock_getlocker_int(lt, id, 1, &lockerp)) != 0)
434 goto err;
435
436 /* Point to our parent. */
437 lockerp->parent_locker = R_OFFSET(<->reginfo, mlockerp);
438
439 /* See if this locker is the family master. */
440 if (mlockerp->master_locker == INVALID_ROFF)
441 lockerp->master_locker = R_OFFSET(<->reginfo, mlockerp);
442 else {
443 lockerp->master_locker = mlockerp->master_locker;
444 mlockerp = R_ADDR(<->reginfo, mlockerp->master_locker);
445 }
446
447 /*
448 * Set the family locker flag, so it is possible to distinguish
449 * between locks held by subtransactions and those with compatible
450 * lockers.
451 */
452 if (is_family)
453 F_SET(mlockerp, DB_LOCKER_FAMILY_LOCKER);
454
455 /*
456 * Link the child at the head of the master's list.
457 * The guess is when looking for deadlock that
458 * the most recent child is the one that's blocked.
459 */
460 SH_LIST_INSERT_HEAD(
461 &mlockerp->child_locker, lockerp, child_link, __db_locker);
462
463 err: UNLOCK_LOCKERS(env, region);
464
465 return (ret);
466 }
467
468 /*
469 * __lock_freelocker_int
470 * Common code for deleting a locker; must be called with the
471 * locker bucket locked.
472 */
473 static int
__lock_freelocker_int(lt,region,sh_locker,reallyfree)474 __lock_freelocker_int(lt, region, sh_locker, reallyfree)
475 DB_LOCKTAB *lt;
476 DB_LOCKREGION *region;
477 DB_LOCKER *sh_locker;
478 int reallyfree;
479 {
480 ENV *env;
481 u_int32_t indx;
482 int ret;
483
484 env = lt->env;
485
486 if (SH_LIST_FIRST(&sh_locker->heldby, __db_lock) != NULL) {
487 __db_errx(env, DB_STR("2047",
488 "Freeing locker with locks"));
489 return (EINVAL);
490 }
491
492 /* If this is part of a family, we must fix up its links. */
493 if (sh_locker->master_locker != INVALID_ROFF) {
494 SH_LIST_REMOVE(sh_locker, child_link, __db_locker);
495 sh_locker->master_locker = INVALID_ROFF;
496 }
497
498 if (reallyfree) {
499 LOCKER_HASH(lt, region, sh_locker->id, indx);
500 SH_TAILQ_REMOVE(<->locker_tab[indx], sh_locker,
501 links, __db_locker);
502 if (sh_locker->mtx_locker != MUTEX_INVALID &&
503 (ret = __mutex_free(env, &sh_locker->mtx_locker)) != 0)
504 return (ret);
505 SH_TAILQ_INSERT_HEAD(®ion->free_lockers, sh_locker,
506 links, __db_locker);
507 SH_TAILQ_REMOVE(®ion->lockers, sh_locker,
508 ulinks, __db_locker);
509 region->nlockers--;
510 STAT_PERFMON2(env,
511 lock, nlockers, region->nlockers, sh_locker->id);
512 }
513
514 return (0);
515 }
516
517 /*
518 * __lock_freelocker
519 * Remove a locker its family from the hash table.
520 *
521 * This must be called without the locker bucket locked.
522 *
523 * PUBLIC: int __lock_freelocker __P((DB_LOCKTAB *, DB_LOCKER *));
524 */
525 int
__lock_freelocker(lt,sh_locker)526 __lock_freelocker(lt, sh_locker)
527 DB_LOCKTAB *lt;
528 DB_LOCKER *sh_locker;
529 {
530 DB_LOCKREGION *region;
531 ENV *env;
532 int ret;
533
534 region = lt->reginfo.primary;
535 env = lt->env;
536
537 if (sh_locker == NULL)
538 return (0);
539
540 LOCK_LOCKERS(env, region);
541 ret = __lock_freelocker_int(lt, region, sh_locker, 1);
542 UNLOCK_LOCKERS(env, region);
543
544 return (ret);
545 }
546
547 /*
548 * __lock_familyremove
549 * Remove a locker from its family.
550 *
551 * This must be called without the locker bucket locked.
552 *
553 * PUBLIC: int __lock_familyremove __P((DB_LOCKTAB *, DB_LOCKER *));
554 */
555 int
__lock_familyremove(lt,sh_locker)556 __lock_familyremove(lt, sh_locker)
557 DB_LOCKTAB *lt;
558 DB_LOCKER *sh_locker;
559 {
560 DB_LOCKREGION *region;
561 ENV *env;
562 int ret;
563
564 region = lt->reginfo.primary;
565 env = lt->env;
566
567 LOCK_LOCKERS(env, region);
568 ret = __lock_freelocker_int(lt, region, sh_locker, 0);
569 UNLOCK_LOCKERS(env, region);
570
571 return (ret);
572 }
573