1 /*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 1998 5 * Sleepycat Software. All rights reserved. 6 */ 7 8 /* XXX Remove the global transaction and hang it off the environment. */ 9 #include "config.h" 10 11 #ifndef lint 12 static const char sccsid[] = "@(#)xa.c 10.4 (Sleepycat) 10/11/98"; 13 #endif /* not lint */ 14 15 #ifndef NO_SYSTEM_INCLUDES 16 #include <sys/types.h> 17 18 #include <stdlib.h> 19 #include <stdio.h> 20 #include <string.h> 21 #endif 22 23 #include "db_int.h" 24 #include "db_page.h" 25 #include "shqueue.h" 26 #include "log.h" 27 #include "txn.h" 28 #include "db_auto.h" 29 #include "db_ext.h" 30 #include "db_dispatch.h" 31 32 static int __db_xa_close __P((char *, int, long)); 33 static int __db_xa_commit __P((XID *, int, long)); 34 static int __db_xa_complete __P((int *, int *, int, long)); 35 static int __db_xa_end __P((XID *, int, long)); 36 static int __db_xa_forget __P((XID *, int, long)); 37 static int __db_xa_open __P((char *, int, long)); 38 static int __db_xa_prepare __P((XID *, int, long)); 39 static int __db_xa_recover __P((XID *, long, int, long)); 40 static int __db_xa_rollback __P((XID *, int, long)); 41 static int __db_xa_start __P((XID *, int, long)); 42 static void __xa_txn_end __P((DB_ENV *)); 43 static void __xa_txn_init __P((DB_ENV *, TXN_DETAIL *, size_t)); 44 45 /* 46 * Possible flag values: 47 * Dynamic registration 0 => no dynamic registration 48 * TMREGISTER => dynamic registration 49 * Asynchronous operation 0 => no support for asynchrony 50 * TMUSEASYNC => async support 51 * Migration support 0 => migration of transactions across 52 * threads is possible 53 * TMNOMIGRATE => no migration across threads 54 */ 55 const struct xa_switch_t db_xa_switch = { 56 "Berkeley DB", /* name[RMNAMESZ] */ 57 TMNOMIGRATE, /* flags */ 58 0, /* version */ 59 __db_xa_open, /* xa_open_entry */ 60 __db_xa_close, /* xa_close_entry */ 61 __db_xa_start, /* xa_start_entry */ 62 __db_xa_end, /* xa_end_entry */ 63 __db_xa_rollback, /* xa_rollback_entry */ 64 __db_xa_prepare, /* xa_prepare_entry */ 65 __db_xa_commit, /* xa_commit_entry */ 66 __db_xa_recover, /* xa_recover_entry */ 67 __db_xa_forget, /* xa_forget_entry */ 68 __db_xa_complete /* xa_complete_entry */ 69 }; 70 71 /* 72 * __db_xa_open -- 73 * The open call in the XA protocol. The rmid field is an id number 74 * that the TM assigned us and will pass us on every xa call. We need to 75 * map that rmid number into a dbenv structure that we create during 76 * initialization. Since this id number is thread specific, we do not 77 * need to store it in shared memory. The file xa_map.c implements all 78 * such xa->db mappings. 79 * The xa_info field is instance specific information. We require 80 * that the value of DB_HOME be passed in xa_info. Since xa_info is the 81 * only thing that we get to pass to db_appinit, any config information 82 * will have to be done via a config file instead of via the db_appinit 83 * call. 84 */ 85 static int 86 __db_xa_open(xa_info, rmid, flags) 87 char *xa_info; 88 int rmid; 89 long flags; 90 { 91 DB_ENV *env; 92 93 if (LF_ISSET(TMASYNC)) 94 return (XAER_ASYNC); 95 if (flags != TMNOFLAGS) 96 return (XAER_INVAL); 97 98 /* Verify if we already have this environment open. */ 99 if (__db_rmid_to_env(rmid, &env, 0) == 0) 100 return (XA_OK); 101 102 /* 103 * Since we cannot tell whether the environment is OK or not, 104 * we can't actually do the db_appinit in xa_open. Instead, 105 * we save the mapping between the rmid and the xa_info. If 106 * we next get a call to __xa_recover, we do the db_appinit 107 * with DB_RECOVER set. If we get any other call, then we 108 * do the db_appinit. 109 */ 110 return (__db_map_rmid_name(rmid, xa_info)); 111 } 112 113 /* 114 * __db_xa_close -- 115 * The close call of the XA protocol. The only trickiness here 116 * is that if there are any active transactions, we must fail. It is 117 * *not* an error to call close on an environment that has already been 118 * closed (I am interpreting that to mean it's OK to call close on an 119 * environment that has never been opened). 120 */ 121 static int 122 __db_xa_close(xa_info, rmid, flags) 123 char *xa_info; 124 int rmid; 125 long flags; 126 { 127 DB_ENV *env; 128 int ret, t_ret; 129 130 COMPQUIET(xa_info, NULL); 131 132 if (LF_ISSET(TMASYNC)) 133 return (XAER_ASYNC); 134 if (flags != TMNOFLAGS) 135 return (XAER_INVAL); 136 137 /* If the environment is closed, then we're done. */ 138 if (__db_rmid_to_env(rmid, &env, 0) != 0) 139 return (XA_OK); 140 141 /* Check if there are any pending transactions. */ 142 if (env->xa_txn != NULL && env->xa_txn->txnid != TXN_INVALID) 143 return (XAER_PROTO); 144 145 /* Now, destroy the mapping and close the environment. */ 146 ret = __db_unmap_rmid(rmid); 147 if ((t_ret = db_appexit(env)) != 0 && ret == 0) 148 ret = t_ret; 149 150 __os_free(env, sizeof(DB_ENV)); 151 152 return (ret == 0 ? XA_OK : XAER_RMERR); 153 } 154 155 /* 156 * __db_xa_start -- 157 * Begin a transaction for the current resource manager. 158 */ 159 static int 160 __db_xa_start(xid, rmid, flags) 161 XID *xid; 162 int rmid; 163 long flags; 164 { 165 DB_ENV *env; 166 TXN_DETAIL *td; 167 size_t off; 168 int is_known; 169 170 #define OK_FLAGS (TMJOIN | TMRESUME | TMNOWAIT | TMASYNC | TMNOFLAGS) 171 if (LF_ISSET(~OK_FLAGS)) 172 return (XAER_INVAL); 173 174 if (LF_ISSET(TMJOIN) && LF_ISSET(TMRESUME)) 175 return (XAER_INVAL); 176 177 if (LF_ISSET(TMASYNC)) 178 return (XAER_ASYNC); 179 180 if (__db_rmid_to_env(rmid, &env, 1) != 0) 181 return (XAER_PROTO); 182 183 is_known = __db_xid_to_txn(env, xid, &off) == 0; 184 185 if (is_known && !LF_ISSET(TMRESUME) && !LF_ISSET(TMJOIN)) 186 return (XAER_DUPID); 187 188 if (!is_known && LF_ISSET(TMRESUME | TMJOIN)) 189 return (XAER_NOTA); 190 191 /* 192 * This can't block, so we can ignore TMNOWAIT. 193 * 194 * Other error conditions: RMERR, RMFAIL, OUTSIDE, PROTO, RB* 195 */ 196 if (is_known) { 197 td = (TXN_DETAIL *)((u_int8_t *)env->tx_info->region + off); 198 if (td->xa_status == TXN_XA_SUSPENDED && !LF_ISSET(TMRESUME)) 199 return (XAER_PROTO); 200 if (td->xa_status == TXN_XA_DEADLOCKED) 201 return (XA_RBDEADLOCK); 202 if (td->xa_status == TXN_XA_ABORTED) 203 return (XA_RBOTHER); 204 205 /* Now, fill in the global transaction structure. */ 206 __xa_txn_init(env, td, off); 207 td->xa_status = TXN_XA_STARTED; 208 } else { 209 if (__txn_xa_begin(env, env->xa_txn) != 0) 210 return (XAER_RMERR); 211 (void)__db_map_xid(env, xid, env->xa_txn->off); 212 td = (TXN_DETAIL *) 213 ((u_int8_t *)env->tx_info->region + env->xa_txn->off); 214 td->xa_status = TXN_XA_STARTED; 215 } 216 return (XA_OK); 217 } 218 219 /* 220 * __db_xa_end -- 221 * Disassociate the current transaction from the current process. 222 */ 223 static int 224 __db_xa_end(xid, rmid, flags) 225 XID *xid; 226 int rmid; 227 long flags; 228 { 229 DB_ENV *env; 230 DB_TXN *txn; 231 TXN_DETAIL *td; 232 size_t off; 233 234 if (flags != TMNOFLAGS && !LF_ISSET(TMSUSPEND | TMSUCCESS | TMFAIL)) 235 return (XAER_INVAL); 236 237 if (__db_rmid_to_env(rmid, &env, 0) != 0) 238 return (XAER_PROTO); 239 240 if (__db_xid_to_txn(env, xid, &off) != 0) 241 return (XAER_NOTA); 242 243 txn = env->xa_txn; 244 if (off != txn->off) 245 return (XAER_PROTO); 246 247 td = (TXN_DETAIL *)((u_int8_t *)env->tx_info->region + off); 248 if (td->xa_status == TXN_XA_DEADLOCKED) 249 return (XA_RBDEADLOCK); 250 251 if (td->status == TXN_ABORTED) 252 return (XA_RBOTHER); 253 254 if (td->xa_status != TXN_XA_STARTED) 255 return (XAER_PROTO); 256 257 /* Update the shared memory last_lsn field */ 258 td->last_lsn = txn->last_lsn; 259 260 /* 261 * If we ever support XA migration, we cannot keep SUSPEND/END 262 * status in the shared region; it would have to be process local. 263 */ 264 if (LF_ISSET(TMSUSPEND)) 265 td->xa_status = TXN_XA_SUSPENDED; 266 else 267 td->xa_status = TXN_XA_ENDED; 268 269 txn->txnid = TXN_INVALID; 270 return (XA_OK); 271 } 272 273 /* 274 * __db_xa_prepare -- 275 * Sync the log to disk so we can guarantee recoverability. 276 */ 277 static int 278 __db_xa_prepare(xid, rmid, flags) 279 XID *xid; 280 int rmid; 281 long flags; 282 { 283 DB_ENV *env; 284 TXN_DETAIL *td; 285 size_t off; 286 287 if (LF_ISSET(TMASYNC)) 288 return (XAER_ASYNC); 289 if (flags != TMNOFLAGS) 290 return (XAER_INVAL); 291 292 /* 293 * We need to know if we've ever called prepare on this. 294 * As part of the prepare, we set the xa_status field to 295 * reflect that fact that prepare has been called, and if 296 * it's ever called again, it's an error. 297 */ 298 if (__db_rmid_to_env(rmid, &env, 1) != 0) 299 return (XAER_PROTO); 300 301 if (__db_xid_to_txn(env, xid, &off) != 0) 302 return (XAER_NOTA); 303 304 td = (TXN_DETAIL *)((u_int8_t *)env->tx_info->region + off); 305 306 if (td->xa_status == TXN_XA_DEADLOCKED) 307 return (XA_RBDEADLOCK); 308 309 if (td->xa_status != TXN_XA_ENDED && td->xa_status != TXN_XA_SUSPENDED) 310 return (XAER_PROTO); 311 312 /* Now, fill in the global transaction structure. */ 313 __xa_txn_init(env, td, off); 314 315 if (txn_prepare(env->xa_txn) != 0) 316 return (XAER_RMERR); 317 318 td->xa_status = TXN_XA_PREPARED; 319 320 /* No fatal value that would require an XAER_RMFAIL. */ 321 __xa_txn_end(env); 322 return (XA_OK); 323 } 324 325 /* 326 * __db_xa_commit -- 327 * Commit the transaction 328 */ 329 static int 330 __db_xa_commit(xid, rmid, flags) 331 XID *xid; 332 int rmid; 333 long flags; 334 { 335 DB_ENV *env; 336 TXN_DETAIL *td; 337 size_t off; 338 339 if (LF_ISSET(TMASYNC)) 340 return (XAER_ASYNC); 341 #undef OK_FLAGS 342 #define OK_FLAGS (TMNOFLAGS | TMNOWAIT | TMONEPHASE) 343 if (LF_ISSET(~OK_FLAGS)) 344 return (XAER_INVAL); 345 346 /* 347 * We need to know if we've ever called prepare on this. 348 * We can verify this by examining the xa_status field. 349 */ 350 if (__db_rmid_to_env(rmid, &env, 1) != 0) 351 return (XAER_PROTO); 352 353 if (__db_xid_to_txn(env, xid, &off) != 0) 354 return (XAER_NOTA); 355 356 td = (TXN_DETAIL *)((u_int8_t *)env->tx_info->region + off); 357 358 if (td->xa_status == TXN_XA_DEADLOCKED) 359 return (XA_RBDEADLOCK); 360 361 if (td->xa_status == TXN_XA_ABORTED) 362 return (XA_RBOTHER); 363 364 if (LF_ISSET(TMONEPHASE) && 365 td->xa_status != TXN_XA_ENDED && td->xa_status != TXN_XA_SUSPENDED) 366 return (XAER_PROTO); 367 368 if (!LF_ISSET(TMONEPHASE) && td->xa_status != TXN_XA_PREPARED) 369 return (XAER_PROTO); 370 371 /* Now, fill in the global transaction structure. */ 372 __xa_txn_init(env, td, off); 373 374 if (txn_commit(env->xa_txn) != 0) 375 return (XAER_RMERR); 376 377 /* No fatal value that would require an XAER_RMFAIL. */ 378 __xa_txn_end(env); 379 return (XA_OK); 380 } 381 382 /* 383 * __db_xa_recover -- 384 * Returns a list of prepared and heuristically completed transactions. 385 * 386 * The return value is the number of xids placed into the xid array (less 387 * than or equal to the count parameter). The flags are going to indicate 388 * whether we are starting a scan or continuing one. 389 */ 390 static int 391 __db_xa_recover(xids, count, rmid, flags) 392 XID *xids; 393 long count, flags; 394 int rmid; 395 { 396 __txn_xa_regop_args *argp; 397 DBT data; 398 DB_ENV *env; 399 DB_LOG *log; 400 XID *xidp; 401 char *dbhome; 402 int err, ret; 403 u_int32_t rectype, txnid; 404 405 ret = 0; 406 xidp = xids; 407 408 409 /* 410 * If we are starting a scan, then we need to open the environment 411 * and run recovery. This recovery puts us in a state where we can 412 * either commit or abort any transactions that were prepared but not 413 * yet committed. Once we've done that, we need to figure out where 414 * to begin checking for such transactions. If we are not starting 415 * a scan, then the environment had better have already been recovered 416 * and we'll start from * wherever the log cursor is. Since XA apps 417 * cannot be threaded, we don't have to worry about someone else 418 * having moved it. 419 */ 420 if (LF_ISSET(TMSTARTRSCAN)) { 421 /* If the environment is open, we have a problem. */ 422 if (__db_rmid_to_env(rmid, &env, 0) == XA_OK) 423 return (XAER_PROTO); 424 425 if ((ret = __os_calloc(1, sizeof(DB_ENV), &env)) != 0) 426 return (XAER_RMERR); 427 428 if (__db_rmid_to_name(rmid, &dbhome) != 0) 429 goto err1; 430 431 #undef XA_FLAGS 432 #define XA_FLAGS DB_RECOVER | \ 433 DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN 434 if ((ret = db_appinit(dbhome, NULL, env, XA_FLAGS)) != 0) 435 goto err1; 436 437 if (__db_map_rmid(rmid, env) != 0) 438 goto err2; 439 440 /* Now figure out from where to begin scan. */ 441 log = env->lg_info; 442 if ((err = __log_findckp(log, &log->xa_first)) == DB_NOTFOUND) { 443 /* 444 * If there were no log files, then we have no 445 * transactions to return, so we simply return 0. 446 */ 447 return (0); 448 } 449 if ((err = __db_txnlist_init(&log->xa_info)) != 0) 450 goto err3; 451 } else { 452 /* We had better already know about this rmid. */ 453 if (__db_rmid_to_env(rmid, &env, 0) != 0) 454 return (XAER_PROTO); 455 /* 456 * If we are not starting a scan, the log cursor had 457 * better be set. 458 */ 459 log = env->lg_info; 460 if (IS_ZERO_LSN(log->xa_lsn)) 461 return (XAER_PROTO); 462 } 463 464 /* 465 * At this point log->xa_first contains the point in the log 466 * to which we need to roll back. If we are starting a scan, 467 * we'll start at the last record; if we're continuing a scan, 468 * we'll have to start at log->xa_lsn. 469 */ 470 471 memset(&data, 0, sizeof(data)); 472 for (err = log_get(log, &log->xa_lsn, &data, 473 LF_ISSET(TMSTARTRSCAN) ? DB_LAST : DB_SET); 474 err == 0 && log_compare(&log->xa_lsn, &log->xa_first) > 0; 475 err = log_get(log, &log->xa_lsn, &data, DB_PREV)) { 476 memcpy(&rectype, data.data, sizeof(rectype)); 477 478 /* 479 * The only record type we care about is an DB_txn_xa_regop. 480 * If it's a commit, we have to add it to a txnlist. If it's 481 * a prepare, and we don't have a commit, then we return it. 482 * We are redoing some of what's in the xa_regop_recovery 483 * code, but we have to do it here so we can get at the xid 484 * in the record. 485 */ 486 if (rectype != DB_txn_xa_regop && rectype != DB_txn_regop) 487 continue; 488 489 memcpy(&txnid, (u_int8_t *)data.data + sizeof(rectype), 490 sizeof(txnid)); 491 err = __db_txnlist_find(log->xa_info, txnid); 492 switch (rectype) { 493 case DB_txn_regop: 494 if (err == DB_NOTFOUND) 495 __db_txnlist_add(log->xa_info, txnid); 496 err = 0; 497 break; 498 case DB_txn_xa_regop: 499 /* 500 * This transaction is commited, so we needn't read 501 * the record and do anything. 502 */ 503 if (err == 0) 504 break; 505 if ((err = 506 __txn_xa_regop_read(data.data, &argp)) != 0) { 507 ret = XAER_RMERR; 508 goto out; 509 } 510 511 xidp->formatID = argp->formatID; 512 xidp->gtrid_length = argp->gtrid; 513 xidp->bqual_length = argp->bqual; 514 memcpy(xidp->data, argp->xid.data, argp->xid.size); 515 ret++; 516 xidp++; 517 __os_free(argp, sizeof(*argp)); 518 if (ret == count) 519 goto done; 520 break; 521 } 522 } 523 524 if (err != 0 && err != DB_NOTFOUND) 525 goto out; 526 527 done: if (LF_ISSET(TMENDRSCAN)) { 528 ZERO_LSN(log->xa_lsn); 529 ZERO_LSN(log->xa_first); 530 531 out: __db_txnlist_end(log->xa_info); 532 log->xa_info = NULL; 533 } 534 return (ret); 535 536 err3: (void)__db_unmap_rmid(rmid); 537 err2: (void)db_appexit(env); 538 err1: __os_free(env, sizeof(DB_ENV)); 539 return (XAER_RMERR); 540 } 541 542 /* 543 * __db_xa_rollback 544 * Abort an XA transaction. 545 */ 546 static int 547 __db_xa_rollback(xid, rmid, flags) 548 XID *xid; 549 int rmid; 550 long flags; 551 { 552 DB_ENV *env; 553 TXN_DETAIL *td; 554 size_t off; 555 556 if (LF_ISSET(TMASYNC)) 557 return (XAER_ASYNC); 558 if (flags != TMNOFLAGS) 559 return (XAER_INVAL); 560 561 if (__db_rmid_to_env(rmid, &env, 1) != 0) 562 return (XAER_PROTO); 563 564 if (__db_xid_to_txn(env, xid, &off) != 0) 565 return (XAER_NOTA); 566 567 td = (TXN_DETAIL *)((u_int8_t *)env->tx_info->region + off); 568 569 if (td->xa_status == TXN_XA_DEADLOCKED) 570 return (XA_RBDEADLOCK); 571 572 if (td->xa_status == TXN_XA_ABORTED) 573 return (XA_RBOTHER); 574 575 if (LF_ISSET(TMONEPHASE) && 576 td->xa_status != TXN_XA_ENDED && td->xa_status != TXN_XA_SUSPENDED) 577 return (XAER_PROTO); 578 579 /* Now, fill in the global transaction structure. */ 580 __xa_txn_init(env, td, off); 581 if (txn_abort(env->xa_txn) != 0) 582 return (XAER_RMERR); 583 584 /* No fatal value that would require an XAER_RMFAIL. */ 585 __xa_txn_end(env); 586 return (XA_OK); 587 } 588 589 /* 590 * __db_xa_forget -- 591 * Forget about an XID for a transaction that was heuristically 592 * completed. Since we do not heuristically complete anything, I 593 * don't think we have to do anything here, but we should make sure 594 * that we reclaim the slots in the txnid table. 595 */ 596 static int 597 __db_xa_forget(xid, rmid, flags) 598 XID *xid; 599 int rmid; 600 long flags; 601 { 602 DB_ENV *env; 603 size_t off; 604 605 if (LF_ISSET(TMASYNC)) 606 return (XAER_ASYNC); 607 if (flags != TMNOFLAGS) 608 return (XAER_INVAL); 609 610 if (__db_rmid_to_env(rmid, &env, 1) != 0) 611 return (XAER_PROTO); 612 613 /* 614 * If mapping is gone, then we're done. 615 */ 616 if (__db_xid_to_txn(env, xid, &off) != 0) 617 return (XA_OK); 618 619 __db_unmap_xid(env, xid, off); 620 621 /* No fatal value that would require an XAER_RMFAIL. */ 622 return (XA_OK); 623 } 624 625 /* 626 * __db_xa_complete -- 627 * Used to wait for asynchronous operations to complete. Since we're 628 * not doing asynch, this is an invalid operation. 629 */ 630 static int 631 __db_xa_complete(handle, retval, rmid, flags) 632 int *handle, *retval, rmid; 633 long flags; 634 { 635 COMPQUIET(handle, NULL); 636 COMPQUIET(retval, NULL); 637 COMPQUIET(rmid, 0); 638 COMPQUIET(flags, 0); 639 640 return (XAER_INVAL); 641 } 642 643 /* 644 * __xa_txn_init -- 645 * Fill in the fields of the local transaction structure given 646 * the detail transaction structure. 647 */ 648 static void 649 __xa_txn_init(env, td, off) 650 DB_ENV *env; 651 TXN_DETAIL *td; 652 size_t off; 653 { 654 DB_TXN *txn; 655 656 txn = env->xa_txn; 657 txn->mgrp = env->tx_info; 658 txn->parent = NULL; 659 txn->last_lsn = td->last_lsn; 660 txn->txnid = td->txnid; 661 txn->off = off; 662 txn->flags = 0; 663 } 664 665 /* 666 * __xa_txn_end -- 667 * Invalidate a transaction structure that was generated by xa_txn_init. 668 */ 669 static void 670 __xa_txn_end(env) 671 DB_ENV *env; 672 { 673 DB_TXN *txn; 674 675 txn = env->xa_txn; 676 if (txn != NULL) 677 txn->txnid = TXN_INVALID; 678 } 679 680