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, &region->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			    &region->lock_id, &region->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, &lt->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		    &region->free_lockers, __db_locker)) == NULL)
304			return (__lock_nomem(env, "locker entries"));
305		SH_TAILQ_REMOVE(
306		    &region->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		    &lt->locker_tab[indx], sh_locker, links, __db_locker);
329		SH_TAILQ_INSERT_HEAD(&region->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(&lt->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(&lt->reginfo, mlockerp);
378	else {
379		lockerp->master_locker = mlockerp->master_locker;
380		mlockerp = R_ADDR(&lt->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(&lt->locker_tab[indx], sh_locker, links, __db_locker);
455	SH_TAILQ_INSERT_HEAD(
456	    &region->free_lockers, sh_locker, links, __db_locker);
457	SH_TAILQ_REMOVE(&region->lockers, sh_locker, ulinks, __db_locker);
458	region->nlockers--;
459	return (0);
460}
461