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
__db_xa_open(xa_info,rmid,flags)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
__db_xa_close(xa_info,rmid,flags)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
__db_xa_start(xid,rmid,flags)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
__db_xa_end(xid,rmid,flags)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
__db_xa_prepare(xid,rmid,flags)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
__db_xa_commit(xid,rmid,flags)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
__db_xa_recover(xids,count,rmid,flags)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
__db_xa_rollback(xid,rmid,flags)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
__db_xa_forget(xid,rmid,flags)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
__db_xa_complete(handle,retval,rmid,flags)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
__xa_txn_init(env,td,off)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
__xa_txn_end(env)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