1 /*-
2 * Copyright (c) 1996, 2020 Oracle and/or its affiliates. All rights reserved.
3 *
4 * See the file LICENSE for license information.
5 *
6 * $Id$
7 */
8
9 #include "db_config.h"
10
11 #include "db_int.h"
12 #include "dbinc/lock.h"
13
14 /*
15 * __lock_env_create --
16 * Lock specific creation of the DB_ENV structure.
17 *
18 * PUBLIC: int __lock_env_create __P((DB_ENV *));
19 */
20 int
__lock_env_create(dbenv)21 __lock_env_create(dbenv)
22 DB_ENV *dbenv;
23 {
24 u_int32_t cpu;
25 /*
26 * !!!
27 * Our caller has not yet had the opportunity to reset the panic
28 * state or turn off mutex locking, and so we can neither check
29 * the panic state or acquire a mutex in the DB_ENV create path.
30 */
31 dbenv->lk_init = 0;
32 dbenv->lk_init_lockers = 0;
33 dbenv->lk_init_objects = 0;
34
35 /*
36 * Default to 10 partitions per cpu. This seems to be near
37 * the point of diminishing returns on Xeon type processors.
38 * Cpu count often returns the number of hyper threads and if
39 * there is only one CPU you probably do not want to run partitions.
40 */
41 cpu = __os_cpu_count();
42 dbenv->lk_partitions = cpu > 1 ? 10 * cpu : 1;
43
44 return (0);
45 }
46
47 /*
48 * __lock_env_destroy --
49 * Lock specific destruction of the DB_ENV structure.
50 *
51 * PUBLIC: void __lock_env_destroy __P((DB_ENV *));
52 */
53 void
__lock_env_destroy(dbenv)54 __lock_env_destroy(dbenv)
55 DB_ENV *dbenv;
56 {
57 ENV *env;
58
59 env = dbenv->env;
60
61 if (dbenv->lk_conflicts != NULL) {
62 __os_free(env, dbenv->lk_conflicts);
63 dbenv->lk_conflicts = NULL;
64 }
65 }
66
67 /*
68 * __lock_get_lk_conflicts
69 * Get the conflicts matrix.
70 *
71 * PUBLIC: int __lock_get_lk_conflicts
72 * PUBLIC: __P((DB_ENV *, const u_int8_t **, int *));
73 */
74 int
__lock_get_lk_conflicts(dbenv,lk_conflictsp,lk_modesp)75 __lock_get_lk_conflicts(dbenv, lk_conflictsp, lk_modesp)
76 DB_ENV *dbenv;
77 const u_int8_t **lk_conflictsp;
78 int *lk_modesp;
79 {
80 DB_LOCKTAB *lt;
81 ENV *env;
82
83 env = dbenv->env;
84 lt = env->lk_handle;
85
86 ENV_NOT_CONFIGURED(env,
87 env->lk_handle, "DB_ENV->get_lk_conflicts", DB_INIT_LOCK);
88
89 if (LOCKING_ON(env)) {
90 /* Cannot be set after open, no lock required to read. */
91 if (lk_conflictsp != NULL)
92 *lk_conflictsp = lt->conflicts;
93 if (lk_modesp != NULL)
94 *lk_modesp = ((DB_LOCKREGION *)
95 (lt->reginfo.primary))->nmodes;
96 } else {
97 if (lk_conflictsp != NULL)
98 *lk_conflictsp = dbenv->lk_conflicts;
99 if (lk_modesp != NULL)
100 *lk_modesp = dbenv->lk_modes;
101 }
102 return (0);
103 }
104
105 /*
106 * __lock_set_lk_conflicts
107 * Set the conflicts matrix.
108 *
109 * PUBLIC: int __lock_set_lk_conflicts __P((DB_ENV *, u_int8_t *, int));
110 */
111 int
__lock_set_lk_conflicts(dbenv,lk_conflicts,lk_modes)112 __lock_set_lk_conflicts(dbenv, lk_conflicts, lk_modes)
113 DB_ENV *dbenv;
114 u_int8_t *lk_conflicts;
115 int lk_modes;
116 {
117 ENV *env;
118 int ret;
119
120 env = dbenv->env;
121
122 ENV_ILLEGAL_AFTER_OPEN(env, "DB_ENV->set_lk_conflicts");
123
124 if (lk_modes == 0) {
125 ret = USR_ERR(env, EINVAL);
126 __db_errx(env, DB_STR("2076",
127 "DB_ENV->set_lk_conflicts: nmodes cannot be 0."));
128 return (ret);
129 }
130
131 if (dbenv->lk_conflicts != NULL) {
132 __os_free(env, dbenv->lk_conflicts);
133 dbenv->lk_conflicts = NULL;
134 }
135 if ((ret = __os_malloc(env,
136 (size_t)(lk_modes * lk_modes), &dbenv->lk_conflicts)) != 0)
137 return (ret);
138 memcpy(
139 dbenv->lk_conflicts, lk_conflicts, (size_t)(lk_modes * lk_modes));
140 dbenv->lk_modes = lk_modes;
141
142 return (0);
143 }
144
145 /*
146 * PUBLIC: int __lock_get_lk_detect __P((DB_ENV *, u_int32_t *));
147 */
148 int
__lock_get_lk_detect(dbenv,lk_detectp)149 __lock_get_lk_detect(dbenv, lk_detectp)
150 DB_ENV *dbenv;
151 u_int32_t *lk_detectp;
152 {
153 DB_LOCKTAB *lt;
154 DB_THREAD_INFO *ip;
155 ENV *env;
156
157 env = dbenv->env;
158
159 ENV_NOT_CONFIGURED(env,
160 env->lk_handle, "DB_ENV->get_lk_detect", DB_INIT_LOCK);
161
162 if (LOCKING_ON(env)) {
163 lt = env->lk_handle;
164 ENV_ENTER(env, ip);
165 LOCK_REGION_LOCK(env);
166 *lk_detectp = ((DB_LOCKREGION *)lt->reginfo.primary)->detect;
167 LOCK_REGION_UNLOCK(env);
168 ENV_LEAVE(env, ip);
169 } else
170 *lk_detectp = dbenv->lk_detect;
171 return (0);
172 }
173
174 /*
175 * __lock_set_lk_detect --
176 * DB_ENV->set_lk_detect.
177 *
178 * PUBLIC: int __lock_set_lk_detect __P((DB_ENV *, u_int32_t));
179 */
180 int
__lock_set_lk_detect(dbenv,lk_detect)181 __lock_set_lk_detect(dbenv, lk_detect)
182 DB_ENV *dbenv;
183 u_int32_t lk_detect;
184 {
185 DB_ENV *slice;
186 DB_LOCKREGION *region;
187 DB_LOCKTAB *lt;
188 DB_THREAD_INFO *ip;
189 ENV *env;
190 int i, ret;
191
192 env = dbenv->env;
193
194 ENV_NOT_CONFIGURED(env,
195 env->lk_handle, "DB_ENV->set_lk_detect", DB_INIT_LOCK);
196
197 switch (lk_detect) {
198 case DB_LOCK_DEFAULT:
199 case DB_LOCK_EXPIRE:
200 case DB_LOCK_MAXLOCKS:
201 case DB_LOCK_MAXWRITE:
202 case DB_LOCK_MINLOCKS:
203 case DB_LOCK_MINWRITE:
204 case DB_LOCK_OLDEST:
205 case DB_LOCK_RANDOM:
206 case DB_LOCK_YOUNGEST:
207 break;
208 default:
209 __db_errx(env, DB_STR("2043",
210 "DB_ENV->set_lk_detect: unknown deadlock detection mode specified"));
211 return (EINVAL);
212 }
213
214 ret = 0;
215 if (LOCKING_ON(env)) {
216 ENV_ENTER(env, ip);
217
218 lt = env->lk_handle;
219 region = lt->reginfo.primary;
220 LOCK_REGION_LOCK(env);
221 /*
222 * Check for incompatible automatic deadlock detection requests.
223 * There are scenarios where changing the detector configuration
224 * is reasonable, but we disallow them guessing it is likely to
225 * be an application error.
226 *
227 * We allow applications to turn on the lock detector, and we
228 * ignore attempts to set it to the default or current value.
229 */
230 if (region->detect != DB_LOCK_NORUN &&
231 lk_detect != DB_LOCK_DEFAULT &&
232 region->detect != lk_detect) {
233 ret = USR_ERR(env, EINVAL);
234 __db_errx(env, DB_STR("2044",
235 "DB_ENV->set_lk_detect: incompatible deadlock detector mode"));
236 } else
237 if (region->detect == DB_LOCK_NORUN)
238 region->detect = lk_detect;
239 LOCK_REGION_UNLOCK(env);
240 ENV_LEAVE(env, ip);
241 } else
242 dbenv->lk_detect = lk_detect;
243 if (ret == 0)
244 SLICE_FOREACH(dbenv, slice, i)
245 if ((ret = __lock_set_lk_detect(slice, lk_detect)) != 0)
246 break;
247
248 return (ret);
249 }
250
251 /*
252 * PUBLIC: int __lock_get_lk_max_locks __P((DB_ENV *, u_int32_t *));
253 */
254 int
__lock_get_lk_max_locks(dbenv,lk_maxp)255 __lock_get_lk_max_locks(dbenv, lk_maxp)
256 DB_ENV *dbenv;
257 u_int32_t *lk_maxp;
258 {
259 ENV *env;
260
261 env = dbenv->env;
262
263 ENV_NOT_CONFIGURED(env,
264 env->lk_handle, "DB_ENV->get_lk_maxlocks", DB_INIT_LOCK);
265
266 if (LOCKING_ON(env)) {
267 /* Cannot be set after open, no lock required to read. */
268 *lk_maxp = ((DB_LOCKREGION *)
269 env->lk_handle->reginfo.primary)->stat.st_maxlocks;
270 } else
271 *lk_maxp = dbenv->lk_max;
272 return (0);
273 }
274
275 /*
276 * __lock_set_lk_max_locks
277 * DB_ENV->set_lk_max_locks.
278 *
279 * PUBLIC: int __lock_set_lk_max_locks __P((DB_ENV *, u_int32_t));
280 */
281 int
__lock_set_lk_max_locks(dbenv,lk_max)282 __lock_set_lk_max_locks(dbenv, lk_max)
283 DB_ENV *dbenv;
284 u_int32_t lk_max;
285 {
286 ENV *env;
287
288 env = dbenv->env;
289
290 ENV_ILLEGAL_AFTER_OPEN(env, "DB_ENV->set_lk_max_locks");
291
292 dbenv->lk_max = lk_max;
293 return (0);
294 }
295
296 /*
297 * PUBLIC: int __lock_get_lk_max_lockers __P((DB_ENV *, u_int32_t *));
298 */
299 int
__lock_get_lk_max_lockers(dbenv,lk_maxp)300 __lock_get_lk_max_lockers(dbenv, lk_maxp)
301 DB_ENV *dbenv;
302 u_int32_t *lk_maxp;
303 {
304 ENV *env;
305
306 env = dbenv->env;
307
308 ENV_NOT_CONFIGURED(env,
309 env->lk_handle, "DB_ENV->get_lk_max_lockers", DB_INIT_LOCK);
310
311 if (LOCKING_ON(env)) {
312 /* Cannot be set after open, no lock required to read. */
313 *lk_maxp = ((DB_LOCKREGION *)
314 env->lk_handle->reginfo.primary)->stat.st_maxlockers;
315 } else
316 *lk_maxp = dbenv->lk_max_lockers;
317 return (0);
318 }
319
320 /*
321 * __lock_set_lk_max_lockers
322 * DB_ENV->set_lk_max_lockers.
323 *
324 * PUBLIC: int __lock_set_lk_max_lockers __P((DB_ENV *, u_int32_t));
325 */
326 int
__lock_set_lk_max_lockers(dbenv,lk_max)327 __lock_set_lk_max_lockers(dbenv, lk_max)
328 DB_ENV *dbenv;
329 u_int32_t lk_max;
330 {
331 ENV *env;
332
333 env = dbenv->env;
334
335 ENV_ILLEGAL_AFTER_OPEN(env, "DB_ENV->set_lk_max_lockers");
336
337 dbenv->lk_max_lockers = lk_max;
338 return (0);
339 }
340
341 /*
342 * PUBLIC: int __lock_get_lk_max_objects __P((DB_ENV *, u_int32_t *));
343 */
344 int
__lock_get_lk_max_objects(dbenv,lk_maxp)345 __lock_get_lk_max_objects(dbenv, lk_maxp)
346 DB_ENV *dbenv;
347 u_int32_t *lk_maxp;
348 {
349 ENV *env;
350
351 env = dbenv->env;
352
353 ENV_NOT_CONFIGURED(env,
354 env->lk_handle, "DB_ENV->get_lk_max_objects", DB_INIT_LOCK);
355
356 if (LOCKING_ON(env)) {
357 /* Cannot be set after open, no lock required to read. */
358 *lk_maxp = ((DB_LOCKREGION *)
359 env->lk_handle->reginfo.primary)->stat.st_maxobjects;
360 } else
361 *lk_maxp = dbenv->lk_max_objects;
362 return (0);
363 }
364
365 /*
366 * __lock_set_lk_max_objects
367 * DB_ENV->set_lk_max_objects.
368 *
369 * PUBLIC: int __lock_set_lk_max_objects __P((DB_ENV *, u_int32_t));
370 */
371 int
__lock_set_lk_max_objects(dbenv,lk_max)372 __lock_set_lk_max_objects(dbenv, lk_max)
373 DB_ENV *dbenv;
374 u_int32_t lk_max;
375 {
376 ENV *env;
377
378 env = dbenv->env;
379
380 ENV_ILLEGAL_AFTER_OPEN(env, "DB_ENV->set_lk_max_objects");
381
382 dbenv->lk_max_objects = lk_max;
383 return (0);
384 }
385 /*
386 * PUBLIC: int __lock_get_lk_partitions __P((DB_ENV *, u_int32_t *));
387 */
388 int
__lock_get_lk_partitions(dbenv,lk_partitionp)389 __lock_get_lk_partitions(dbenv, lk_partitionp)
390 DB_ENV *dbenv;
391 u_int32_t *lk_partitionp;
392 {
393 ENV *env;
394
395 env = dbenv->env;
396
397 ENV_NOT_CONFIGURED(env,
398 env->lk_handle, "DB_ENV->get_lk_partitions", DB_INIT_LOCK);
399
400 if (LOCKING_ON(env)) {
401 /* Cannot be set after open, no lock required to read. */
402 *lk_partitionp = ((DB_LOCKREGION *)
403 env->lk_handle->reginfo.primary)->stat.st_partitions;
404 } else
405 *lk_partitionp = dbenv->lk_partitions;
406 return (0);
407 }
408
409 /*
410 * __lock_set_lk_partitions
411 * DB_ENV->set_lk_partitions.
412 *
413 * PUBLIC: int __lock_set_lk_partitions __P((DB_ENV *, u_int32_t));
414 */
415 int
__lock_set_lk_partitions(dbenv,lk_partitions)416 __lock_set_lk_partitions(dbenv, lk_partitions)
417 DB_ENV *dbenv;
418 u_int32_t lk_partitions;
419 {
420 ENV *env;
421
422 env = dbenv->env;
423
424 ENV_ILLEGAL_AFTER_OPEN(env, "DB_ENV->set_lk_partitions");
425
426 if (lk_partitions == 0) {
427 __db_errx(env, DB_STR("2077",
428 "DB_ENV->set_lk_partitions: partitions cannot be 0."));
429 return (EINVAL);
430 }
431
432 dbenv->lk_partitions = lk_partitions;
433 return (0);
434 }
435 /*
436 * PUBLIC: int __lock_get_lk_tablesize __P((DB_ENV *, u_int32_t *));
437 */
438 int
__lock_get_lk_tablesize(dbenv,lk_tablesizep)439 __lock_get_lk_tablesize(dbenv, lk_tablesizep)
440 DB_ENV *dbenv;
441 u_int32_t *lk_tablesizep;
442 {
443 ENV *env;
444
445 env = dbenv->env;
446
447 ENV_NOT_CONFIGURED(env,
448 env->lk_handle, "DB_ENV->get_lk_tablesize", DB_INIT_LOCK);
449
450 if (LOCKING_ON(env)) {
451 /* Cannot be set after open, no lock required to read. */
452 *lk_tablesizep = ((DB_LOCKREGION *)
453 env->lk_handle->reginfo.primary)->stat.st_tablesize;
454 } else
455 *lk_tablesizep = dbenv->object_t_size;
456 return (0);
457 }
458
459 /*
460 * __lock_set_lk_tablesize
461 * DB_ENV->set_lk_tablesize.
462 *
463 * PUBLIC: int __lock_set_lk_tablesize __P((DB_ENV *, u_int32_t));
464 */
465 int
__lock_set_lk_tablesize(dbenv,lk_tablesize)466 __lock_set_lk_tablesize(dbenv, lk_tablesize)
467 DB_ENV *dbenv;
468 u_int32_t lk_tablesize;
469 {
470 ENV *env;
471
472 env = dbenv->env;
473
474 ENV_ILLEGAL_AFTER_OPEN(env, "DB_ENV->set_lk_tablesize");
475
476 dbenv->object_t_size = lk_tablesize;
477 return (0);
478 }
479
480 /*
481 * __lock_set_lk_priority --
482 * Set a locker's priority.
483 *
484 * PUBLIC: int __lock_set_lk_priority __P((DB_ENV *, u_int32_t, u_int32_t));
485 */
486 int
__lock_set_lk_priority(dbenv,lockid,priority)487 __lock_set_lk_priority(dbenv, lockid, priority)
488 DB_ENV *dbenv;
489 u_int32_t lockid, priority;
490 {
491 DB_LOCKER *locker;
492 ENV *env;
493 int ret;
494
495 env = dbenv->env;
496
497 if (!LOCKING_ON(env))
498 return (EINVAL);
499
500 if ((ret = __lock_getlocker(env->lk_handle, lockid, 0, &locker)) == 0)
501 locker->priority = priority;
502 return (ret);
503 }
504
505 /*
506 * __lock_get_lk_priority --
507 * Get a locker's priority.
508 *
509 * PUBLIC: int __lock_get_lk_priority __P((DB_ENV *, u_int32_t, u_int32_t *));
510 */
511 int
__lock_get_lk_priority(dbenv,lockid,priorityp)512 __lock_get_lk_priority(dbenv, lockid, priorityp)
513 DB_ENV *dbenv;
514 u_int32_t lockid, *priorityp;
515 {
516 DB_LOCKER *locker;
517 ENV *env;
518 int ret;
519
520 env = dbenv->env;
521
522 if (!LOCKING_ON(env))
523 return (EINVAL);
524
525 if ((ret = __lock_getlocker(env->lk_handle, lockid, 0, &locker)) == 0)
526 *priorityp = locker->priority;
527 return ret;
528 }
529
530 /*
531 * PUBLIC: int __lock_get_env_timeout
532 * PUBLIC: __P((DB_ENV *, db_timeout_t *, u_int32_t));
533 */
534 int
__lock_get_env_timeout(dbenv,timeoutp,flag)535 __lock_get_env_timeout(dbenv, timeoutp, flag)
536 DB_ENV *dbenv;
537 db_timeout_t *timeoutp;
538 u_int32_t flag;
539 {
540 DB_LOCKREGION *region;
541 DB_LOCKTAB *lt;
542 DB_THREAD_INFO *ip;
543 ENV *env;
544 int ret;
545
546 env = dbenv->env;
547
548 ENV_NOT_CONFIGURED(env,
549 env->lk_handle, "DB_ENV->get_env_timeout", DB_INIT_LOCK);
550
551 ret = 0;
552 if (LOCKING_ON(env)) {
553 lt = env->lk_handle;
554 region = lt->reginfo.primary;
555 ENV_ENTER(env, ip);
556 LOCK_REGION_LOCK(env);
557 switch (flag) {
558 case DB_SET_LOCK_TIMEOUT:
559 *timeoutp = region->lk_timeout;
560 break;
561 case DB_SET_TXN_TIMEOUT:
562 *timeoutp = region->tx_timeout;
563 break;
564 default:
565 ret = USR_ERR(env, EINVAL);
566 break;
567 }
568 LOCK_REGION_UNLOCK(env);
569 ENV_LEAVE(env, ip);
570 } else
571 switch (flag) {
572 case DB_SET_LOCK_TIMEOUT:
573 *timeoutp = dbenv->lk_timeout;
574 break;
575 case DB_SET_TXN_TIMEOUT:
576 *timeoutp = dbenv->tx_timeout;
577 break;
578 default:
579 ret = USR_ERR(env, EINVAL);
580 break;
581 }
582
583 if (ret)
584 ret = __db_ferr(env, "DB_ENV->get_timeout", 0);
585
586 return (ret);
587 }
588
589 /*
590 * __lock_set_env_timeout
591 * DB_ENV->set_lock_timeout.
592 *
593 * PUBLIC: int __lock_set_env_timeout __P((DB_ENV *, db_timeout_t, u_int32_t));
594 */
595 int
__lock_set_env_timeout(dbenv,timeout,flags)596 __lock_set_env_timeout(dbenv, timeout, flags)
597 DB_ENV *dbenv;
598 db_timeout_t timeout;
599 u_int32_t flags;
600 {
601 DB_LOCKREGION *region;
602 DB_LOCKTAB *lt;
603 DB_THREAD_INFO *ip;
604 ENV *env;
605 int badflag;
606
607 env = dbenv->env;
608
609 ENV_NOT_CONFIGURED(env,
610 env->lk_handle, "DB_ENV->set_env_timeout", DB_INIT_LOCK);
611
612 badflag = 0;
613 if (LOCKING_ON(env)) {
614 lt = env->lk_handle;
615 region = lt->reginfo.primary;
616 ENV_ENTER(env, ip);
617 LOCK_REGION_LOCK(env);
618 switch (flags) {
619 case DB_SET_LOCK_TIMEOUT:
620 region->lk_timeout = timeout;
621 break;
622 case DB_SET_TXN_TIMEOUT:
623 region->tx_timeout = timeout;
624 break;
625 default:
626 badflag = 1;
627 break;
628 }
629 LOCK_REGION_UNLOCK(env);
630 ENV_LEAVE(env, ip);
631 } else
632 switch (flags) {
633 case DB_SET_LOCK_TIMEOUT:
634 dbenv->lk_timeout = timeout;
635 break;
636 case DB_SET_TXN_TIMEOUT:
637 dbenv->tx_timeout = timeout;
638 break;
639 default:
640 badflag = 1;
641 break;
642 }
643
644 if (badflag)
645 return (__db_ferr(env, "DB_ENV->set_timeout", 0));
646 return (0);
647 }
648