Update to db 2.3.10.
[kopensolaris-gnu/glibc.git] / db2 / txn / txn.c
1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 1996, 1997
5  *      Sleepycat Software.  All rights reserved.
6  */
7 /*
8  * Copyright (c) 1995, 1996
9  *      The President and Fellows of Harvard University.  All rights reserved.
10  *
11  * This code is derived from software contributed to Berkeley by
12  * Margo Seltzer.
13  *
14  * Redistribution and use in source and binary forms, with or without
15  * modification, are permitted provided that the following conditions
16  * are met:
17  * 1. Redistributions of source code must retain the above copyright
18  *    notice, this list of conditions and the following disclaimer.
19  * 2. Redistributions in binary form must reproduce the above copyright
20  *    notice, this list of conditions and the following disclaimer in the
21  *    documentation and/or other materials provided with the distribution.
22  * 3. All advertising materials mentioning features or use of this software
23  *    must display the following acknowledgement:
24  *      This product includes software developed by the University of
25  *      California, Berkeley and its contributors.
26  * 4. Neither the name of the University nor the names of its contributors
27  *    may be used to endorse or promote products derived from this software
28  *    without specific prior written permission.
29  *
30  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
31  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
32  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
34  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
35  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
36  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
37  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
38  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
39  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40  * SUCH DAMAGE.
41  */
42
43 #include "config.h"
44
45 #ifndef lint
46 static const char sccsid[] = "@(#)txn.c 10.30 (Sleepycat) 9/23/97";
47 #endif /* not lint */
48
49
50 #ifndef NO_SYSTEM_INCLUDES
51 #include <sys/types.h>
52 #include <sys/mman.h>
53 #include <sys/stat.h>
54
55 #include <errno.h>
56 #include <fcntl.h>
57 #include <stddef.h>
58 #include <stdio.h>
59 #include <stdlib.h>
60 #include <string.h>
61 #include <time.h>
62 #include <unistd.h>
63 #endif
64
65 #include "shqueue.h"
66 #include "db_int.h"
67 #include "db_page.h"
68 #include "db_shash.h"
69 #include "txn.h"
70 #include "db_dispatch.h"
71 #include "lock.h"
72 #include "log.h"
73 #include "db_am.h"
74 #include "common_ext.h"
75
76 static int __txn_check_running __P((const DB_TXN *));
77 static int __txn_create __P((DB_ENV *, const char *, u_int));
78 static int __txn_end __P((DB_TXN *, int));
79 static int __txn_grow_region __P((DB_TXNMGR *));
80 static int __txn_undo __P((DB_TXN *));
81 static int __txn_validate_region __P((DB_TXNMGR *));
82
83 /*
84  * This file contains the top level routines of the transaction library.
85  * It assumes that a lock manager and log manager that conform to the db_log(3)
86  * and db_lock(3) interfaces exist.
87  *
88  * Create and initialize a transaction region in shared memory.
89  * Return 0 on success, errno on failure.
90  */
91 static int
92 __txn_create(dbenv, path, mode)
93         DB_ENV *dbenv;
94         const char *path;
95         u_int mode;
96 {
97         DB_TXNREGION *txn_region;
98         time_t now;
99         int fd, maxtxns, ret;
100
101         maxtxns = dbenv->tx_max != 0 ? dbenv->tx_max : 1000;
102         (void)time(&now);
103
104         ret = __db_rcreate(dbenv, DB_APP_NONE, path,
105             DEFAULT_TXN_FILE, mode, TXN_REGION_SIZE(maxtxns), &fd, &txn_region);
106
107         /* Region may have existed.  If it didn't, the open will fail. */
108         if (ret != 0)
109                 return (ret);
110
111         txn_region->magic = DB_TXNMAGIC;
112         txn_region->version = DB_TXNVERSION;
113         txn_region->maxtxns = maxtxns;
114         txn_region->last_txnid = TXN_MINIMUM;
115         /* XXX If we ever do more types of locking and logging, this changes. */
116         txn_region->logtype = 0;
117         txn_region->locktype = 0;
118         txn_region->time_ckp = now;
119         ZERO_LSN(txn_region->last_ckp);
120         ZERO_LSN(txn_region->pending_ckp);
121         SH_TAILQ_INIT(&txn_region->active_txn);
122         __db_shalloc_init((void *)&txn_region[1],
123             TXN_REGION_SIZE(maxtxns) - sizeof(DB_TXNREGION));
124
125         /* Unlock the region. */
126         (void)__db_mutex_unlock(&txn_region->hdr.lock, fd);
127
128         /* Now unmap and close the region. */
129         if ((ret = __db_rclose(dbenv, fd, txn_region)) != 0) {
130                 (void)txn_unlink(path, 1 /* force */, dbenv);
131                 return (ret);
132         }
133         return (0);
134 }
135
136 int
137 txn_open(path, flags, mode, dbenv, mgrpp)
138         const char *path;
139         int flags, mode;
140         DB_ENV *dbenv;
141         DB_TXNMGR **mgrpp;
142 {
143         DB_TXNMGR *tmgrp;
144         DB_TXNREGION *txn_regionp;
145         int fd, ret, retry_cnt;
146
147         tmgrp = NULL;
148         txn_regionp = NULL;
149         fd = -1;
150
151         /* Validate arguments. */
152         if (dbenv == NULL)
153                 return (EINVAL);
154 #ifdef HAVE_SPINLOCKS
155 #define OKFLAGS (DB_CREATE | DB_THREAD | DB_TXN_NOSYNC)
156 #else
157 #define OKFLAGS (DB_CREATE | DB_TXN_NOSYNC)
158 #endif
159         if ((ret = __db_fchk(dbenv, "txn_open", flags, OKFLAGS)) != 0)
160                 return (ret);
161
162         retry_cnt = 0;
163 retry:  if (LF_ISSET(DB_CREATE) && (ret = __txn_create(dbenv, path, mode)) != 0)
164                 if (ret == EAGAIN && ++retry_cnt < 0) {
165                         (void)__db_sleep(1, 0);
166                         goto retry;
167                 } else  /* We did not really create the region */
168                         flags &= ~DB_CREATE;
169
170         retry_cnt = 0;
171 retry1: if ((ret = __db_ropen(dbenv, DB_APP_NONE, path, DEFAULT_TXN_FILE,
172             flags & ~(DB_CREATE | DB_THREAD | DB_TXN_NOSYNC),
173             &fd, &txn_regionp)) != 0) {
174                 if (ret == EAGAIN && ++retry_cnt < 3) {
175                         (void)__db_sleep(1, 0);
176                         goto retry1;
177                 }
178                 goto out;
179         }
180
181
182         /* Check if valid region. */
183         if (txn_regionp->magic != DB_TXNMAGIC) {
184                 __db_err(dbenv, "txn_open: Bad magic number");
185                 ret = EINVAL;
186                 goto out;
187         }
188
189         /* Now, create the transaction manager structure and set its fields. */
190         if ((tmgrp = (DB_TXNMGR *)malloc(sizeof(DB_TXNMGR))) == NULL) {
191                 __db_err(dbenv, "txn_open: %s", strerror(ENOMEM));
192                 ret = ENOMEM;
193                 goto out;
194         }
195
196         tmgrp->dbenv = dbenv;
197         tmgrp->recover =
198             dbenv->tx_recover == NULL ? __db_dispatch : dbenv->tx_recover;
199         tmgrp->region = txn_regionp;
200         tmgrp->reg_size = txn_regionp->hdr.size;
201         tmgrp->fd = fd;
202         tmgrp->flags = LF_ISSET(DB_TXN_NOSYNC | DB_THREAD);
203         tmgrp->mem = &txn_regionp[1];
204         tmgrp->mutexp = NULL;
205         TAILQ_INIT(&tmgrp->txn_chain);
206         if (LF_ISSET(DB_THREAD)) {
207                 LOCK_TXNREGION(tmgrp);
208                 if ((ret = __db_shalloc(tmgrp->mem, sizeof(db_mutex_t), 
209                     MUTEX_ALIGNMENT, &tmgrp->mutexp)) == 0)
210                         __db_mutex_init(tmgrp->mutexp, -1);
211                 UNLOCK_TXNREGION(tmgrp);
212                 if (ret != 0)
213                         goto out;
214         }
215         *mgrpp = tmgrp;
216         return (0);
217
218 out:    if (txn_regionp != NULL)
219                 (void)__db_rclose(dbenv, fd, txn_regionp);
220         if (flags & DB_CREATE)
221                 (void)txn_unlink(path, 1, dbenv);
222         if (tmgrp != NULL) {
223                 if (tmgrp->mutexp != NULL) {
224                         LOCK_TXNREGION(tmgrp);
225                         __db_shalloc_free(tmgrp->mem, tmgrp->mutexp);
226                         UNLOCK_TXNREGION(tmgrp);
227                 }
228                 free(tmgrp);
229         }
230         return (ret);
231 }
232
233 /*
234  * Internally, we use TXN_DETAIL structures, but we allocate and return
235  * DB_TXN structures that provide access to the transaction ID and the
236  * offset in the transaction region of the TXN_DETAIL structure.
237  */
238 int
239 txn_begin(tmgrp, parent, txnpp)
240         DB_TXNMGR *tmgrp;
241         DB_TXN *parent;
242         DB_TXN **txnpp;
243 {
244         TXN_DETAIL *txnp;
245         DB_TXN *retp;
246         int id, ret;
247
248         LOCK_TXNREGION(tmgrp);
249
250         if ((ret = __txn_validate_region(tmgrp)) != 0)
251                 goto err;
252
253         /* Allocate a new transaction detail structure. */
254         if ((ret = __db_shalloc(tmgrp->mem, sizeof(TXN_DETAIL), 0, &txnp)) != 0
255             && ret == ENOMEM && (ret = __txn_grow_region(tmgrp)) == 0)
256                 ret = __db_shalloc(tmgrp->mem, sizeof(TXN_DETAIL), 0, &txnp);
257                 
258         if (ret != 0)
259                 goto err;
260
261         /* Make sure that last_txnid is not going to wrap around. */
262         if (tmgrp->region->last_txnid == TXN_INVALID)
263                 return (EINVAL);
264
265         if ((retp = (DB_TXN *)malloc(sizeof(DB_TXN))) == NULL) {
266                 __db_err(tmgrp->dbenv, "txn_begin : %s", strerror(ENOMEM));
267                 ret = ENOMEM;
268                 goto err1;
269         }
270
271         id = ++tmgrp->region->last_txnid;
272         tmgrp->region->nbegins++;
273
274         txnp->txnid = id;
275         ZERO_LSN(txnp->last_lsn);
276         ZERO_LSN(txnp->begin_lsn);
277         txnp->last_lock = 0;
278         txnp->status = TXN_RUNNING;
279         SH_TAILQ_INSERT_HEAD(&tmgrp->region->active_txn,
280             txnp, links, __txn_detail);
281
282         UNLOCK_TXNREGION(tmgrp);
283
284         ZERO_LSN(retp->last_lsn);
285         retp->txnid = id;
286         retp->parent = parent;
287         retp->off = (u_int8_t *)txnp - (u_int8_t *)tmgrp->region;
288         retp->mgrp = tmgrp;
289
290         if (tmgrp->dbenv->lg_info != NULL &&
291             (ret = __txn_regop_log(tmgrp->dbenv->lg_info,
292                 retp, &txnp->begin_lsn, 0, TXN_BEGIN)) != 0) {
293
294                 /* Deallocate transaction. */
295                 LOCK_TXNREGION(tmgrp);
296                 SH_TAILQ_REMOVE(&tmgrp->region->active_txn,
297                     txnp, links, __txn_detail);
298                 __db_shalloc_free(tmgrp->mem, txnp);
299                 UNLOCK_TXNREGION(tmgrp);
300                 free (retp);
301                 return (ret);
302         }
303
304         LOCK_TXNTHREAD(tmgrp);
305         TAILQ_INSERT_TAIL(&tmgrp->txn_chain, retp, links);
306         UNLOCK_TXNTHREAD(tmgrp);
307
308         *txnpp  = retp;
309         return (0);
310
311 err1:
312         __db_shalloc_free(tmgrp->mem, txnp);
313 err:
314         UNLOCK_TXNREGION(tmgrp);
315         return (ret);
316 }
317
318 /* The db_txn(3) man page describes txn_commit. */
319 int
320 txn_commit(txnp)
321         DB_TXN *txnp;
322 {
323         DB_LOG *logp;
324         int ret;
325
326         if ((ret = __txn_check_running(txnp)) != 0)
327                 return (ret);
328
329         /* Sync the log. */
330         if ((logp = txnp->mgrp->dbenv->lg_info) != NULL &&
331             (ret = __txn_regop_log(logp,
332             txnp, &txnp->last_lsn,
333             F_ISSET(txnp->mgrp, DB_TXN_NOSYNC) ? 0 : DB_FLUSH, TXN_COMMIT))
334             != 0)
335                 return (ret);
336
337         return (__txn_end(txnp, 1));
338 }
339
340 /* The db_txn(3) man page describes txn_abort. */
341 int
342 txn_abort(txnp)
343         DB_TXN *txnp;
344 {
345         int ret;
346
347         if ((ret = __txn_check_running(txnp)) != 0)
348                 return (ret);
349
350         if ((ret = __txn_undo(txnp)) != 0) {
351                 __db_err(txnp->mgrp->dbenv,
352                     "txn_abort: Log undo failed %s", strerror(ret));
353                 return (ret);
354         }
355         return (__txn_end(txnp, 0));
356 }
357
358 /*
359  * Flush the log so a future commit is guaranteed to succeed.
360  */
361 int
362 txn_prepare(txnp)
363         DB_TXN *txnp;
364 {
365         int ret;
366         TXN_DETAIL *tp;
367
368         if ((ret = __txn_check_running(txnp)) != 0)
369                 return (ret);
370
371         if (txnp->mgrp->dbenv->lg_info != NULL) {
372                 if ((ret = log_flush(txnp->mgrp->dbenv->lg_info,
373                     &txnp->last_lsn)) != 0)
374                         __db_err(txnp->mgrp->dbenv,
375                             "txn_prepare: log_flush failed %s\n",
376                             strerror(ret));
377                 return (ret);
378         }
379
380         LOCK_TXNTHREAD(txnp->mgrp);
381         tp = (TXN_DETAIL *)((u_int8_t *)txnp->mgrp->region + txnp->off);
382         tp->status = TXN_PREPARED;
383         UNLOCK_TXNTHREAD(txnp->mgrp);
384         return (ret);
385 }
386
387 /*
388  * Return the transaction ID associated with a particular transaction
389  */
390 u_int32_t
391 txn_id(txnp)
392         DB_TXN *txnp;
393 {
394         return (txnp->txnid);
395 }
396
397 /*
398  * The db_txn(3) man page describes txn_close. Currently the caller should
399  * arrange a checkpoint before calling txn_close.
400  */
401 int
402 txn_close(tmgrp)
403         DB_TXNMGR *tmgrp;
404 {
405         DB_TXN *txnp;
406         int ret, t_ret;
407
408         /*
409          * This function had better only be called once per process
410          * (i.e., not per thread), so there should be no synchronization
411          * required.
412          */
413         for (ret = 0, txnp = TAILQ_FIRST(&tmgrp->txn_chain);
414             txnp != TAILQ_END(&tmgrp->txn_chain);
415             txnp = TAILQ_FIRST(&tmgrp->txn_chain)) {
416                 if ((t_ret = txn_abort(txnp)) != 0 && ret == 0)
417                         ret = t_ret;
418         }
419
420         if (tmgrp->dbenv->lg_info && (t_ret =
421             log_flush(tmgrp->dbenv->lg_info, NULL)) != 0 &&
422             ret == 0)
423                 ret = t_ret;
424
425         if (tmgrp->mutexp != NULL) {
426                 LOCK_TXNREGION(tmgrp);
427                 __db_shalloc_free(tmgrp->mem, tmgrp->mutexp);
428                 UNLOCK_TXNREGION(tmgrp);
429         }
430
431         if ((t_ret = __db_rclose(tmgrp->dbenv, tmgrp->fd, tmgrp->region)) != 0
432             && ret == 0)
433                 ret = t_ret;
434
435         if (ret == 0)
436                 free (tmgrp);
437
438         return (ret);
439 }
440
441 /*
442  * The db_txn(3) man page describes txn_unlink.  Right now it is up to
443  * txn_close to write the final checkpoint record.
444  */
445 int
446 txn_unlink(path, force, dbenv)
447         const char *path;
448         int force;
449         DB_ENV *dbenv;
450 {
451         return (__db_runlink(dbenv,
452             DB_APP_NONE, path, DEFAULT_TXN_FILE, force));
453 }
454
455 /* Internal routines. */
456
457 /*
458  * Return 0 if the txnp is reasonable, otherwise returns EINVAL.
459  */
460 static int
461 __txn_check_running(txnp)
462         const DB_TXN *txnp;
463 {
464         TXN_DETAIL *tp;
465
466         tp = NULL;
467         if (txnp != NULL && txnp->mgrp != NULL && txnp->mgrp->region != NULL) {
468                 tp = (TXN_DETAIL *)((u_int8_t *)txnp->mgrp->region + txnp->off);
469                 if (tp->status != TXN_RUNNING)
470                         tp = NULL;
471         }
472
473         return (tp == NULL ? EINVAL : 0);
474 }
475
476 static int
477 __txn_end(txnp, is_commit)
478         DB_TXN *txnp;
479         int is_commit;
480 {
481         DB_TXNMGR *mgr;
482         TXN_DETAIL *tp;
483         DB_LOCKREQ request;
484         int ret;
485         u_int32_t locker;
486
487         mgr = txnp->mgrp;
488
489         LOCK_TXNTHREAD(mgr);
490         TAILQ_REMOVE(&mgr->txn_chain, txnp, links);
491         UNLOCK_TXNTHREAD(mgr);
492
493         /* Release the locks. */
494         locker = txnp->txnid;
495         request.op = DB_LOCK_PUT_ALL;
496
497         if (mgr->dbenv->lk_info) {
498                 ret = lock_vec(mgr->dbenv->lk_info, locker, 0,
499                     &request, 1, NULL);
500                 if (ret != 0 && (ret != DB_LOCK_DEADLOCK || is_commit)) {
501                         __db_err(mgr->dbenv, "%s: release locks failed %s",
502                             is_commit ? "txn_commit" : "txn_abort",
503                             strerror(ret));
504                         return (ret);
505                 }
506         }
507
508         /* End the transaction. */
509         LOCK_TXNREGION(mgr);
510         tp = (TXN_DETAIL *)((u_int8_t *)mgr->region + txnp->off);
511         SH_TAILQ_REMOVE(&mgr->region->active_txn, tp, links, __txn_detail);
512         __db_shalloc_free(mgr->mem, tp);
513         if (is_commit)
514                 mgr->region->ncommits++;
515         else
516                 mgr->region->naborts++;
517         UNLOCK_TXNREGION(mgr);
518
519         FREE(txnp, sizeof(*txnp));
520
521         return (0);
522 }
523
524
525 /*
526  * __txn_undo --
527  *      Undo the transaction with id txnid.  Returns 0 on success and
528  *      errno on failure.
529  */
530 static int
531 __txn_undo(txnp)
532         DB_TXN *txnp;
533 {
534         DB_TXNMGR *mgr;
535         DB_LOG *logp;
536         DBT rdbt;
537         DB_LSN key_lsn;
538         int ret;
539
540         mgr = txnp->mgrp;
541         logp = mgr->dbenv->lg_info;
542         if (logp == NULL)
543                 return (0);
544
545         /*
546          * This is the simplest way to code this, but if the mallocs during
547          * recovery turn out to be a performance issue, we can do the
548          * allocation here and use DB_DBT_USERMEM.
549          */
550         memset(&rdbt, 0, sizeof(rdbt));
551         if (F_ISSET(logp, DB_AM_THREAD))
552                 F_SET(&rdbt, DB_DBT_MALLOC);
553
554         key_lsn = txnp->last_lsn;               /* structure assignment */
555         for (ret = 0; ret == 0 && !IS_ZERO_LSN(key_lsn);) {
556                 /*
557                  * The dispatch routine returns the lsn of the record
558                  * before the current one in the key_lsn argument.
559                  */
560                 if ((ret = log_get(logp, &key_lsn, &rdbt, DB_SET)) == 0) {
561                         ret =
562                             mgr->recover(logp, &rdbt, &key_lsn, TXN_UNDO, NULL);
563                         if (F_ISSET(logp, DB_AM_THREAD) && rdbt.data != NULL) {
564                                 free(rdbt.data);
565                                 rdbt.data = NULL;
566                         }
567                 }
568                 if (ret != 0)
569                         return (ret);
570         }
571
572         return (ret);
573 }
574
575 /*
576  * Transaction checkpoint.
577  * If either kbytes or minutes is non-zero, then we only take the checkpoint
578  * more than "minutes" minutes have passed since the last checkpoint or if
579  * more than "kbytes" of log data have been written since the last checkpoint.
580  * When taking a checkpoint, find the oldest active transaction and figure out
581  * its first LSN.  This is the lowest LSN we can checkpoint, since any record
582  * written after since that point may be involved in a transaction and may
583  * therefore need to be undone in the case of an abort.
584  */
585 int
586 txn_checkpoint(mgr, kbytes, minutes)
587         const DB_TXNMGR *mgr;
588         int kbytes, minutes;
589 {
590         TXN_DETAIL *txnp;
591         DB_LSN ckp_lsn, last_ckp;
592         DB_LOG *dblp;
593         u_int32_t bytes_written;
594         time_t last_ckp_time, now;
595         int ret;
596
597         /* Check usage. */
598         if (kbytes < 0 || minutes < 0)
599                 return (EINVAL);
600
601         /*
602          * Check if we need to run recovery.
603          */
604         ZERO_LSN(ckp_lsn);
605         if (minutes != 0) {
606                 (void)time(&now);
607
608                 LOCK_TXNREGION(mgr);
609                 last_ckp_time = mgr->region->time_ckp;
610                 UNLOCK_TXNREGION(mgr);
611
612                 if (now - last_ckp_time >= (time_t)(minutes * 60))
613                         goto do_ckp;
614         }
615
616         if (kbytes != 0) {
617                 dblp = mgr->dbenv->lg_info;
618                 LOCK_LOGREGION(dblp);
619                 bytes_written = dblp->lp->written;
620                 ckp_lsn = dblp->lp->lsn;
621                 UNLOCK_LOGREGION(dblp);
622                 if (bytes_written >= (u_int32_t)(kbytes * 1024))
623                         goto do_ckp;
624         }
625
626         /*
627          * If we checked time and data and didn't go to checkpoint,
628          * we're done.
629          */
630         if (minutes != 0 || kbytes != 0)
631                 return (0);
632
633 do_ckp:
634         if (IS_ZERO_LSN(ckp_lsn)) {
635                 dblp = mgr->dbenv->lg_info;
636                 LOCK_LOGREGION(dblp);
637                 ckp_lsn = dblp->lp->lsn;
638                 UNLOCK_LOGREGION(dblp);
639         }
640
641         /*
642          * We have to find an LSN such that all transactions begun
643          * before that LSN are complete.
644          */
645         LOCK_TXNREGION(mgr);
646
647         if (!IS_ZERO_LSN(mgr->region->pending_ckp))
648                 ckp_lsn = mgr->region->pending_ckp;
649         else
650                 for (txnp =
651                     SH_TAILQ_FIRST(&mgr->region->active_txn, __txn_detail);
652                     txnp != NULL;
653                     txnp = SH_TAILQ_NEXT(txnp, links, __txn_detail)) {
654
655                         /*
656                          * Look through the active transactions for the
657                          * lowest begin lsn.
658                          */
659                         if (!IS_ZERO_LSN(txnp->begin_lsn) &&
660                             log_compare(&txnp->begin_lsn, &ckp_lsn) < 0)
661                                 ckp_lsn = txnp->begin_lsn;
662                 }
663
664         mgr->region->pending_ckp = ckp_lsn;
665         UNLOCK_TXNREGION(mgr);
666
667         ret = memp_sync(mgr->dbenv->mp_info, &ckp_lsn);
668         if (ret > 0) {
669                 __db_err(mgr->dbenv,
670                     "txn_checkpoint: system failure in memp_sync %s\n",
671                     strerror(ret));
672         } else if (ret == 0 && mgr->dbenv->lg_info != NULL) {
673                 LOCK_TXNREGION(mgr);
674                 last_ckp = mgr->region->last_ckp;
675                 ZERO_LSN(mgr->region->pending_ckp);
676                 UNLOCK_TXNREGION(mgr);
677
678                 if ((ret = __txn_ckp_log(mgr->dbenv->lg_info,
679                    NULL, &ckp_lsn, DB_CHECKPOINT, &ckp_lsn, &last_ckp)) != 0) {
680                         __db_err(mgr->dbenv,
681                             "txn_checkpoint: log failed at LSN [%ld %ld] %s\n",
682                             (long)ckp_lsn.file, (long)ckp_lsn.offset,
683                             strerror(ret));
684                         return (ret);
685                 }
686
687                 LOCK_TXNREGION(mgr);
688                 mgr->region->last_ckp = ckp_lsn;
689                 (void)time(&mgr->region->time_ckp);
690                 UNLOCK_TXNREGION(mgr);
691         }
692         /*
693          * ret < 0 means that there are still buffers to flush; the
694          * checkpoint is not complete. Back off and try again.
695          */
696         return (ret);
697 }
698
699 /*
700  * This is called at every interface to verify if the region
701  * has changed size, and if so, to remap the region in and
702  * reset the process pointers.
703  */
704 static int
705 __txn_validate_region(tp)
706         DB_TXNMGR *tp;
707 {
708         int ret;
709
710         if (tp->reg_size == tp->region->hdr.size)
711                 return (0);
712
713         /* Grow the region. */
714         if ((ret = __db_rremap(tp->dbenv, tp->region,
715             tp->reg_size, tp->region->hdr.size, tp->fd, &tp->region)) != 0)
716                 return (ret);
717
718         tp->reg_size = tp->region->hdr.size;
719         tp->mem = &tp->region[1];
720
721         return (0);
722 }
723
724 static int
725 __txn_grow_region(tp)
726         DB_TXNMGR *tp;
727 {
728         size_t incr;
729         u_int32_t oldmax;
730         u_int8_t *curaddr;
731         int ret;
732
733         oldmax = tp->region->maxtxns;
734         incr = oldmax * sizeof(DB_TXN);
735
736         if ((ret = __db_rgrow(tp->dbenv, tp->fd, incr)) != 0)
737                 return (ret);
738
739         if ((ret = __db_rremap(tp->dbenv, tp->region,
740             tp->reg_size, tp->reg_size + incr, tp->fd, &tp->region)) != 0)
741                 return (ret);
742
743         /* Throw the new space on the free list. */
744         curaddr = (u_int8_t *)tp->region + tp->reg_size;
745         tp->mem = &tp->region[1];
746         tp->reg_size += incr;
747
748         *((size_t *)curaddr) = incr - sizeof(size_t);
749         curaddr += sizeof(size_t);
750         __db_shalloc_free(tp->mem, curaddr);
751
752         tp->region->maxtxns = 2 * oldmax;
753
754         return (0);
755 }
756
757 int
758 txn_stat(mgr, statp, db_malloc)
759         DB_TXNMGR *mgr;
760         DB_TXN_STAT **statp;
761         void *(*db_malloc) __P((size_t));
762 {
763         DB_TXN_STAT *stats;
764         TXN_DETAIL *txnp;
765         size_t nbytes;
766         u_int32_t nactive, ndx;
767
768         LOCK_TXNREGION(mgr);
769         nactive = mgr->region->nbegins -
770             mgr->region->naborts - mgr->region->ncommits;
771         UNLOCK_TXNREGION(mgr);
772
773         /*
774          * Allocate a bunch of extra active structures to handle any
775          * that have been created since we unlocked the region.
776          */
777         nbytes = sizeof(DB_TXN_STAT) + sizeof(DB_TXN_ACTIVE) * (nactive + 200);
778         if (db_malloc == NULL)
779                 stats = (DB_TXN_STAT *)malloc(nbytes);
780         else
781                 stats = (DB_TXN_STAT *)db_malloc(nbytes);
782
783         if (stats == NULL)
784                 return (ENOMEM);
785
786         LOCK_TXNREGION(mgr);
787         stats->st_last_txnid = mgr->region->last_txnid;
788         stats->st_last_ckp = mgr->region->last_ckp;
789         stats->st_maxtxns = mgr->region->maxtxns;
790         stats->st_naborts = mgr->region->naborts;
791         stats->st_nbegins = mgr->region->nbegins;
792         stats->st_ncommits = mgr->region->ncommits;
793         stats->st_pending_ckp = mgr->region->pending_ckp;
794         stats->st_time_ckp = mgr->region->time_ckp;
795         stats->st_nactive = stats->st_nbegins -
796             stats->st_naborts - stats->st_ncommits;
797         if (stats->st_nactive > nactive + 200)
798                 stats->st_nactive = nactive + 200;
799         stats->st_txnarray = (DB_TXN_ACTIVE *)&stats[1];
800
801         ndx = 0;
802         for (txnp = SH_TAILQ_FIRST(&mgr->region->active_txn, __txn_detail);
803             txnp != NULL;
804             txnp = SH_TAILQ_NEXT(txnp, links, __txn_detail)) {
805                 stats->st_txnarray[ndx].txnid = txnp->txnid;
806                 stats->st_txnarray[ndx].lsn = txnp->begin_lsn;
807                 ndx++;
808
809                 if (ndx >= stats->st_nactive)
810                         break;
811         }
812
813         UNLOCK_TXNREGION(mgr);
814         *statp = stats;
815         return (0);
816 }