1*7c478bd9Sstevel@tonic-gate /*- 2*7c478bd9Sstevel@tonic-gate * See the file LICENSE for redistribution information. 3*7c478bd9Sstevel@tonic-gate * 4*7c478bd9Sstevel@tonic-gate * Copyright (c) 1996, 1997, 1998 5*7c478bd9Sstevel@tonic-gate * Sleepycat Software. All rights reserved. 6*7c478bd9Sstevel@tonic-gate */ 7*7c478bd9Sstevel@tonic-gate /* 8*7c478bd9Sstevel@tonic-gate * Copyright (c) 1995, 1996 9*7c478bd9Sstevel@tonic-gate * The President and Fellows of Harvard University. All rights reserved. 10*7c478bd9Sstevel@tonic-gate * 11*7c478bd9Sstevel@tonic-gate * This code is derived from software contributed to Berkeley by 12*7c478bd9Sstevel@tonic-gate * Margo Seltzer. 13*7c478bd9Sstevel@tonic-gate * 14*7c478bd9Sstevel@tonic-gate * Redistribution and use in source and binary forms, with or without 15*7c478bd9Sstevel@tonic-gate * modification, are permitted provided that the following conditions 16*7c478bd9Sstevel@tonic-gate * are met: 17*7c478bd9Sstevel@tonic-gate * 1. Redistributions of source code must retain the above copyright 18*7c478bd9Sstevel@tonic-gate * notice, this list of conditions and the following disclaimer. 19*7c478bd9Sstevel@tonic-gate * 2. Redistributions in binary form must reproduce the above copyright 20*7c478bd9Sstevel@tonic-gate * notice, this list of conditions and the following disclaimer in the 21*7c478bd9Sstevel@tonic-gate * documentation and/or other materials provided with the distribution. 22*7c478bd9Sstevel@tonic-gate * 3. All advertising materials mentioning features or use of this software 23*7c478bd9Sstevel@tonic-gate * must display the following acknowledgement: 24*7c478bd9Sstevel@tonic-gate * This product includes software developed by the University of 25*7c478bd9Sstevel@tonic-gate * California, Berkeley and its contributors. 26*7c478bd9Sstevel@tonic-gate * 4. Neither the name of the University nor the names of its contributors 27*7c478bd9Sstevel@tonic-gate * may be used to endorse or promote products derived from this software 28*7c478bd9Sstevel@tonic-gate * without specific prior written permission. 29*7c478bd9Sstevel@tonic-gate * 30*7c478bd9Sstevel@tonic-gate * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 31*7c478bd9Sstevel@tonic-gate * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 32*7c478bd9Sstevel@tonic-gate * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 33*7c478bd9Sstevel@tonic-gate * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 34*7c478bd9Sstevel@tonic-gate * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 35*7c478bd9Sstevel@tonic-gate * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 36*7c478bd9Sstevel@tonic-gate * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 37*7c478bd9Sstevel@tonic-gate * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 38*7c478bd9Sstevel@tonic-gate * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 39*7c478bd9Sstevel@tonic-gate * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 40*7c478bd9Sstevel@tonic-gate * SUCH DAMAGE. 41*7c478bd9Sstevel@tonic-gate */ 42*7c478bd9Sstevel@tonic-gate 43*7c478bd9Sstevel@tonic-gate #include "config.h" 44*7c478bd9Sstevel@tonic-gate 45*7c478bd9Sstevel@tonic-gate #ifndef lint 46*7c478bd9Sstevel@tonic-gate static const char sccsid[] = "@(#)txn.c 10.66 (Sleepycat) 1/3/99"; 47*7c478bd9Sstevel@tonic-gate #endif /* not lint */ 48*7c478bd9Sstevel@tonic-gate 49*7c478bd9Sstevel@tonic-gate 50*7c478bd9Sstevel@tonic-gate #ifndef NO_SYSTEM_INCLUDES 51*7c478bd9Sstevel@tonic-gate #include <sys/types.h> 52*7c478bd9Sstevel@tonic-gate 53*7c478bd9Sstevel@tonic-gate #include <errno.h> 54*7c478bd9Sstevel@tonic-gate #include <string.h> 55*7c478bd9Sstevel@tonic-gate #include <time.h> 56*7c478bd9Sstevel@tonic-gate #endif 57*7c478bd9Sstevel@tonic-gate 58*7c478bd9Sstevel@tonic-gate #include "db_int.h" 59*7c478bd9Sstevel@tonic-gate #include "shqueue.h" 60*7c478bd9Sstevel@tonic-gate #include "db_page.h" 61*7c478bd9Sstevel@tonic-gate #include "db_shash.h" 62*7c478bd9Sstevel@tonic-gate #include "txn.h" 63*7c478bd9Sstevel@tonic-gate #include "db_dispatch.h" 64*7c478bd9Sstevel@tonic-gate #include "lock.h" 65*7c478bd9Sstevel@tonic-gate #include "log.h" 66*7c478bd9Sstevel@tonic-gate #include "db_am.h" 67*7c478bd9Sstevel@tonic-gate #include "common_ext.h" 68*7c478bd9Sstevel@tonic-gate 69*7c478bd9Sstevel@tonic-gate static int __txn_begin __P((DB_TXN *)); 70*7c478bd9Sstevel@tonic-gate static int __txn_check_running __P((const DB_TXN *, TXN_DETAIL **)); 71*7c478bd9Sstevel@tonic-gate static int __txn_end __P((DB_TXN *, int)); 72*7c478bd9Sstevel@tonic-gate static void __txn_freekids __P((DB_TXN *)); 73*7c478bd9Sstevel@tonic-gate static int __txn_grow_region __P((DB_TXNMGR *)); 74*7c478bd9Sstevel@tonic-gate static int __txn_init __P((DB_TXNREGION *)); 75*7c478bd9Sstevel@tonic-gate static int __txn_undo __P((DB_TXN *)); 76*7c478bd9Sstevel@tonic-gate static int __txn_validate_region __P((DB_TXNMGR *)); 77*7c478bd9Sstevel@tonic-gate 78*7c478bd9Sstevel@tonic-gate /* 79*7c478bd9Sstevel@tonic-gate * This file contains the top level routines of the transaction library. 80*7c478bd9Sstevel@tonic-gate * It assumes that a lock manager and log manager that conform to the db_log(3) 81*7c478bd9Sstevel@tonic-gate * and db_lock(3) interfaces exist. 82*7c478bd9Sstevel@tonic-gate * 83*7c478bd9Sstevel@tonic-gate * Initialize a transaction region in shared memory. 84*7c478bd9Sstevel@tonic-gate * Return 0 on success, errno on failure. 85*7c478bd9Sstevel@tonic-gate */ 86*7c478bd9Sstevel@tonic-gate static int 87*7c478bd9Sstevel@tonic-gate __txn_init(txn_region) 88*7c478bd9Sstevel@tonic-gate DB_TXNREGION *txn_region; 89*7c478bd9Sstevel@tonic-gate { 90*7c478bd9Sstevel@tonic-gate time_t now; 91*7c478bd9Sstevel@tonic-gate 92*7c478bd9Sstevel@tonic-gate (void)time(&now); 93*7c478bd9Sstevel@tonic-gate 94*7c478bd9Sstevel@tonic-gate /* maxtxns is already initialized. */ 95*7c478bd9Sstevel@tonic-gate txn_region->magic = DB_TXNMAGIC; 96*7c478bd9Sstevel@tonic-gate txn_region->version = DB_TXNVERSION; 97*7c478bd9Sstevel@tonic-gate txn_region->last_txnid = TXN_MINIMUM; 98*7c478bd9Sstevel@tonic-gate /* 99*7c478bd9Sstevel@tonic-gate * XXX 100*7c478bd9Sstevel@tonic-gate * If we ever do more types of locking and logging, this changes. 101*7c478bd9Sstevel@tonic-gate */ 102*7c478bd9Sstevel@tonic-gate txn_region->logtype = 0; 103*7c478bd9Sstevel@tonic-gate txn_region->locktype = 0; 104*7c478bd9Sstevel@tonic-gate txn_region->time_ckp = now; 105*7c478bd9Sstevel@tonic-gate ZERO_LSN(txn_region->last_ckp); 106*7c478bd9Sstevel@tonic-gate ZERO_LSN(txn_region->pending_ckp); 107*7c478bd9Sstevel@tonic-gate SH_TAILQ_INIT(&txn_region->active_txn); 108*7c478bd9Sstevel@tonic-gate __db_shalloc_init((void *)&txn_region[1], 109*7c478bd9Sstevel@tonic-gate TXN_REGION_SIZE(txn_region->maxtxns) - sizeof(DB_TXNREGION)); 110*7c478bd9Sstevel@tonic-gate 111*7c478bd9Sstevel@tonic-gate return (0); 112*7c478bd9Sstevel@tonic-gate } 113*7c478bd9Sstevel@tonic-gate 114*7c478bd9Sstevel@tonic-gate int 115*7c478bd9Sstevel@tonic-gate txn_open(path, flags, mode, dbenv, mgrpp) 116*7c478bd9Sstevel@tonic-gate const char *path; 117*7c478bd9Sstevel@tonic-gate u_int32_t flags; 118*7c478bd9Sstevel@tonic-gate int mode; 119*7c478bd9Sstevel@tonic-gate DB_ENV *dbenv; 120*7c478bd9Sstevel@tonic-gate DB_TXNMGR **mgrpp; 121*7c478bd9Sstevel@tonic-gate { 122*7c478bd9Sstevel@tonic-gate DB_TXNMGR *tmgrp; 123*7c478bd9Sstevel@tonic-gate u_int32_t maxtxns; 124*7c478bd9Sstevel@tonic-gate int ret; 125*7c478bd9Sstevel@tonic-gate 126*7c478bd9Sstevel@tonic-gate /* Validate arguments. */ 127*7c478bd9Sstevel@tonic-gate if (dbenv == NULL) 128*7c478bd9Sstevel@tonic-gate return (EINVAL); 129*7c478bd9Sstevel@tonic-gate #ifdef HAVE_SPINLOCKS 130*7c478bd9Sstevel@tonic-gate #define OKFLAGS (DB_CREATE | DB_THREAD | DB_TXN_NOSYNC) 131*7c478bd9Sstevel@tonic-gate #else 132*7c478bd9Sstevel@tonic-gate #define OKFLAGS (DB_CREATE | DB_TXN_NOSYNC) 133*7c478bd9Sstevel@tonic-gate #endif 134*7c478bd9Sstevel@tonic-gate if ((ret = __db_fchk(dbenv, "txn_open", flags, OKFLAGS)) != 0) 135*7c478bd9Sstevel@tonic-gate return (ret); 136*7c478bd9Sstevel@tonic-gate 137*7c478bd9Sstevel@tonic-gate maxtxns = dbenv->tx_max != 0 ? dbenv->tx_max : 20; 138*7c478bd9Sstevel@tonic-gate 139*7c478bd9Sstevel@tonic-gate /* Now, create the transaction manager structure and set its fields. */ 140*7c478bd9Sstevel@tonic-gate if ((ret = __os_calloc(1, sizeof(DB_TXNMGR), &tmgrp)) != 0) 141*7c478bd9Sstevel@tonic-gate return (ret); 142*7c478bd9Sstevel@tonic-gate 143*7c478bd9Sstevel@tonic-gate /* Initialize the transaction manager structure. */ 144*7c478bd9Sstevel@tonic-gate tmgrp->mutexp = NULL; 145*7c478bd9Sstevel@tonic-gate tmgrp->dbenv = dbenv; 146*7c478bd9Sstevel@tonic-gate tmgrp->recover = 147*7c478bd9Sstevel@tonic-gate dbenv->tx_recover == NULL ? __db_dispatch : dbenv->tx_recover; 148*7c478bd9Sstevel@tonic-gate tmgrp->flags = LF_ISSET(DB_TXN_NOSYNC | DB_THREAD); 149*7c478bd9Sstevel@tonic-gate TAILQ_INIT(&tmgrp->txn_chain); 150*7c478bd9Sstevel@tonic-gate 151*7c478bd9Sstevel@tonic-gate /* Join/create the txn region. */ 152*7c478bd9Sstevel@tonic-gate tmgrp->reginfo.dbenv = dbenv; 153*7c478bd9Sstevel@tonic-gate tmgrp->reginfo.appname = DB_APP_NONE; 154*7c478bd9Sstevel@tonic-gate if (path == NULL) 155*7c478bd9Sstevel@tonic-gate tmgrp->reginfo.path = NULL; 156*7c478bd9Sstevel@tonic-gate else 157*7c478bd9Sstevel@tonic-gate if ((ret = __os_strdup(path, &tmgrp->reginfo.path)) != 0) 158*7c478bd9Sstevel@tonic-gate goto err; 159*7c478bd9Sstevel@tonic-gate tmgrp->reginfo.file = DEFAULT_TXN_FILE; 160*7c478bd9Sstevel@tonic-gate tmgrp->reginfo.mode = mode; 161*7c478bd9Sstevel@tonic-gate tmgrp->reginfo.size = TXN_REGION_SIZE(maxtxns); 162*7c478bd9Sstevel@tonic-gate tmgrp->reginfo.dbflags = flags; 163*7c478bd9Sstevel@tonic-gate tmgrp->reginfo.addr = NULL; 164*7c478bd9Sstevel@tonic-gate tmgrp->reginfo.fd = -1; 165*7c478bd9Sstevel@tonic-gate tmgrp->reginfo.flags = dbenv->tx_max == 0 ? REGION_SIZEDEF : 0; 166*7c478bd9Sstevel@tonic-gate if ((ret = __db_rattach(&tmgrp->reginfo)) != 0) 167*7c478bd9Sstevel@tonic-gate goto err; 168*7c478bd9Sstevel@tonic-gate 169*7c478bd9Sstevel@tonic-gate /* Fill in region-related fields. */ 170*7c478bd9Sstevel@tonic-gate tmgrp->region = tmgrp->reginfo.addr; 171*7c478bd9Sstevel@tonic-gate tmgrp->mem = &tmgrp->region[1]; 172*7c478bd9Sstevel@tonic-gate 173*7c478bd9Sstevel@tonic-gate if (F_ISSET(&tmgrp->reginfo, REGION_CREATED)) { 174*7c478bd9Sstevel@tonic-gate tmgrp->region->maxtxns = maxtxns; 175*7c478bd9Sstevel@tonic-gate if ((ret = __txn_init(tmgrp->region)) != 0) 176*7c478bd9Sstevel@tonic-gate goto err; 177*7c478bd9Sstevel@tonic-gate 178*7c478bd9Sstevel@tonic-gate } else if (tmgrp->region->magic != DB_TXNMAGIC) { 179*7c478bd9Sstevel@tonic-gate /* Check if valid region. */ 180*7c478bd9Sstevel@tonic-gate __db_err(dbenv, "txn_open: Bad magic number"); 181*7c478bd9Sstevel@tonic-gate ret = EINVAL; 182*7c478bd9Sstevel@tonic-gate goto err; 183*7c478bd9Sstevel@tonic-gate } 184*7c478bd9Sstevel@tonic-gate 185*7c478bd9Sstevel@tonic-gate if (LF_ISSET(DB_THREAD)) { 186*7c478bd9Sstevel@tonic-gate if ((ret = __db_shalloc(tmgrp->mem, sizeof(db_mutex_t), 187*7c478bd9Sstevel@tonic-gate MUTEX_ALIGNMENT, &tmgrp->mutexp)) == 0) 188*7c478bd9Sstevel@tonic-gate /* 189*7c478bd9Sstevel@tonic-gate * Since we only get here if threading is turned on, we 190*7c478bd9Sstevel@tonic-gate * know that we have spinlocks, so the offset is going 191*7c478bd9Sstevel@tonic-gate * to be ignored. We put 0 here as a valid placeholder. 192*7c478bd9Sstevel@tonic-gate */ 193*7c478bd9Sstevel@tonic-gate __db_mutex_init(tmgrp->mutexp, 0); 194*7c478bd9Sstevel@tonic-gate if (ret != 0) 195*7c478bd9Sstevel@tonic-gate goto err; 196*7c478bd9Sstevel@tonic-gate } 197*7c478bd9Sstevel@tonic-gate 198*7c478bd9Sstevel@tonic-gate UNLOCK_TXNREGION(tmgrp); 199*7c478bd9Sstevel@tonic-gate *mgrpp = tmgrp; 200*7c478bd9Sstevel@tonic-gate return (0); 201*7c478bd9Sstevel@tonic-gate 202*7c478bd9Sstevel@tonic-gate err: if (tmgrp->reginfo.addr != NULL) { 203*7c478bd9Sstevel@tonic-gate if (tmgrp->mutexp != NULL) 204*7c478bd9Sstevel@tonic-gate __db_shalloc_free(tmgrp->mem, tmgrp->mutexp); 205*7c478bd9Sstevel@tonic-gate 206*7c478bd9Sstevel@tonic-gate UNLOCK_TXNREGION(tmgrp); 207*7c478bd9Sstevel@tonic-gate (void)__db_rdetach(&tmgrp->reginfo); 208*7c478bd9Sstevel@tonic-gate if (F_ISSET(&tmgrp->reginfo, REGION_CREATED)) 209*7c478bd9Sstevel@tonic-gate (void)txn_unlink(path, 1, dbenv); 210*7c478bd9Sstevel@tonic-gate } 211*7c478bd9Sstevel@tonic-gate 212*7c478bd9Sstevel@tonic-gate if (tmgrp->reginfo.path != NULL) 213*7c478bd9Sstevel@tonic-gate __os_freestr(tmgrp->reginfo.path); 214*7c478bd9Sstevel@tonic-gate __os_free(tmgrp, sizeof(*tmgrp)); 215*7c478bd9Sstevel@tonic-gate return (ret); 216*7c478bd9Sstevel@tonic-gate } 217*7c478bd9Sstevel@tonic-gate 218*7c478bd9Sstevel@tonic-gate /* 219*7c478bd9Sstevel@tonic-gate * __txn_panic -- 220*7c478bd9Sstevel@tonic-gate * Panic a transaction region. 221*7c478bd9Sstevel@tonic-gate * 222*7c478bd9Sstevel@tonic-gate * PUBLIC: void __txn_panic __P((DB_ENV *)); 223*7c478bd9Sstevel@tonic-gate */ 224*7c478bd9Sstevel@tonic-gate void 225*7c478bd9Sstevel@tonic-gate __txn_panic(dbenv) 226*7c478bd9Sstevel@tonic-gate DB_ENV *dbenv; 227*7c478bd9Sstevel@tonic-gate { 228*7c478bd9Sstevel@tonic-gate if (dbenv->tx_info != NULL) 229*7c478bd9Sstevel@tonic-gate dbenv->tx_info->region->hdr.panic = 1; 230*7c478bd9Sstevel@tonic-gate } 231*7c478bd9Sstevel@tonic-gate 232*7c478bd9Sstevel@tonic-gate /* 233*7c478bd9Sstevel@tonic-gate * txn_begin -- 234*7c478bd9Sstevel@tonic-gate * This is a wrapper to the actual begin process. Normal txn_begin() 235*7c478bd9Sstevel@tonic-gate * allocates a DB_TXN structure for the caller, while txn_xa_begin() does 236*7c478bd9Sstevel@tonic-gate * not. Other than that, both call into the common __txn_begin code(). 237*7c478bd9Sstevel@tonic-gate * 238*7c478bd9Sstevel@tonic-gate * Internally, we use TXN_DETAIL structures, but the DB_TXN structure 239*7c478bd9Sstevel@tonic-gate * provides access to the transaction ID and the offset in the transaction 240*7c478bd9Sstevel@tonic-gate * region of the TXN_DETAIL structure. 241*7c478bd9Sstevel@tonic-gate */ 242*7c478bd9Sstevel@tonic-gate int 243*7c478bd9Sstevel@tonic-gate txn_begin(tmgrp, parent, txnpp) 244*7c478bd9Sstevel@tonic-gate DB_TXNMGR *tmgrp; 245*7c478bd9Sstevel@tonic-gate DB_TXN *parent, **txnpp; 246*7c478bd9Sstevel@tonic-gate { 247*7c478bd9Sstevel@tonic-gate DB_TXN *txn; 248*7c478bd9Sstevel@tonic-gate int ret; 249*7c478bd9Sstevel@tonic-gate 250*7c478bd9Sstevel@tonic-gate TXN_PANIC_CHECK(tmgrp); 251*7c478bd9Sstevel@tonic-gate 252*7c478bd9Sstevel@tonic-gate if ((ret = __os_calloc(1, sizeof(DB_TXN), &txn)) != 0) 253*7c478bd9Sstevel@tonic-gate return (ret); 254*7c478bd9Sstevel@tonic-gate 255*7c478bd9Sstevel@tonic-gate txn->parent = parent; 256*7c478bd9Sstevel@tonic-gate TAILQ_INIT(&txn->kids); 257*7c478bd9Sstevel@tonic-gate txn->mgrp = tmgrp; 258*7c478bd9Sstevel@tonic-gate txn->flags = TXN_MALLOC; 259*7c478bd9Sstevel@tonic-gate if ((ret = __txn_begin(txn)) != 0) { 260*7c478bd9Sstevel@tonic-gate __os_free(txn, sizeof(DB_TXN)); 261*7c478bd9Sstevel@tonic-gate txn = NULL; 262*7c478bd9Sstevel@tonic-gate } 263*7c478bd9Sstevel@tonic-gate if (txn != NULL && parent != NULL) 264*7c478bd9Sstevel@tonic-gate TAILQ_INSERT_HEAD(&parent->kids, txn, klinks); 265*7c478bd9Sstevel@tonic-gate *txnpp = txn; 266*7c478bd9Sstevel@tonic-gate return (ret); 267*7c478bd9Sstevel@tonic-gate } 268*7c478bd9Sstevel@tonic-gate 269*7c478bd9Sstevel@tonic-gate /* 270*7c478bd9Sstevel@tonic-gate * __txn_xa_begin -- 271*7c478bd9Sstevel@tonic-gate * XA version of txn_begin. 272*7c478bd9Sstevel@tonic-gate * 273*7c478bd9Sstevel@tonic-gate * PUBLIC: int __txn_xa_begin __P((DB_ENV *, DB_TXN *)); 274*7c478bd9Sstevel@tonic-gate */ 275*7c478bd9Sstevel@tonic-gate int 276*7c478bd9Sstevel@tonic-gate __txn_xa_begin(dbenv, txn) 277*7c478bd9Sstevel@tonic-gate DB_ENV *dbenv; 278*7c478bd9Sstevel@tonic-gate DB_TXN *txn; 279*7c478bd9Sstevel@tonic-gate { 280*7c478bd9Sstevel@tonic-gate TXN_PANIC_CHECK(dbenv->tx_info); 281*7c478bd9Sstevel@tonic-gate 282*7c478bd9Sstevel@tonic-gate memset(txn, 0, sizeof(DB_TXN)); 283*7c478bd9Sstevel@tonic-gate 284*7c478bd9Sstevel@tonic-gate txn->mgrp = dbenv->tx_info; 285*7c478bd9Sstevel@tonic-gate 286*7c478bd9Sstevel@tonic-gate return (__txn_begin(txn)); 287*7c478bd9Sstevel@tonic-gate } 288*7c478bd9Sstevel@tonic-gate 289*7c478bd9Sstevel@tonic-gate /* 290*7c478bd9Sstevel@tonic-gate * __txn_begin -- 291*7c478bd9Sstevel@tonic-gate * Normal DB version of txn_begin. 292*7c478bd9Sstevel@tonic-gate */ 293*7c478bd9Sstevel@tonic-gate static int 294*7c478bd9Sstevel@tonic-gate __txn_begin(txn) 295*7c478bd9Sstevel@tonic-gate DB_TXN *txn; 296*7c478bd9Sstevel@tonic-gate { 297*7c478bd9Sstevel@tonic-gate DB_LSN begin_lsn; 298*7c478bd9Sstevel@tonic-gate DB_TXNMGR *mgr; 299*7c478bd9Sstevel@tonic-gate TXN_DETAIL *td; 300*7c478bd9Sstevel@tonic-gate size_t off; 301*7c478bd9Sstevel@tonic-gate u_int32_t id; 302*7c478bd9Sstevel@tonic-gate int ret; 303*7c478bd9Sstevel@tonic-gate 304*7c478bd9Sstevel@tonic-gate /* 305*7c478bd9Sstevel@tonic-gate * We do not have to write begin records (and if we do not, then we 306*7c478bd9Sstevel@tonic-gate * need never write records for read-only transactions). However, 307*7c478bd9Sstevel@tonic-gate * we do need to find the current LSN so that we can store it in the 308*7c478bd9Sstevel@tonic-gate * transaction structure, so we can know where to take checkpoints. 309*7c478bd9Sstevel@tonic-gate */ 310*7c478bd9Sstevel@tonic-gate mgr = txn->mgrp; 311*7c478bd9Sstevel@tonic-gate if (mgr->dbenv->lg_info != NULL && (ret = 312*7c478bd9Sstevel@tonic-gate log_put(mgr->dbenv->lg_info, &begin_lsn, NULL, DB_CURLSN)) != 0) 313*7c478bd9Sstevel@tonic-gate goto err2; 314*7c478bd9Sstevel@tonic-gate 315*7c478bd9Sstevel@tonic-gate LOCK_TXNREGION(mgr); 316*7c478bd9Sstevel@tonic-gate 317*7c478bd9Sstevel@tonic-gate /* Make sure that last_txnid is not going to wrap around. */ 318*7c478bd9Sstevel@tonic-gate if (mgr->region->last_txnid == TXN_INVALID) { 319*7c478bd9Sstevel@tonic-gate __db_err(mgr->dbenv, "txn_begin: %s %s", 320*7c478bd9Sstevel@tonic-gate "Transaction ID wrapping.", 321*7c478bd9Sstevel@tonic-gate "Snapshot your database and start a new log."); 322*7c478bd9Sstevel@tonic-gate ret = EINVAL; 323*7c478bd9Sstevel@tonic-gate goto err1; 324*7c478bd9Sstevel@tonic-gate } 325*7c478bd9Sstevel@tonic-gate 326*7c478bd9Sstevel@tonic-gate if ((ret = __txn_validate_region(mgr)) != 0) 327*7c478bd9Sstevel@tonic-gate goto err1; 328*7c478bd9Sstevel@tonic-gate 329*7c478bd9Sstevel@tonic-gate /* Allocate a new transaction detail structure. */ 330*7c478bd9Sstevel@tonic-gate if ((ret = __db_shalloc(mgr->mem, sizeof(TXN_DETAIL), 0, &td)) != 0 331*7c478bd9Sstevel@tonic-gate && ret == ENOMEM && (ret = __txn_grow_region(mgr)) == 0) 332*7c478bd9Sstevel@tonic-gate ret = __db_shalloc(mgr->mem, sizeof(TXN_DETAIL), 0, &td); 333*7c478bd9Sstevel@tonic-gate if (ret != 0) 334*7c478bd9Sstevel@tonic-gate goto err1; 335*7c478bd9Sstevel@tonic-gate 336*7c478bd9Sstevel@tonic-gate /* Place transaction on active transaction list. */ 337*7c478bd9Sstevel@tonic-gate SH_TAILQ_INSERT_HEAD(&mgr->region->active_txn, td, links, __txn_detail); 338*7c478bd9Sstevel@tonic-gate 339*7c478bd9Sstevel@tonic-gate id = ++mgr->region->last_txnid; 340*7c478bd9Sstevel@tonic-gate ++mgr->region->nbegins; 341*7c478bd9Sstevel@tonic-gate 342*7c478bd9Sstevel@tonic-gate td->txnid = id; 343*7c478bd9Sstevel@tonic-gate td->begin_lsn = begin_lsn; 344*7c478bd9Sstevel@tonic-gate ZERO_LSN(td->last_lsn); 345*7c478bd9Sstevel@tonic-gate td->last_lock = 0; 346*7c478bd9Sstevel@tonic-gate td->status = TXN_RUNNING; 347*7c478bd9Sstevel@tonic-gate if (txn->parent != NULL) 348*7c478bd9Sstevel@tonic-gate td->parent = txn->parent->off; 349*7c478bd9Sstevel@tonic-gate else 350*7c478bd9Sstevel@tonic-gate td->parent = 0; 351*7c478bd9Sstevel@tonic-gate 352*7c478bd9Sstevel@tonic-gate off = (u_int8_t *)td - (u_int8_t *)mgr->region; 353*7c478bd9Sstevel@tonic-gate UNLOCK_TXNREGION(mgr); 354*7c478bd9Sstevel@tonic-gate 355*7c478bd9Sstevel@tonic-gate ZERO_LSN(txn->last_lsn); 356*7c478bd9Sstevel@tonic-gate txn->txnid = id; 357*7c478bd9Sstevel@tonic-gate txn->off = off; 358*7c478bd9Sstevel@tonic-gate 359*7c478bd9Sstevel@tonic-gate if (F_ISSET(txn, TXN_MALLOC)) { 360*7c478bd9Sstevel@tonic-gate LOCK_TXNTHREAD(mgr); 361*7c478bd9Sstevel@tonic-gate TAILQ_INSERT_TAIL(&mgr->txn_chain, txn, links); 362*7c478bd9Sstevel@tonic-gate UNLOCK_TXNTHREAD(mgr); 363*7c478bd9Sstevel@tonic-gate } 364*7c478bd9Sstevel@tonic-gate 365*7c478bd9Sstevel@tonic-gate return (0); 366*7c478bd9Sstevel@tonic-gate 367*7c478bd9Sstevel@tonic-gate err1: UNLOCK_TXNREGION(mgr); 368*7c478bd9Sstevel@tonic-gate 369*7c478bd9Sstevel@tonic-gate err2: return (ret); 370*7c478bd9Sstevel@tonic-gate } 371*7c478bd9Sstevel@tonic-gate /* 372*7c478bd9Sstevel@tonic-gate * txn_commit -- 373*7c478bd9Sstevel@tonic-gate * Commit a transaction. 374*7c478bd9Sstevel@tonic-gate */ 375*7c478bd9Sstevel@tonic-gate int 376*7c478bd9Sstevel@tonic-gate txn_commit(txnp) 377*7c478bd9Sstevel@tonic-gate DB_TXN *txnp; 378*7c478bd9Sstevel@tonic-gate { 379*7c478bd9Sstevel@tonic-gate DB_LOG *logp; 380*7c478bd9Sstevel@tonic-gate DB_TXNMGR *mgr; 381*7c478bd9Sstevel@tonic-gate int ret; 382*7c478bd9Sstevel@tonic-gate 383*7c478bd9Sstevel@tonic-gate mgr = txnp->mgrp; 384*7c478bd9Sstevel@tonic-gate 385*7c478bd9Sstevel@tonic-gate TXN_PANIC_CHECK(mgr); 386*7c478bd9Sstevel@tonic-gate if ((ret = __txn_check_running(txnp, NULL)) != 0) 387*7c478bd9Sstevel@tonic-gate return (ret); 388*7c478bd9Sstevel@tonic-gate 389*7c478bd9Sstevel@tonic-gate /* 390*7c478bd9Sstevel@tonic-gate * If there are any log records, write a log record and sync 391*7c478bd9Sstevel@tonic-gate * the log, else do no log writes. If the commit is for a child 392*7c478bd9Sstevel@tonic-gate * transaction, we do not need to commit the child synchronously 393*7c478bd9Sstevel@tonic-gate * since if its parent aborts, it will abort too and its parent 394*7c478bd9Sstevel@tonic-gate * (or ultimate ancestor) will write synchronously. 395*7c478bd9Sstevel@tonic-gate */ 396*7c478bd9Sstevel@tonic-gate if ((logp = mgr->dbenv->lg_info) != NULL && 397*7c478bd9Sstevel@tonic-gate !IS_ZERO_LSN(txnp->last_lsn)) { 398*7c478bd9Sstevel@tonic-gate if (txnp->parent == NULL) 399*7c478bd9Sstevel@tonic-gate ret = __txn_regop_log(logp, txnp, &txnp->last_lsn, 400*7c478bd9Sstevel@tonic-gate F_ISSET(mgr, DB_TXN_NOSYNC) ? 0 : DB_FLUSH, 401*7c478bd9Sstevel@tonic-gate TXN_COMMIT); 402*7c478bd9Sstevel@tonic-gate else 403*7c478bd9Sstevel@tonic-gate ret = __txn_child_log(logp, txnp, &txnp->last_lsn, 0, 404*7c478bd9Sstevel@tonic-gate TXN_COMMIT, txnp->parent->txnid); 405*7c478bd9Sstevel@tonic-gate if (ret != 0) 406*7c478bd9Sstevel@tonic-gate return (ret); 407*7c478bd9Sstevel@tonic-gate } 408*7c478bd9Sstevel@tonic-gate 409*7c478bd9Sstevel@tonic-gate /* 410*7c478bd9Sstevel@tonic-gate * If this is the senior ancestor (i.e., it has no children), then we 411*7c478bd9Sstevel@tonic-gate * can release all the child transactions since everyone is committing. 412*7c478bd9Sstevel@tonic-gate * Then we can release this transaction. If this is not the ultimate 413*7c478bd9Sstevel@tonic-gate * ancestor, then we can neither free it or its children. 414*7c478bd9Sstevel@tonic-gate */ 415*7c478bd9Sstevel@tonic-gate if (txnp->parent == NULL) 416*7c478bd9Sstevel@tonic-gate __txn_freekids(txnp); 417*7c478bd9Sstevel@tonic-gate 418*7c478bd9Sstevel@tonic-gate return (__txn_end(txnp, 1)); 419*7c478bd9Sstevel@tonic-gate } 420*7c478bd9Sstevel@tonic-gate 421*7c478bd9Sstevel@tonic-gate /* 422*7c478bd9Sstevel@tonic-gate * txn_abort -- 423*7c478bd9Sstevel@tonic-gate * Abort a transcation. 424*7c478bd9Sstevel@tonic-gate */ 425*7c478bd9Sstevel@tonic-gate int 426*7c478bd9Sstevel@tonic-gate txn_abort(txnp) 427*7c478bd9Sstevel@tonic-gate DB_TXN *txnp; 428*7c478bd9Sstevel@tonic-gate { 429*7c478bd9Sstevel@tonic-gate int ret; 430*7c478bd9Sstevel@tonic-gate DB_TXN *kids; 431*7c478bd9Sstevel@tonic-gate 432*7c478bd9Sstevel@tonic-gate TXN_PANIC_CHECK(txnp->mgrp); 433*7c478bd9Sstevel@tonic-gate if ((ret = __txn_check_running(txnp, NULL)) != 0) 434*7c478bd9Sstevel@tonic-gate return (ret); 435*7c478bd9Sstevel@tonic-gate 436*7c478bd9Sstevel@tonic-gate for (kids = TAILQ_FIRST(&txnp->kids); 437*7c478bd9Sstevel@tonic-gate kids != NULL; 438*7c478bd9Sstevel@tonic-gate kids = TAILQ_FIRST(&txnp->kids)) 439*7c478bd9Sstevel@tonic-gate txn_abort(kids); 440*7c478bd9Sstevel@tonic-gate 441*7c478bd9Sstevel@tonic-gate if ((ret = __txn_undo(txnp)) != 0) { 442*7c478bd9Sstevel@tonic-gate __db_err(txnp->mgrp->dbenv, 443*7c478bd9Sstevel@tonic-gate "txn_abort: Log undo failed %s", strerror(ret)); 444*7c478bd9Sstevel@tonic-gate return (ret); 445*7c478bd9Sstevel@tonic-gate } 446*7c478bd9Sstevel@tonic-gate return (__txn_end(txnp, 0)); 447*7c478bd9Sstevel@tonic-gate } 448*7c478bd9Sstevel@tonic-gate 449*7c478bd9Sstevel@tonic-gate /* 450*7c478bd9Sstevel@tonic-gate * txn_prepare -- 451*7c478bd9Sstevel@tonic-gate * Flush the log so a future commit is guaranteed to succeed. 452*7c478bd9Sstevel@tonic-gate */ 453*7c478bd9Sstevel@tonic-gate int 454*7c478bd9Sstevel@tonic-gate txn_prepare(txnp) 455*7c478bd9Sstevel@tonic-gate DB_TXN *txnp; 456*7c478bd9Sstevel@tonic-gate { 457*7c478bd9Sstevel@tonic-gate DBT xid; 458*7c478bd9Sstevel@tonic-gate DB_ENV *dbenv; 459*7c478bd9Sstevel@tonic-gate TXN_DETAIL *td; 460*7c478bd9Sstevel@tonic-gate int ret; 461*7c478bd9Sstevel@tonic-gate 462*7c478bd9Sstevel@tonic-gate if ((ret = __txn_check_running(txnp, &td)) != 0) 463*7c478bd9Sstevel@tonic-gate return (ret); 464*7c478bd9Sstevel@tonic-gate 465*7c478bd9Sstevel@tonic-gate dbenv = txnp->mgrp->dbenv; 466*7c478bd9Sstevel@tonic-gate memset(&xid, 0, sizeof(xid)); 467*7c478bd9Sstevel@tonic-gate xid.data = td->xid; 468*7c478bd9Sstevel@tonic-gate /* 469*7c478bd9Sstevel@tonic-gate * We indicate that a transaction is an XA transaction by putting 470*7c478bd9Sstevel@tonic-gate * a valid size in the xid.size fiels. XA requires that the transaction 471*7c478bd9Sstevel@tonic-gate * be either ENDED or SUSPENDED when prepare is called, so we know 472*7c478bd9Sstevel@tonic-gate * that if the xa_status isn't in one of those states, but we are 473*7c478bd9Sstevel@tonic-gate * calling prepare that we are not an XA transaction. 474*7c478bd9Sstevel@tonic-gate */ 475*7c478bd9Sstevel@tonic-gate xid.size = 476*7c478bd9Sstevel@tonic-gate td->xa_status != TXN_XA_ENDED && td->xa_status != TXN_XA_SUSPENDED ? 477*7c478bd9Sstevel@tonic-gate 0 : sizeof(td->xid); 478*7c478bd9Sstevel@tonic-gate if (dbenv->lg_info != NULL && 479*7c478bd9Sstevel@tonic-gate (ret = __txn_xa_regop_log(dbenv->lg_info, txnp, &txnp->last_lsn, 480*7c478bd9Sstevel@tonic-gate F_ISSET(txnp->mgrp, DB_TXN_NOSYNC) ? 0 : DB_FLUSH, TXN_PREPARE, 481*7c478bd9Sstevel@tonic-gate &xid, td->format, td->gtrid, td->bqual, &td->begin_lsn)) != 0) { 482*7c478bd9Sstevel@tonic-gate __db_err(dbenv, 483*7c478bd9Sstevel@tonic-gate "txn_prepare: log_write failed %s\n", strerror(ret)); 484*7c478bd9Sstevel@tonic-gate return (ret); 485*7c478bd9Sstevel@tonic-gate } 486*7c478bd9Sstevel@tonic-gate 487*7c478bd9Sstevel@tonic-gate LOCK_TXNTHREAD(txnp->mgrp); 488*7c478bd9Sstevel@tonic-gate td->status = TXN_PREPARED; 489*7c478bd9Sstevel@tonic-gate UNLOCK_TXNTHREAD(txnp->mgrp); 490*7c478bd9Sstevel@tonic-gate return (ret); 491*7c478bd9Sstevel@tonic-gate } 492*7c478bd9Sstevel@tonic-gate 493*7c478bd9Sstevel@tonic-gate /* 494*7c478bd9Sstevel@tonic-gate * Return the transaction ID associated with a particular transaction 495*7c478bd9Sstevel@tonic-gate */ 496*7c478bd9Sstevel@tonic-gate u_int32_t 497*7c478bd9Sstevel@tonic-gate txn_id(txnp) 498*7c478bd9Sstevel@tonic-gate DB_TXN *txnp; 499*7c478bd9Sstevel@tonic-gate { 500*7c478bd9Sstevel@tonic-gate return (txnp->txnid); 501*7c478bd9Sstevel@tonic-gate } 502*7c478bd9Sstevel@tonic-gate 503*7c478bd9Sstevel@tonic-gate /* 504*7c478bd9Sstevel@tonic-gate * txn_close -- 505*7c478bd9Sstevel@tonic-gate * Close the transaction region, does not imply a checkpoint. 506*7c478bd9Sstevel@tonic-gate */ 507*7c478bd9Sstevel@tonic-gate int 508*7c478bd9Sstevel@tonic-gate txn_close(tmgrp) 509*7c478bd9Sstevel@tonic-gate DB_TXNMGR *tmgrp; 510*7c478bd9Sstevel@tonic-gate { 511*7c478bd9Sstevel@tonic-gate DB_TXN *txnp; 512*7c478bd9Sstevel@tonic-gate int ret, t_ret; 513*7c478bd9Sstevel@tonic-gate 514*7c478bd9Sstevel@tonic-gate TXN_PANIC_CHECK(tmgrp); 515*7c478bd9Sstevel@tonic-gate 516*7c478bd9Sstevel@tonic-gate ret = 0; 517*7c478bd9Sstevel@tonic-gate 518*7c478bd9Sstevel@tonic-gate /* 519*7c478bd9Sstevel@tonic-gate * This function had better only be called once per process 520*7c478bd9Sstevel@tonic-gate * (i.e., not per thread), so there should be no synchronization 521*7c478bd9Sstevel@tonic-gate * required. 522*7c478bd9Sstevel@tonic-gate */ 523*7c478bd9Sstevel@tonic-gate while ((txnp = 524*7c478bd9Sstevel@tonic-gate TAILQ_FIRST(&tmgrp->txn_chain)) != TAILQ_END(&tmgrp->txn_chain)) 525*7c478bd9Sstevel@tonic-gate if ((t_ret = txn_abort(txnp)) != 0) { 526*7c478bd9Sstevel@tonic-gate __txn_end(txnp, 0); 527*7c478bd9Sstevel@tonic-gate if (ret == 0) 528*7c478bd9Sstevel@tonic-gate ret = t_ret; 529*7c478bd9Sstevel@tonic-gate } 530*7c478bd9Sstevel@tonic-gate 531*7c478bd9Sstevel@tonic-gate if (tmgrp->dbenv->lg_info && 532*7c478bd9Sstevel@tonic-gate (t_ret = log_flush(tmgrp->dbenv->lg_info, NULL)) != 0 && ret == 0) 533*7c478bd9Sstevel@tonic-gate ret = t_ret; 534*7c478bd9Sstevel@tonic-gate 535*7c478bd9Sstevel@tonic-gate if (tmgrp->mutexp != NULL) { 536*7c478bd9Sstevel@tonic-gate LOCK_TXNREGION(tmgrp); 537*7c478bd9Sstevel@tonic-gate __db_shalloc_free(tmgrp->mem, tmgrp->mutexp); 538*7c478bd9Sstevel@tonic-gate UNLOCK_TXNREGION(tmgrp); 539*7c478bd9Sstevel@tonic-gate } 540*7c478bd9Sstevel@tonic-gate 541*7c478bd9Sstevel@tonic-gate if ((t_ret = __db_rdetach(&tmgrp->reginfo)) != 0 && ret == 0) 542*7c478bd9Sstevel@tonic-gate ret = t_ret; 543*7c478bd9Sstevel@tonic-gate 544*7c478bd9Sstevel@tonic-gate if (tmgrp->reginfo.path != NULL) 545*7c478bd9Sstevel@tonic-gate __os_freestr(tmgrp->reginfo.path); 546*7c478bd9Sstevel@tonic-gate __os_free(tmgrp, sizeof(*tmgrp)); 547*7c478bd9Sstevel@tonic-gate 548*7c478bd9Sstevel@tonic-gate return (ret); 549*7c478bd9Sstevel@tonic-gate } 550*7c478bd9Sstevel@tonic-gate 551*7c478bd9Sstevel@tonic-gate /* 552*7c478bd9Sstevel@tonic-gate * txn_unlink -- 553*7c478bd9Sstevel@tonic-gate * Remove the transaction region. 554*7c478bd9Sstevel@tonic-gate */ 555*7c478bd9Sstevel@tonic-gate int 556*7c478bd9Sstevel@tonic-gate txn_unlink(path, force, dbenv) 557*7c478bd9Sstevel@tonic-gate const char *path; 558*7c478bd9Sstevel@tonic-gate int force; 559*7c478bd9Sstevel@tonic-gate DB_ENV *dbenv; 560*7c478bd9Sstevel@tonic-gate { 561*7c478bd9Sstevel@tonic-gate REGINFO reginfo; 562*7c478bd9Sstevel@tonic-gate int ret; 563*7c478bd9Sstevel@tonic-gate 564*7c478bd9Sstevel@tonic-gate memset(®info, 0, sizeof(reginfo)); 565*7c478bd9Sstevel@tonic-gate reginfo.dbenv = dbenv; 566*7c478bd9Sstevel@tonic-gate reginfo.appname = DB_APP_NONE; 567*7c478bd9Sstevel@tonic-gate if (path != NULL && (ret = __os_strdup(path, ®info.path)) != 0) 568*7c478bd9Sstevel@tonic-gate return (ret); 569*7c478bd9Sstevel@tonic-gate reginfo.file = DEFAULT_TXN_FILE; 570*7c478bd9Sstevel@tonic-gate ret = __db_runlink(®info, force); 571*7c478bd9Sstevel@tonic-gate if (reginfo.path != NULL) 572*7c478bd9Sstevel@tonic-gate __os_freestr(reginfo.path); 573*7c478bd9Sstevel@tonic-gate return (ret); 574*7c478bd9Sstevel@tonic-gate } 575*7c478bd9Sstevel@tonic-gate 576*7c478bd9Sstevel@tonic-gate /* Internal routines. */ 577*7c478bd9Sstevel@tonic-gate 578*7c478bd9Sstevel@tonic-gate /* 579*7c478bd9Sstevel@tonic-gate * Return 0 if the txnp is reasonable, otherwise returns EINVAL. 580*7c478bd9Sstevel@tonic-gate */ 581*7c478bd9Sstevel@tonic-gate static int 582*7c478bd9Sstevel@tonic-gate __txn_check_running(txnp, tdp) 583*7c478bd9Sstevel@tonic-gate const DB_TXN *txnp; 584*7c478bd9Sstevel@tonic-gate TXN_DETAIL **tdp; 585*7c478bd9Sstevel@tonic-gate { 586*7c478bd9Sstevel@tonic-gate TXN_DETAIL *tp; 587*7c478bd9Sstevel@tonic-gate 588*7c478bd9Sstevel@tonic-gate tp = NULL; 589*7c478bd9Sstevel@tonic-gate if (txnp != NULL && txnp->mgrp != NULL && txnp->mgrp->region != NULL) { 590*7c478bd9Sstevel@tonic-gate tp = (TXN_DETAIL *)((u_int8_t *)txnp->mgrp->region + txnp->off); 591*7c478bd9Sstevel@tonic-gate /* 592*7c478bd9Sstevel@tonic-gate * Child transactions could be marked committed which is OK. 593*7c478bd9Sstevel@tonic-gate */ 594*7c478bd9Sstevel@tonic-gate if (tp->status != TXN_RUNNING && 595*7c478bd9Sstevel@tonic-gate tp->status != TXN_PREPARED && tp->status != TXN_COMMITTED) 596*7c478bd9Sstevel@tonic-gate tp = NULL; 597*7c478bd9Sstevel@tonic-gate if (tdp != NULL) 598*7c478bd9Sstevel@tonic-gate *tdp = tp; 599*7c478bd9Sstevel@tonic-gate } 600*7c478bd9Sstevel@tonic-gate 601*7c478bd9Sstevel@tonic-gate return (tp == NULL ? EINVAL : 0); 602*7c478bd9Sstevel@tonic-gate } 603*7c478bd9Sstevel@tonic-gate 604*7c478bd9Sstevel@tonic-gate static int 605*7c478bd9Sstevel@tonic-gate __txn_end(txnp, is_commit) 606*7c478bd9Sstevel@tonic-gate DB_TXN *txnp; 607*7c478bd9Sstevel@tonic-gate int is_commit; 608*7c478bd9Sstevel@tonic-gate { 609*7c478bd9Sstevel@tonic-gate DB_LOCKREQ request; 610*7c478bd9Sstevel@tonic-gate DB_TXNMGR *mgr; 611*7c478bd9Sstevel@tonic-gate TXN_DETAIL *tp; 612*7c478bd9Sstevel@tonic-gate u_int32_t locker; 613*7c478bd9Sstevel@tonic-gate int ret; 614*7c478bd9Sstevel@tonic-gate 615*7c478bd9Sstevel@tonic-gate mgr = txnp->mgrp; 616*7c478bd9Sstevel@tonic-gate 617*7c478bd9Sstevel@tonic-gate /* Release the locks. */ 618*7c478bd9Sstevel@tonic-gate locker = txnp->txnid; 619*7c478bd9Sstevel@tonic-gate request.op = txnp->parent == NULL || 620*7c478bd9Sstevel@tonic-gate is_commit == 0 ? DB_LOCK_PUT_ALL : DB_LOCK_INHERIT; 621*7c478bd9Sstevel@tonic-gate 622*7c478bd9Sstevel@tonic-gate if (mgr->dbenv->lk_info) { 623*7c478bd9Sstevel@tonic-gate ret = 624*7c478bd9Sstevel@tonic-gate lock_tvec(mgr->dbenv->lk_info, txnp, 0, &request, 1, NULL); 625*7c478bd9Sstevel@tonic-gate if (ret != 0 && (ret != DB_LOCK_DEADLOCK || is_commit)) { 626*7c478bd9Sstevel@tonic-gate __db_err(mgr->dbenv, "%s: release locks failed %s", 627*7c478bd9Sstevel@tonic-gate is_commit ? "txn_commit" : "txn_abort", 628*7c478bd9Sstevel@tonic-gate strerror(ret)); 629*7c478bd9Sstevel@tonic-gate return (ret); 630*7c478bd9Sstevel@tonic-gate } 631*7c478bd9Sstevel@tonic-gate } 632*7c478bd9Sstevel@tonic-gate 633*7c478bd9Sstevel@tonic-gate /* End the transaction. */ 634*7c478bd9Sstevel@tonic-gate LOCK_TXNREGION(mgr); 635*7c478bd9Sstevel@tonic-gate 636*7c478bd9Sstevel@tonic-gate /* 637*7c478bd9Sstevel@tonic-gate * Child transactions that are committing cannot be released until 638*7c478bd9Sstevel@tonic-gate * the parent commits, since the parent may abort, causing the child 639*7c478bd9Sstevel@tonic-gate * to abort as well. 640*7c478bd9Sstevel@tonic-gate */ 641*7c478bd9Sstevel@tonic-gate tp = (TXN_DETAIL *)((u_int8_t *)mgr->region + txnp->off); 642*7c478bd9Sstevel@tonic-gate if (txnp->parent == NULL || !is_commit) { 643*7c478bd9Sstevel@tonic-gate SH_TAILQ_REMOVE(&mgr->region->active_txn, 644*7c478bd9Sstevel@tonic-gate tp, links, __txn_detail); 645*7c478bd9Sstevel@tonic-gate 646*7c478bd9Sstevel@tonic-gate __db_shalloc_free(mgr->mem, tp); 647*7c478bd9Sstevel@tonic-gate } else 648*7c478bd9Sstevel@tonic-gate tp->status = is_commit ? TXN_COMMITTED : TXN_ABORTED; 649*7c478bd9Sstevel@tonic-gate 650*7c478bd9Sstevel@tonic-gate if (is_commit) 651*7c478bd9Sstevel@tonic-gate mgr->region->ncommits++; 652*7c478bd9Sstevel@tonic-gate else 653*7c478bd9Sstevel@tonic-gate mgr->region->naborts++; 654*7c478bd9Sstevel@tonic-gate 655*7c478bd9Sstevel@tonic-gate UNLOCK_TXNREGION(mgr); 656*7c478bd9Sstevel@tonic-gate 657*7c478bd9Sstevel@tonic-gate /* 658*7c478bd9Sstevel@tonic-gate * If the transaction aborted, we can remove it from its parent links. 659*7c478bd9Sstevel@tonic-gate * If it committed, then we need to leave it on, since the parent can 660*7c478bd9Sstevel@tonic-gate * still abort. 661*7c478bd9Sstevel@tonic-gate */ 662*7c478bd9Sstevel@tonic-gate if (txnp->parent != NULL && !is_commit) 663*7c478bd9Sstevel@tonic-gate TAILQ_REMOVE(&txnp->parent->kids, txnp, klinks); 664*7c478bd9Sstevel@tonic-gate 665*7c478bd9Sstevel@tonic-gate /* Free the space. */ 666*7c478bd9Sstevel@tonic-gate if (F_ISSET(txnp, TXN_MALLOC) && (txnp->parent == NULL || !is_commit)) { 667*7c478bd9Sstevel@tonic-gate LOCK_TXNTHREAD(mgr); 668*7c478bd9Sstevel@tonic-gate TAILQ_REMOVE(&mgr->txn_chain, txnp, links); 669*7c478bd9Sstevel@tonic-gate UNLOCK_TXNTHREAD(mgr); 670*7c478bd9Sstevel@tonic-gate 671*7c478bd9Sstevel@tonic-gate __os_free(txnp, sizeof(*txnp)); 672*7c478bd9Sstevel@tonic-gate } 673*7c478bd9Sstevel@tonic-gate 674*7c478bd9Sstevel@tonic-gate return (0); 675*7c478bd9Sstevel@tonic-gate } 676*7c478bd9Sstevel@tonic-gate 677*7c478bd9Sstevel@tonic-gate 678*7c478bd9Sstevel@tonic-gate /* 679*7c478bd9Sstevel@tonic-gate * __txn_undo -- 680*7c478bd9Sstevel@tonic-gate * Undo the transaction with id txnid. Returns 0 on success and 681*7c478bd9Sstevel@tonic-gate * errno on failure. 682*7c478bd9Sstevel@tonic-gate */ 683*7c478bd9Sstevel@tonic-gate static int 684*7c478bd9Sstevel@tonic-gate __txn_undo(txnp) 685*7c478bd9Sstevel@tonic-gate DB_TXN *txnp; 686*7c478bd9Sstevel@tonic-gate { 687*7c478bd9Sstevel@tonic-gate DBT rdbt; 688*7c478bd9Sstevel@tonic-gate DB_LOG *logp; 689*7c478bd9Sstevel@tonic-gate DB_LSN key_lsn; 690*7c478bd9Sstevel@tonic-gate DB_TXNMGR *mgr; 691*7c478bd9Sstevel@tonic-gate int ret; 692*7c478bd9Sstevel@tonic-gate 693*7c478bd9Sstevel@tonic-gate mgr = txnp->mgrp; 694*7c478bd9Sstevel@tonic-gate logp = mgr->dbenv->lg_info; 695*7c478bd9Sstevel@tonic-gate if (logp == NULL) 696*7c478bd9Sstevel@tonic-gate return (0); 697*7c478bd9Sstevel@tonic-gate 698*7c478bd9Sstevel@tonic-gate /* 699*7c478bd9Sstevel@tonic-gate * This is the simplest way to code this, but if the mallocs during 700*7c478bd9Sstevel@tonic-gate * recovery turn out to be a performance issue, we can do the 701*7c478bd9Sstevel@tonic-gate * allocation here and use DB_DBT_USERMEM. 702*7c478bd9Sstevel@tonic-gate */ 703*7c478bd9Sstevel@tonic-gate memset(&rdbt, 0, sizeof(rdbt)); 704*7c478bd9Sstevel@tonic-gate if (F_ISSET(logp, DB_AM_THREAD)) 705*7c478bd9Sstevel@tonic-gate F_SET(&rdbt, DB_DBT_MALLOC); 706*7c478bd9Sstevel@tonic-gate 707*7c478bd9Sstevel@tonic-gate key_lsn = txnp->last_lsn; /* structure assignment */ 708*7c478bd9Sstevel@tonic-gate for (ret = 0; ret == 0 && !IS_ZERO_LSN(key_lsn);) { 709*7c478bd9Sstevel@tonic-gate /* 710*7c478bd9Sstevel@tonic-gate * The dispatch routine returns the lsn of the record 711*7c478bd9Sstevel@tonic-gate * before the current one in the key_lsn argument. 712*7c478bd9Sstevel@tonic-gate */ 713*7c478bd9Sstevel@tonic-gate if ((ret = log_get(logp, &key_lsn, &rdbt, DB_SET)) == 0) { 714*7c478bd9Sstevel@tonic-gate ret = 715*7c478bd9Sstevel@tonic-gate mgr->recover(logp, &rdbt, &key_lsn, TXN_UNDO, NULL); 716*7c478bd9Sstevel@tonic-gate if (F_ISSET(logp, DB_AM_THREAD) && rdbt.data != NULL) { 717*7c478bd9Sstevel@tonic-gate __os_free(rdbt.data, rdbt.size); 718*7c478bd9Sstevel@tonic-gate rdbt.data = NULL; 719*7c478bd9Sstevel@tonic-gate } 720*7c478bd9Sstevel@tonic-gate } 721*7c478bd9Sstevel@tonic-gate if (ret != 0) 722*7c478bd9Sstevel@tonic-gate return (ret); 723*7c478bd9Sstevel@tonic-gate } 724*7c478bd9Sstevel@tonic-gate 725*7c478bd9Sstevel@tonic-gate return (ret); 726*7c478bd9Sstevel@tonic-gate } 727*7c478bd9Sstevel@tonic-gate 728*7c478bd9Sstevel@tonic-gate /* 729*7c478bd9Sstevel@tonic-gate * Transaction checkpoint. 730*7c478bd9Sstevel@tonic-gate * If either kbytes or minutes is non-zero, then we only take the checkpoint 731*7c478bd9Sstevel@tonic-gate * more than "minutes" minutes have passed since the last checkpoint or if 732*7c478bd9Sstevel@tonic-gate * more than "kbytes" of log data have been written since the last checkpoint. 733*7c478bd9Sstevel@tonic-gate * When taking a checkpoint, find the oldest active transaction and figure out 734*7c478bd9Sstevel@tonic-gate * its first LSN. This is the lowest LSN we can checkpoint, since any record 735*7c478bd9Sstevel@tonic-gate * written after since that point may be involved in a transaction and may 736*7c478bd9Sstevel@tonic-gate * therefore need to be undone in the case of an abort. 737*7c478bd9Sstevel@tonic-gate */ 738*7c478bd9Sstevel@tonic-gate int 739*7c478bd9Sstevel@tonic-gate txn_checkpoint(mgr, kbytes, minutes) 740*7c478bd9Sstevel@tonic-gate const DB_TXNMGR *mgr; 741*7c478bd9Sstevel@tonic-gate u_int32_t kbytes, minutes; 742*7c478bd9Sstevel@tonic-gate { 743*7c478bd9Sstevel@tonic-gate DB_LOG *dblp; 744*7c478bd9Sstevel@tonic-gate DB_LSN ckp_lsn, sync_lsn, last_ckp; 745*7c478bd9Sstevel@tonic-gate TXN_DETAIL *txnp; 746*7c478bd9Sstevel@tonic-gate time_t last_ckp_time, now; 747*7c478bd9Sstevel@tonic-gate u_int32_t kbytes_written; 748*7c478bd9Sstevel@tonic-gate int ret; 749*7c478bd9Sstevel@tonic-gate 750*7c478bd9Sstevel@tonic-gate TXN_PANIC_CHECK(mgr); 751*7c478bd9Sstevel@tonic-gate 752*7c478bd9Sstevel@tonic-gate /* 753*7c478bd9Sstevel@tonic-gate * Check if we need to run recovery. 754*7c478bd9Sstevel@tonic-gate */ 755*7c478bd9Sstevel@tonic-gate ZERO_LSN(ckp_lsn); 756*7c478bd9Sstevel@tonic-gate if (minutes != 0) { 757*7c478bd9Sstevel@tonic-gate (void)time(&now); 758*7c478bd9Sstevel@tonic-gate 759*7c478bd9Sstevel@tonic-gate LOCK_TXNREGION(mgr); 760*7c478bd9Sstevel@tonic-gate last_ckp_time = mgr->region->time_ckp; 761*7c478bd9Sstevel@tonic-gate UNLOCK_TXNREGION(mgr); 762*7c478bd9Sstevel@tonic-gate 763*7c478bd9Sstevel@tonic-gate if (now - last_ckp_time >= (time_t)(minutes * 60)) 764*7c478bd9Sstevel@tonic-gate goto do_ckp; 765*7c478bd9Sstevel@tonic-gate } 766*7c478bd9Sstevel@tonic-gate 767*7c478bd9Sstevel@tonic-gate if (kbytes != 0) { 768*7c478bd9Sstevel@tonic-gate dblp = mgr->dbenv->lg_info; 769*7c478bd9Sstevel@tonic-gate LOCK_LOGREGION(dblp); 770*7c478bd9Sstevel@tonic-gate kbytes_written = 771*7c478bd9Sstevel@tonic-gate dblp->lp->stat.st_wc_mbytes * 1024 + 772*7c478bd9Sstevel@tonic-gate dblp->lp->stat.st_wc_bytes / 1024; 773*7c478bd9Sstevel@tonic-gate ckp_lsn = dblp->lp->lsn; 774*7c478bd9Sstevel@tonic-gate UNLOCK_LOGREGION(dblp); 775*7c478bd9Sstevel@tonic-gate if (kbytes_written >= (u_int32_t)kbytes) 776*7c478bd9Sstevel@tonic-gate goto do_ckp; 777*7c478bd9Sstevel@tonic-gate } 778*7c478bd9Sstevel@tonic-gate 779*7c478bd9Sstevel@tonic-gate /* 780*7c478bd9Sstevel@tonic-gate * If we checked time and data and didn't go to checkpoint, 781*7c478bd9Sstevel@tonic-gate * we're done. 782*7c478bd9Sstevel@tonic-gate */ 783*7c478bd9Sstevel@tonic-gate if (minutes != 0 || kbytes != 0) 784*7c478bd9Sstevel@tonic-gate return (0); 785*7c478bd9Sstevel@tonic-gate 786*7c478bd9Sstevel@tonic-gate do_ckp: 787*7c478bd9Sstevel@tonic-gate if (IS_ZERO_LSN(ckp_lsn)) { 788*7c478bd9Sstevel@tonic-gate dblp = mgr->dbenv->lg_info; 789*7c478bd9Sstevel@tonic-gate LOCK_LOGREGION(dblp); 790*7c478bd9Sstevel@tonic-gate ckp_lsn = dblp->lp->lsn; 791*7c478bd9Sstevel@tonic-gate UNLOCK_LOGREGION(dblp); 792*7c478bd9Sstevel@tonic-gate } 793*7c478bd9Sstevel@tonic-gate 794*7c478bd9Sstevel@tonic-gate /* 795*7c478bd9Sstevel@tonic-gate * We have to find an LSN such that all transactions begun 796*7c478bd9Sstevel@tonic-gate * before that LSN are complete. 797*7c478bd9Sstevel@tonic-gate */ 798*7c478bd9Sstevel@tonic-gate LOCK_TXNREGION(mgr); 799*7c478bd9Sstevel@tonic-gate 800*7c478bd9Sstevel@tonic-gate if (!IS_ZERO_LSN(mgr->region->pending_ckp)) 801*7c478bd9Sstevel@tonic-gate ckp_lsn = mgr->region->pending_ckp; 802*7c478bd9Sstevel@tonic-gate else 803*7c478bd9Sstevel@tonic-gate for (txnp = 804*7c478bd9Sstevel@tonic-gate SH_TAILQ_FIRST(&mgr->region->active_txn, __txn_detail); 805*7c478bd9Sstevel@tonic-gate txnp != NULL; 806*7c478bd9Sstevel@tonic-gate txnp = SH_TAILQ_NEXT(txnp, links, __txn_detail)) { 807*7c478bd9Sstevel@tonic-gate 808*7c478bd9Sstevel@tonic-gate /* 809*7c478bd9Sstevel@tonic-gate * Look through the active transactions for the 810*7c478bd9Sstevel@tonic-gate * lowest begin lsn. 811*7c478bd9Sstevel@tonic-gate */ 812*7c478bd9Sstevel@tonic-gate if (!IS_ZERO_LSN(txnp->begin_lsn) && 813*7c478bd9Sstevel@tonic-gate log_compare(&txnp->begin_lsn, &ckp_lsn) < 0) 814*7c478bd9Sstevel@tonic-gate ckp_lsn = txnp->begin_lsn; 815*7c478bd9Sstevel@tonic-gate } 816*7c478bd9Sstevel@tonic-gate 817*7c478bd9Sstevel@tonic-gate mgr->region->pending_ckp = ckp_lsn; 818*7c478bd9Sstevel@tonic-gate UNLOCK_TXNREGION(mgr); 819*7c478bd9Sstevel@tonic-gate 820*7c478bd9Sstevel@tonic-gate /* 821*7c478bd9Sstevel@tonic-gate * memp_sync may change the lsn you pass it, so don't pass it 822*7c478bd9Sstevel@tonic-gate * the actual ckp_lsn, pass it a temp instead. 823*7c478bd9Sstevel@tonic-gate */ 824*7c478bd9Sstevel@tonic-gate sync_lsn = ckp_lsn; 825*7c478bd9Sstevel@tonic-gate if (mgr->dbenv->mp_info != NULL && 826*7c478bd9Sstevel@tonic-gate (ret = memp_sync(mgr->dbenv->mp_info, &sync_lsn)) != 0) { 827*7c478bd9Sstevel@tonic-gate /* 828*7c478bd9Sstevel@tonic-gate * ret == DB_INCOMPLETE means that there are still buffers to 829*7c478bd9Sstevel@tonic-gate * flush, the checkpoint is not complete. Wait and try again. 830*7c478bd9Sstevel@tonic-gate */ 831*7c478bd9Sstevel@tonic-gate if (ret > 0) 832*7c478bd9Sstevel@tonic-gate __db_err(mgr->dbenv, 833*7c478bd9Sstevel@tonic-gate "txn_checkpoint: system failure in memp_sync %s\n", 834*7c478bd9Sstevel@tonic-gate strerror(ret)); 835*7c478bd9Sstevel@tonic-gate return (ret); 836*7c478bd9Sstevel@tonic-gate } 837*7c478bd9Sstevel@tonic-gate if (mgr->dbenv->lg_info != NULL) { 838*7c478bd9Sstevel@tonic-gate LOCK_TXNREGION(mgr); 839*7c478bd9Sstevel@tonic-gate last_ckp = mgr->region->last_ckp; 840*7c478bd9Sstevel@tonic-gate ZERO_LSN(mgr->region->pending_ckp); 841*7c478bd9Sstevel@tonic-gate UNLOCK_TXNREGION(mgr); 842*7c478bd9Sstevel@tonic-gate 843*7c478bd9Sstevel@tonic-gate if ((ret = __txn_ckp_log(mgr->dbenv->lg_info, 844*7c478bd9Sstevel@tonic-gate NULL, &ckp_lsn, DB_CHECKPOINT, &ckp_lsn, &last_ckp)) != 0) { 845*7c478bd9Sstevel@tonic-gate __db_err(mgr->dbenv, 846*7c478bd9Sstevel@tonic-gate "txn_checkpoint: log failed at LSN [%ld %ld] %s\n", 847*7c478bd9Sstevel@tonic-gate (long)ckp_lsn.file, (long)ckp_lsn.offset, 848*7c478bd9Sstevel@tonic-gate strerror(ret)); 849*7c478bd9Sstevel@tonic-gate return (ret); 850*7c478bd9Sstevel@tonic-gate } 851*7c478bd9Sstevel@tonic-gate 852*7c478bd9Sstevel@tonic-gate LOCK_TXNREGION(mgr); 853*7c478bd9Sstevel@tonic-gate mgr->region->last_ckp = ckp_lsn; 854*7c478bd9Sstevel@tonic-gate (void)time(&mgr->region->time_ckp); 855*7c478bd9Sstevel@tonic-gate UNLOCK_TXNREGION(mgr); 856*7c478bd9Sstevel@tonic-gate } 857*7c478bd9Sstevel@tonic-gate return (0); 858*7c478bd9Sstevel@tonic-gate } 859*7c478bd9Sstevel@tonic-gate 860*7c478bd9Sstevel@tonic-gate /* 861*7c478bd9Sstevel@tonic-gate * __txn_validate_region -- 862*7c478bd9Sstevel@tonic-gate * Called at every interface to verify if the region has changed size, 863*7c478bd9Sstevel@tonic-gate * and if so, to remap the region in and reset the process' pointers. 864*7c478bd9Sstevel@tonic-gate */ 865*7c478bd9Sstevel@tonic-gate static int 866*7c478bd9Sstevel@tonic-gate __txn_validate_region(tp) 867*7c478bd9Sstevel@tonic-gate DB_TXNMGR *tp; 868*7c478bd9Sstevel@tonic-gate { 869*7c478bd9Sstevel@tonic-gate int ret; 870*7c478bd9Sstevel@tonic-gate 871*7c478bd9Sstevel@tonic-gate if (tp->reginfo.size == tp->region->hdr.size) 872*7c478bd9Sstevel@tonic-gate return (0); 873*7c478bd9Sstevel@tonic-gate 874*7c478bd9Sstevel@tonic-gate /* Detach/reattach the region. */ 875*7c478bd9Sstevel@tonic-gate if ((ret = __db_rreattach(&tp->reginfo, tp->region->hdr.size)) != 0) 876*7c478bd9Sstevel@tonic-gate return (ret); 877*7c478bd9Sstevel@tonic-gate 878*7c478bd9Sstevel@tonic-gate /* Reset region information. */ 879*7c478bd9Sstevel@tonic-gate tp->region = tp->reginfo.addr; 880*7c478bd9Sstevel@tonic-gate tp->mem = &tp->region[1]; 881*7c478bd9Sstevel@tonic-gate 882*7c478bd9Sstevel@tonic-gate return (0); 883*7c478bd9Sstevel@tonic-gate } 884*7c478bd9Sstevel@tonic-gate 885*7c478bd9Sstevel@tonic-gate static int 886*7c478bd9Sstevel@tonic-gate __txn_grow_region(tp) 887*7c478bd9Sstevel@tonic-gate DB_TXNMGR *tp; 888*7c478bd9Sstevel@tonic-gate { 889*7c478bd9Sstevel@tonic-gate size_t incr, oldsize; 890*7c478bd9Sstevel@tonic-gate u_int32_t mutex_offset, oldmax; 891*7c478bd9Sstevel@tonic-gate u_int8_t *curaddr; 892*7c478bd9Sstevel@tonic-gate int ret; 893*7c478bd9Sstevel@tonic-gate 894*7c478bd9Sstevel@tonic-gate oldmax = tp->region->maxtxns; 895*7c478bd9Sstevel@tonic-gate incr = oldmax * sizeof(DB_TXN); 896*7c478bd9Sstevel@tonic-gate mutex_offset = tp->mutexp != NULL ? 897*7c478bd9Sstevel@tonic-gate (u_int8_t *)tp->mutexp - (u_int8_t *)tp->region : 0; 898*7c478bd9Sstevel@tonic-gate 899*7c478bd9Sstevel@tonic-gate oldsize = tp->reginfo.size; 900*7c478bd9Sstevel@tonic-gate if ((ret = __db_rgrow(&tp->reginfo, oldsize + incr)) != 0) 901*7c478bd9Sstevel@tonic-gate return (ret); 902*7c478bd9Sstevel@tonic-gate tp->region = tp->reginfo.addr; 903*7c478bd9Sstevel@tonic-gate 904*7c478bd9Sstevel@tonic-gate /* Throw the new space on the free list. */ 905*7c478bd9Sstevel@tonic-gate curaddr = (u_int8_t *)tp->region + oldsize; 906*7c478bd9Sstevel@tonic-gate tp->mem = &tp->region[1]; 907*7c478bd9Sstevel@tonic-gate tp->mutexp = mutex_offset != 0 ? 908*7c478bd9Sstevel@tonic-gate (db_mutex_t *)((u_int8_t *)tp->region + mutex_offset) : NULL; 909*7c478bd9Sstevel@tonic-gate 910*7c478bd9Sstevel@tonic-gate *((size_t *)curaddr) = incr - sizeof(size_t); 911*7c478bd9Sstevel@tonic-gate curaddr += sizeof(size_t); 912*7c478bd9Sstevel@tonic-gate __db_shalloc_free(tp->mem, curaddr); 913*7c478bd9Sstevel@tonic-gate 914*7c478bd9Sstevel@tonic-gate tp->region->maxtxns = 2 * oldmax; 915*7c478bd9Sstevel@tonic-gate 916*7c478bd9Sstevel@tonic-gate return (0); 917*7c478bd9Sstevel@tonic-gate } 918*7c478bd9Sstevel@tonic-gate 919*7c478bd9Sstevel@tonic-gate int 920*7c478bd9Sstevel@tonic-gate txn_stat(mgr, statp, db_malloc) 921*7c478bd9Sstevel@tonic-gate DB_TXNMGR *mgr; 922*7c478bd9Sstevel@tonic-gate DB_TXN_STAT **statp; 923*7c478bd9Sstevel@tonic-gate void *(*db_malloc) __P((size_t)); 924*7c478bd9Sstevel@tonic-gate { 925*7c478bd9Sstevel@tonic-gate DB_TXN_STAT *stats; 926*7c478bd9Sstevel@tonic-gate TXN_DETAIL *txnp; 927*7c478bd9Sstevel@tonic-gate size_t nbytes; 928*7c478bd9Sstevel@tonic-gate u_int32_t nactive, ndx; 929*7c478bd9Sstevel@tonic-gate int ret; 930*7c478bd9Sstevel@tonic-gate 931*7c478bd9Sstevel@tonic-gate TXN_PANIC_CHECK(mgr); 932*7c478bd9Sstevel@tonic-gate 933*7c478bd9Sstevel@tonic-gate LOCK_TXNREGION(mgr); 934*7c478bd9Sstevel@tonic-gate nactive = mgr->region->nbegins - 935*7c478bd9Sstevel@tonic-gate mgr->region->naborts - mgr->region->ncommits; 936*7c478bd9Sstevel@tonic-gate UNLOCK_TXNREGION(mgr); 937*7c478bd9Sstevel@tonic-gate 938*7c478bd9Sstevel@tonic-gate /* 939*7c478bd9Sstevel@tonic-gate * Allocate a bunch of extra active structures to handle any 940*7c478bd9Sstevel@tonic-gate * that have been created since we unlocked the region. 941*7c478bd9Sstevel@tonic-gate */ 942*7c478bd9Sstevel@tonic-gate nbytes = sizeof(DB_TXN_STAT) + sizeof(DB_TXN_ACTIVE) * (nactive + 200); 943*7c478bd9Sstevel@tonic-gate if ((ret = __os_malloc(nbytes, db_malloc, &stats)) != 0) 944*7c478bd9Sstevel@tonic-gate return (ret); 945*7c478bd9Sstevel@tonic-gate 946*7c478bd9Sstevel@tonic-gate LOCK_TXNREGION(mgr); 947*7c478bd9Sstevel@tonic-gate stats->st_last_txnid = mgr->region->last_txnid; 948*7c478bd9Sstevel@tonic-gate stats->st_last_ckp = mgr->region->last_ckp; 949*7c478bd9Sstevel@tonic-gate stats->st_maxtxns = mgr->region->maxtxns; 950*7c478bd9Sstevel@tonic-gate stats->st_naborts = mgr->region->naborts; 951*7c478bd9Sstevel@tonic-gate stats->st_nbegins = mgr->region->nbegins; 952*7c478bd9Sstevel@tonic-gate stats->st_ncommits = mgr->region->ncommits; 953*7c478bd9Sstevel@tonic-gate stats->st_pending_ckp = mgr->region->pending_ckp; 954*7c478bd9Sstevel@tonic-gate stats->st_time_ckp = mgr->region->time_ckp; 955*7c478bd9Sstevel@tonic-gate stats->st_nactive = stats->st_nbegins - 956*7c478bd9Sstevel@tonic-gate stats->st_naborts - stats->st_ncommits; 957*7c478bd9Sstevel@tonic-gate if (stats->st_nactive > nactive + 200) 958*7c478bd9Sstevel@tonic-gate stats->st_nactive = nactive + 200; 959*7c478bd9Sstevel@tonic-gate stats->st_txnarray = (DB_TXN_ACTIVE *)&stats[1]; 960*7c478bd9Sstevel@tonic-gate 961*7c478bd9Sstevel@tonic-gate ndx = 0; 962*7c478bd9Sstevel@tonic-gate for (txnp = SH_TAILQ_FIRST(&mgr->region->active_txn, __txn_detail); 963*7c478bd9Sstevel@tonic-gate txnp != NULL; 964*7c478bd9Sstevel@tonic-gate txnp = SH_TAILQ_NEXT(txnp, links, __txn_detail)) { 965*7c478bd9Sstevel@tonic-gate stats->st_txnarray[ndx].txnid = txnp->txnid; 966*7c478bd9Sstevel@tonic-gate stats->st_txnarray[ndx].lsn = txnp->begin_lsn; 967*7c478bd9Sstevel@tonic-gate ndx++; 968*7c478bd9Sstevel@tonic-gate 969*7c478bd9Sstevel@tonic-gate if (ndx >= stats->st_nactive) 970*7c478bd9Sstevel@tonic-gate break; 971*7c478bd9Sstevel@tonic-gate } 972*7c478bd9Sstevel@tonic-gate 973*7c478bd9Sstevel@tonic-gate stats->st_region_wait = mgr->region->hdr.lock.mutex_set_wait; 974*7c478bd9Sstevel@tonic-gate stats->st_region_nowait = mgr->region->hdr.lock.mutex_set_nowait; 975*7c478bd9Sstevel@tonic-gate stats->st_refcnt = mgr->region->hdr.refcnt; 976*7c478bd9Sstevel@tonic-gate stats->st_regsize = mgr->region->hdr.size; 977*7c478bd9Sstevel@tonic-gate 978*7c478bd9Sstevel@tonic-gate UNLOCK_TXNREGION(mgr); 979*7c478bd9Sstevel@tonic-gate *statp = stats; 980*7c478bd9Sstevel@tonic-gate return (0); 981*7c478bd9Sstevel@tonic-gate } 982*7c478bd9Sstevel@tonic-gate 983*7c478bd9Sstevel@tonic-gate static void 984*7c478bd9Sstevel@tonic-gate __txn_freekids(txnp) 985*7c478bd9Sstevel@tonic-gate DB_TXN *txnp; 986*7c478bd9Sstevel@tonic-gate { 987*7c478bd9Sstevel@tonic-gate DB_TXNMGR *mgr; 988*7c478bd9Sstevel@tonic-gate TXN_DETAIL *tp; 989*7c478bd9Sstevel@tonic-gate DB_TXN *kids; 990*7c478bd9Sstevel@tonic-gate 991*7c478bd9Sstevel@tonic-gate mgr = txnp->mgrp; 992*7c478bd9Sstevel@tonic-gate 993*7c478bd9Sstevel@tonic-gate for (kids = TAILQ_FIRST(&txnp->kids); 994*7c478bd9Sstevel@tonic-gate kids != NULL; 995*7c478bd9Sstevel@tonic-gate kids = TAILQ_FIRST(&txnp->kids)) { 996*7c478bd9Sstevel@tonic-gate /* Free any children of this transaction. */ 997*7c478bd9Sstevel@tonic-gate __txn_freekids(kids); 998*7c478bd9Sstevel@tonic-gate 999*7c478bd9Sstevel@tonic-gate /* Free the transaction detail in the region. */ 1000*7c478bd9Sstevel@tonic-gate LOCK_TXNREGION(mgr); 1001*7c478bd9Sstevel@tonic-gate tp = (TXN_DETAIL *)((u_int8_t *)mgr->region + kids->off); 1002*7c478bd9Sstevel@tonic-gate SH_TAILQ_REMOVE(&mgr->region->active_txn, 1003*7c478bd9Sstevel@tonic-gate tp, links, __txn_detail); 1004*7c478bd9Sstevel@tonic-gate 1005*7c478bd9Sstevel@tonic-gate __db_shalloc_free(mgr->mem, tp); 1006*7c478bd9Sstevel@tonic-gate UNLOCK_TXNREGION(mgr); 1007*7c478bd9Sstevel@tonic-gate 1008*7c478bd9Sstevel@tonic-gate /* Now remove from its parent. */ 1009*7c478bd9Sstevel@tonic-gate TAILQ_REMOVE(&txnp->kids, kids, klinks); 1010*7c478bd9Sstevel@tonic-gate if (F_ISSET(txnp, TXN_MALLOC)) { 1011*7c478bd9Sstevel@tonic-gate LOCK_TXNTHREAD(mgr); 1012*7c478bd9Sstevel@tonic-gate TAILQ_REMOVE(&mgr->txn_chain, kids, links); 1013*7c478bd9Sstevel@tonic-gate UNLOCK_TXNTHREAD(mgr); 1014*7c478bd9Sstevel@tonic-gate __os_free(kids, sizeof(*kids)); 1015*7c478bd9Sstevel@tonic-gate } 1016*7c478bd9Sstevel@tonic-gate } 1017*7c478bd9Sstevel@tonic-gate } 1018*7c478bd9Sstevel@tonic-gate 1019*7c478bd9Sstevel@tonic-gate /* 1020*7c478bd9Sstevel@tonic-gate * __txn_is_ancestor -- 1021*7c478bd9Sstevel@tonic-gate * Determine if a transaction is an ancestor of another transaction. 1022*7c478bd9Sstevel@tonic-gate * This is used during lock promotion when we do not have the per-process 1023*7c478bd9Sstevel@tonic-gate * data structures that link parents together. Instead, we'll have to 1024*7c478bd9Sstevel@tonic-gate * follow the links in the transaction region. 1025*7c478bd9Sstevel@tonic-gate * 1026*7c478bd9Sstevel@tonic-gate * PUBLIC: int __txn_is_ancestor __P((DB_TXNMGR *, size_t, size_t)); 1027*7c478bd9Sstevel@tonic-gate */ 1028*7c478bd9Sstevel@tonic-gate int 1029*7c478bd9Sstevel@tonic-gate __txn_is_ancestor(mgr, hold_off, req_off) 1030*7c478bd9Sstevel@tonic-gate DB_TXNMGR *mgr; 1031*7c478bd9Sstevel@tonic-gate size_t hold_off, req_off; 1032*7c478bd9Sstevel@tonic-gate { 1033*7c478bd9Sstevel@tonic-gate TXN_DETAIL *hold_tp, *req_tp; 1034*7c478bd9Sstevel@tonic-gate 1035*7c478bd9Sstevel@tonic-gate hold_tp = (TXN_DETAIL *)((u_int8_t *)mgr->region + hold_off); 1036*7c478bd9Sstevel@tonic-gate req_tp = (TXN_DETAIL *)((u_int8_t *)mgr->region + req_off); 1037*7c478bd9Sstevel@tonic-gate 1038*7c478bd9Sstevel@tonic-gate while (req_tp->parent != 0) { 1039*7c478bd9Sstevel@tonic-gate req_tp = 1040*7c478bd9Sstevel@tonic-gate (TXN_DETAIL *)((u_int8_t *)mgr->region + req_tp->parent); 1041*7c478bd9Sstevel@tonic-gate if (req_tp->txnid == hold_tp->txnid) 1042*7c478bd9Sstevel@tonic-gate return (1); 1043*7c478bd9Sstevel@tonic-gate } 1044*7c478bd9Sstevel@tonic-gate 1045*7c478bd9Sstevel@tonic-gate return (0); 1046*7c478bd9Sstevel@tonic-gate } 1047