1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 1996,2008 Oracle.  All rights reserved.
5 *
6 * $Id: lock_method.c,v 12.25 2008/05/07 12:27:35 bschmeck Exp $
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 */
20int
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_max = DB_LOCK_DEFAULT_N;
32	dbenv->lk_max_lockers = DB_LOCK_DEFAULT_N;
33	dbenv->lk_max_objects = DB_LOCK_DEFAULT_N;
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 */
53void
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 */
74int
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))->stat.st_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 */
111int
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 (dbenv->lk_conflicts != NULL) {
125		__os_free(env, dbenv->lk_conflicts);
126		dbenv->lk_conflicts = NULL;
127	}
128	if ((ret = __os_malloc(env,
129	    (size_t)(lk_modes * lk_modes), &dbenv->lk_conflicts)) != 0)
130		return (ret);
131	memcpy(
132	    dbenv->lk_conflicts, lk_conflicts, (size_t)(lk_modes * lk_modes));
133	dbenv->lk_modes = lk_modes;
134
135	return (0);
136}
137
138/*
139 * PUBLIC: int __lock_get_lk_detect __P((DB_ENV *, u_int32_t *));
140 */
141int
142__lock_get_lk_detect(dbenv, lk_detectp)
143	DB_ENV *dbenv;
144	u_int32_t *lk_detectp;
145{
146	DB_LOCKTAB *lt;
147	DB_THREAD_INFO *ip;
148	ENV *env;
149
150	env = dbenv->env;
151
152	ENV_NOT_CONFIGURED(env,
153	    env->lk_handle, "DB_ENV->get_lk_detect", DB_INIT_LOCK);
154
155	if (LOCKING_ON(env)) {
156		lt = env->lk_handle;
157		ENV_ENTER(env, ip);
158		LOCK_REGION_LOCK(env);
159		*lk_detectp = ((DB_LOCKREGION *)lt->reginfo.primary)->detect;
160		LOCK_REGION_UNLOCK(env);
161		ENV_LEAVE(env, ip);
162	} else
163		*lk_detectp = dbenv->lk_detect;
164	return (0);
165}
166
167/*
168 * __lock_set_lk_detect
169 *	DB_ENV->set_lk_detect.
170 *
171 * PUBLIC: int __lock_set_lk_detect __P((DB_ENV *, u_int32_t));
172 */
173int
174__lock_set_lk_detect(dbenv, lk_detect)
175	DB_ENV *dbenv;
176	u_int32_t lk_detect;
177{
178	DB_LOCKREGION *region;
179	DB_LOCKTAB *lt;
180	DB_THREAD_INFO *ip;
181	ENV *env;
182	int ret;
183
184	env = dbenv->env;
185
186	ENV_NOT_CONFIGURED(env,
187	    env->lk_handle, "DB_ENV->set_lk_detect", DB_INIT_LOCK);
188
189	switch (lk_detect) {
190	case DB_LOCK_DEFAULT:
191	case DB_LOCK_EXPIRE:
192	case DB_LOCK_MAXLOCKS:
193	case DB_LOCK_MAXWRITE:
194	case DB_LOCK_MINLOCKS:
195	case DB_LOCK_MINWRITE:
196	case DB_LOCK_OLDEST:
197	case DB_LOCK_RANDOM:
198	case DB_LOCK_YOUNGEST:
199		break;
200	default:
201		__db_errx(env,
202	    "DB_ENV->set_lk_detect: unknown deadlock detection mode specified");
203		return (EINVAL);
204	}
205
206	ret = 0;
207	if (LOCKING_ON(env)) {
208		ENV_ENTER(env, ip);
209
210		lt = env->lk_handle;
211		region = lt->reginfo.primary;
212		LOCK_REGION_LOCK(env);
213		/*
214		 * Check for incompatible automatic deadlock detection requests.
215		 * There are scenarios where changing the detector configuration
216		 * is reasonable, but we disallow them guessing it is likely to
217		 * be an application error.
218		 *
219		 * We allow applications to turn on the lock detector, and we
220		 * ignore attempts to set it to the default or current value.
221		 */
222		if (region->detect != DB_LOCK_NORUN &&
223		    lk_detect != DB_LOCK_DEFAULT &&
224		    region->detect != lk_detect) {
225			__db_errx(env,
226	    "DB_ENV->set_lk_detect: incompatible deadlock detector mode");
227			ret = EINVAL;
228		} else
229			if (region->detect == DB_LOCK_NORUN)
230				region->detect = lk_detect;
231		LOCK_REGION_UNLOCK(env);
232		ENV_LEAVE(env, ip);
233	} else
234		dbenv->lk_detect = lk_detect;
235
236	return (ret);
237}
238
239/*
240 * PUBLIC: int __lock_get_lk_max_locks __P((DB_ENV *, u_int32_t *));
241 */
242int
243__lock_get_lk_max_locks(dbenv, lk_maxp)
244	DB_ENV *dbenv;
245	u_int32_t *lk_maxp;
246{
247	ENV *env;
248
249	env = dbenv->env;
250
251	ENV_NOT_CONFIGURED(env,
252	    env->lk_handle, "DB_ENV->get_lk_maxlocks", DB_INIT_LOCK);
253
254	if (LOCKING_ON(env)) {
255		/* Cannot be set after open, no lock required to read. */
256		*lk_maxp = ((DB_LOCKREGION *)
257		    env->lk_handle->reginfo.primary)->stat.st_maxlocks;
258	} else
259		*lk_maxp = dbenv->lk_max;
260	return (0);
261}
262
263/*
264 * __lock_set_lk_max_locks
265 *	DB_ENV->set_lk_max_locks.
266 *
267 * PUBLIC: int __lock_set_lk_max_locks __P((DB_ENV *, u_int32_t));
268 */
269int
270__lock_set_lk_max_locks(dbenv, lk_max)
271	DB_ENV *dbenv;
272	u_int32_t lk_max;
273{
274	ENV *env;
275
276	env = dbenv->env;
277
278	ENV_ILLEGAL_AFTER_OPEN(env, "DB_ENV->set_lk_max_locks");
279
280	dbenv->lk_max = lk_max;
281	return (0);
282}
283
284/*
285 * PUBLIC: int __lock_get_lk_max_lockers __P((DB_ENV *, u_int32_t *));
286 */
287int
288__lock_get_lk_max_lockers(dbenv, lk_maxp)
289	DB_ENV *dbenv;
290	u_int32_t *lk_maxp;
291{
292	ENV *env;
293
294	env = dbenv->env;
295
296	ENV_NOT_CONFIGURED(env,
297	    env->lk_handle, "DB_ENV->get_lk_max_lockers", DB_INIT_LOCK);
298
299	if (LOCKING_ON(env)) {
300		/* Cannot be set after open, no lock required to read. */
301		*lk_maxp = ((DB_LOCKREGION *)
302		    env->lk_handle->reginfo.primary)->stat.st_maxlockers;
303	} else
304		*lk_maxp = dbenv->lk_max_lockers;
305	return (0);
306}
307
308/*
309 * __lock_set_lk_max_lockers
310 *	DB_ENV->set_lk_max_lockers.
311 *
312 * PUBLIC: int __lock_set_lk_max_lockers __P((DB_ENV *, u_int32_t));
313 */
314int
315__lock_set_lk_max_lockers(dbenv, lk_max)
316	DB_ENV *dbenv;
317	u_int32_t lk_max;
318{
319	ENV *env;
320
321	env = dbenv->env;
322
323	ENV_ILLEGAL_AFTER_OPEN(env, "DB_ENV->set_lk_max_lockers");
324
325	dbenv->lk_max_lockers = lk_max;
326	return (0);
327}
328
329/*
330 * PUBLIC: int __lock_get_lk_max_objects __P((DB_ENV *, u_int32_t *));
331 */
332int
333__lock_get_lk_max_objects(dbenv, lk_maxp)
334	DB_ENV *dbenv;
335	u_int32_t *lk_maxp;
336{
337	ENV *env;
338
339	env = dbenv->env;
340
341	ENV_NOT_CONFIGURED(env,
342	    env->lk_handle, "DB_ENV->get_lk_max_objects", DB_INIT_LOCK);
343
344	if (LOCKING_ON(env)) {
345		/* Cannot be set after open, no lock required to read. */
346		*lk_maxp = ((DB_LOCKREGION *)
347		    env->lk_handle->reginfo.primary)->stat.st_maxobjects;
348	} else
349		*lk_maxp = dbenv->lk_max_objects;
350	return (0);
351}
352
353/*
354 * __lock_set_lk_max_objects
355 *	DB_ENV->set_lk_max_objects.
356 *
357 * PUBLIC: int __lock_set_lk_max_objects __P((DB_ENV *, u_int32_t));
358 */
359int
360__lock_set_lk_max_objects(dbenv, lk_max)
361	DB_ENV *dbenv;
362	u_int32_t lk_max;
363{
364	ENV *env;
365
366	env = dbenv->env;
367
368	ENV_ILLEGAL_AFTER_OPEN(env, "DB_ENV->set_lk_max_objects");
369
370	dbenv->lk_max_objects = lk_max;
371	return (0);
372}
373/*
374 * PUBLIC: int __lock_get_lk_partitions __P((DB_ENV *, u_int32_t *));
375 */
376int
377__lock_get_lk_partitions(dbenv, lk_partitionp)
378	DB_ENV *dbenv;
379	u_int32_t *lk_partitionp;
380{
381	ENV *env;
382
383	env = dbenv->env;
384
385	ENV_NOT_CONFIGURED(env,
386	    env->lk_handle, "DB_ENV->get_lk_partitions", DB_INIT_LOCK);
387
388	if (LOCKING_ON(env)) {
389		/* Cannot be set after open, no lock required to read. */
390		*lk_partitionp = ((DB_LOCKREGION *)
391		    env->lk_handle->reginfo.primary)->stat.st_partitions;
392	} else
393		*lk_partitionp = dbenv->lk_partitions;
394	return (0);
395}
396
397/*
398 * __lock_set_lk_partitions
399 *	DB_ENV->set_lk_partitions.
400 *
401 * PUBLIC: int __lock_set_lk_partitions __P((DB_ENV *, u_int32_t));
402 */
403int
404__lock_set_lk_partitions(dbenv, lk_partitions)
405	DB_ENV *dbenv;
406	u_int32_t lk_partitions;
407{
408	ENV *env;
409
410	env = dbenv->env;
411
412	ENV_ILLEGAL_AFTER_OPEN(env, "DB_ENV->set_lk_partitions");
413
414	dbenv->lk_partitions = lk_partitions;
415	return (0);
416}
417
418/*
419 * PUBLIC: int __lock_get_env_timeout
420 * PUBLIC:     __P((DB_ENV *, db_timeout_t *, u_int32_t));
421 */
422int
423__lock_get_env_timeout(dbenv, timeoutp, flag)
424	DB_ENV *dbenv;
425	db_timeout_t *timeoutp;
426	u_int32_t flag;
427{
428	DB_LOCKREGION *region;
429	DB_LOCKTAB *lt;
430	DB_THREAD_INFO *ip;
431	ENV *env;
432	int ret;
433
434	env = dbenv->env;
435
436	ENV_NOT_CONFIGURED(env,
437	    env->lk_handle, "DB_ENV->get_env_timeout", DB_INIT_LOCK);
438
439	ret = 0;
440	if (LOCKING_ON(env)) {
441		lt = env->lk_handle;
442		region = lt->reginfo.primary;
443		ENV_ENTER(env, ip);
444		LOCK_REGION_LOCK(env);
445		switch (flag) {
446		case DB_SET_LOCK_TIMEOUT:
447			*timeoutp = region->lk_timeout;
448			break;
449		case DB_SET_TXN_TIMEOUT:
450			*timeoutp = region->tx_timeout;
451			break;
452		default:
453			ret = 1;
454			break;
455		}
456		LOCK_REGION_UNLOCK(env);
457		ENV_LEAVE(env, ip);
458	} else
459		switch (flag) {
460		case DB_SET_LOCK_TIMEOUT:
461			*timeoutp = dbenv->lk_timeout;
462			break;
463		case DB_SET_TXN_TIMEOUT:
464			*timeoutp = dbenv->tx_timeout;
465			break;
466		default:
467			ret = 1;
468			break;
469		}
470
471	if (ret)
472		ret = __db_ferr(env, "DB_ENV->get_timeout", 0);
473
474	return (ret);
475}
476
477/*
478 * __lock_set_env_timeout
479 *	DB_ENV->set_lock_timeout.
480 *
481 * PUBLIC: int __lock_set_env_timeout __P((DB_ENV *, db_timeout_t, u_int32_t));
482 */
483int
484__lock_set_env_timeout(dbenv, timeout, flags)
485	DB_ENV *dbenv;
486	db_timeout_t timeout;
487	u_int32_t flags;
488{
489	DB_LOCKREGION *region;
490	DB_LOCKTAB *lt;
491	DB_THREAD_INFO *ip;
492	ENV *env;
493	int ret;
494
495	env = dbenv->env;
496
497	ENV_NOT_CONFIGURED(env,
498	    env->lk_handle, "DB_ENV->set_env_timeout", DB_INIT_LOCK);
499
500	ret = 0;
501	if (LOCKING_ON(env)) {
502		lt = env->lk_handle;
503		region = lt->reginfo.primary;
504		ENV_ENTER(env, ip);
505		LOCK_REGION_LOCK(env);
506		switch (flags) {
507		case DB_SET_LOCK_TIMEOUT:
508			region->lk_timeout = timeout;
509			break;
510		case DB_SET_TXN_TIMEOUT:
511			region->tx_timeout = timeout;
512			break;
513		default:
514			ret = 1;
515			break;
516		}
517		LOCK_REGION_UNLOCK(env);
518		ENV_LEAVE(env, ip);
519	} else
520		switch (flags) {
521		case DB_SET_LOCK_TIMEOUT:
522			dbenv->lk_timeout = timeout;
523			break;
524		case DB_SET_TXN_TIMEOUT:
525			dbenv->tx_timeout = timeout;
526			break;
527		default:
528			ret = 1;
529			break;
530		}
531
532	if (ret)
533		ret = __db_ferr(env, "DB_ENV->set_timeout", 0);
534
535	return (ret);
536}
537