fis-gtm/sr_port/op_tstart.c

597 lines
23 KiB
C

/****************************************************************
* *
* Copyright 2001, 2012 Fidelity Information Services, Inc *
* *
* This source code contains the intellectual property *
* of its copyright holder(s), and is made available *
* under a license. If you do not know the terms of *
* the license, please stop and do not read further. *
* *
****************************************************************/
#include "mdef.h"
#include <stdarg.h>
#include <stddef.h> /* for OFFSETOF macro */
#include "gtm_string.h"
#include "gtm_stdio.h"
#include "gdsroot.h"
#include "gdsblk.h"
#include "gtm_facility.h"
#include "fileinfo.h"
#include "gdsbt.h"
#include "gdsfhead.h"
#include "filestruct.h"
#include "gdscc.h"
#include "gdskill.h"
#include "cdb_sc.h"
#include "lv_val.h"
#include "jnl.h"
#include "mlkdef.h"
#include <rtnhdr.h>
#include "mv_stent.h"
#include "stack_frame.h"
#include "tp_frame.h"
#include "buddy_list.h" /* needed for tp.h */
#include "hashtab_int4.h" /* needed for tp.h and cws_insert.h */
#include "tp.h"
#include "tp_timeout.h"
#include "op.h"
#include "have_crit.h"
#include "gtm_caseconv.h"
#include "gvcst_protos.h" /* for gvcst_tp_init prototype */
#include "dpgbldir.h"
#include "longset.h" /* needed for cws_insert.h */
#include "cws_insert.h" /* for cw_stagnate_reinitialized */
#include "alias.h"
#ifdef GTM_TRIGGER
#include "gtm_trigger_trc.h"
#endif
#ifdef UNICODE_SUPPORTED
#include "gtm_icu_api.h" /* needed by *TYPEMASK* macros defined in gtm_utf8.h */
#include "gtm_utf8.h"
#endif
error_def(ERR_STACKCRIT);
error_def(ERR_STACKOFLOW);
error_def(ERR_TPMIXUP);
error_def(ERR_TPTOODEEP);
GBLREF jnl_fence_control jnl_fence_ctl;
GBLREF uint4 dollar_tlevel;
GBLREF boolean_t dollar_truth;
GBLREF mval dollar_zgbldir;
GBLREF sgmnt_addrs *cs_addrs;
GBLREF gd_addr *gd_header;
GBLREF gv_key *gv_currkey;
GBLREF gv_namehead *gv_target;
GBLREF stack_frame *frame_pointer;
GBLREF tp_frame *tp_pointer;
GBLREF mv_stent *mv_chain;
GBLREF mlk_pvtblk *mlk_pvt_root;
GBLREF symval *curr_symval;
GBLREF unsigned char *msp, *stacktop, *stackwarn, *tpstackbase, *tpstacktop, *tp_sp, t_fail_hist[CDB_MAX_TRIES];
GBLREF unsigned int t_tries;
GBLREF tp_region *tp_reg_list, *tp_reg_free_list;
GBLREF trans_num local_tn; /* transaction number for THIS PROCESS */
GBLREF trans_num tstart_local_tn; /* copy of global variable "local_tn" at op_tstart time */
GBLREF boolean_t mupip_jnl_recover;
GBLREF void (*tp_timeout_start_timer_ptr)(int4 tmout_sec);
GBLREF sgm_info *first_sgm_info;
GBLREF global_tlvl_info *global_tlvl_info_head;
GBLREF buddy_list *global_tlvl_info_list;
GBLREF sgmnt_data_ptr_t cs_data;
GBLREF sgmnt_addrs *cs_addrs;
GBLREF gd_region *gv_cur_region;
GBLREF struct_jrec_tcom tcom_record;
GBLREF jnl_gbls_t jgbl;
GBLREF boolean_t tp_in_use;
GBLREF boolean_t gtm_utf8_mode;
GBLREF uint4 tstartcycle;
GBLREF char *update_array_ptr;
GBLREF ua_list *curr_ua, *first_ua;
#ifdef GTM_TRIGGER
GBLREF mval dollar_ztwormhole;
GBLREF int4 gtm_trigger_depth;
GBLREF int4 tstart_trigger_depth;
GBLREF mval dollar_ztslate;
LITREF mval literal_null;
#endif
#ifdef VMS
GBLREF boolean_t tp_has_kill_t_cse; /* cse->mode of kill_t_write or kill_t_create got created in this transaction */
#endif
#define NORESTART -1
#define ALLLOCAL -2
#define TP_STACK_SIZE ((TP_MAX_NEST + 1) * SIZEOF(tp_frame)) /* Size of TP stack frame with no-overflow pad */
void op_tstart(int implicit_flag, ...) /* value of $T when TSTART */
{
boolean_t serial; /* whether SERIAL keyword was present */
int prescnt, /* number of names to save, -1 = no restart, -2 = preserve all */
pres;
lv_val *lv;
mlk_pvtblk *pre_lock;
mlk_tp *lck_tp;
mval *preserve, /* list of names to save */
*tid, /* transaction id */
*mvname;
mv_stent *mv_st_ent, *mvst_tmp, *mvst_prev;
stack_frame *fp, *fp_fix;
tp_frame *tf;
unsigned char *old_sp, *top, *tstack_ptr, *ptrstart, *ptrend, *ptrinvalidbegin;
va_list varlst, lvname;
tp_region *tr, *tr_next;
sgm_info *si;
tlevel_info *tli, *new_tli, *prev_tli;
global_tlvl_info *gtli, *new_gtli, *prev_gtli;
kill_set *ks, *prev_ks;
jnl_format_buffer *jfb, *prev_jfb;
gd_region *r_top, *r_local;
gd_addr *addr_ptr;
unsigned char tp_bat[TP_BATCH_LEN];
mname_entry tpvent;
ht_ent_mname *tabent, *curent, *topent;
sgmnt_addrs *csa;
int4 shift_size;
boolean_t tphold_noshift = FALSE, implicit_tstart;
GTMTRIG_ONLY(boolean_t implicit_trigger;)
DCL_THREADGBL_ACCESS;
SETUP_THREADGBL_ACCESS;
implicit_tstart = 0 != (implicit_flag & IMPLICIT_TSTART);
GTMTRIG_ONLY(implicit_trigger = 0 != (implicit_flag & IMPLICIT_TRIGGER_TSTART));
GTMTRIG_ONLY(assert(!implicit_trigger || (implicit_trigger && implicit_tstart)));
GTMTRIG_ONLY(DBGTRIGR((stderr, "op_tstart: Entered - dollar_tlevel: %d, implicit_flag: %d\n",
dollar_tlevel, implicit_flag)));
if (implicit_tstart)
/* An implicit op_tstart is being done. In this case, even if we are in direct mode, we want to do
* regular TPHOLD processing (no setting of tphold in the parent frame and shifting of all mv_stents).
* This is ok because the life of the TP transaction will be done before the implicit operation()s are done
* done so it will not persist across M lines like it normally would in direct mode.
*/
tphold_noshift = TRUE;
/* If we haven't done any TP until now, turn the flag on to tell gvcst_init to
initialize it in any regions it opens from now on and initialize it in any
regions that are already open.
*/
if (!tp_in_use)
{
tp_in_use = TRUE;
for (addr_ptr = get_next_gdr(NULL); addr_ptr; addr_ptr = get_next_gdr(addr_ptr))
{
for (r_local = addr_ptr->regions, r_top = r_local + addr_ptr->n_regions; r_local < r_top; r_local++)
{
if (r_local->open && !r_local->was_open &&
(dba_bg == r_local->dyn.addr->acc_meth || dba_mm == r_local->dyn.addr->acc_meth))
{ /* Let's initialize those regions but only if it came through gvcst_init_sysops
(being a bg or mm region)
*/
gvcst_tp_init(r_local);
}
}
}
}
if (0 != jnl_fence_ctl.level)
rts_error(VARLSTCNT(4) ERR_TPMIXUP, 2, "An M", "a fenced logical");
if (dollar_tlevel + 1 >= TP_MAX_NEST)
rts_error(VARLSTCNT(1) ERR_TPTOODEEP);
va_start(varlst, implicit_flag); /* no argument count first */
serial = va_arg(varlst, int);
tid = va_arg(varlst, mval *);
prescnt = va_arg(varlst, int);
MV_FORCE_STR(tid);
if (!dollar_tlevel)
{
jnl_fence_ctl.fence_list = JNL_FENCE_LIST_END;
jgbl.cumul_jnl_rec_len = 0;
# ifdef DEBUG
if (1 == jgbl.cumul_index) /* when 1 == jgbl.cumul_index and 0 == jgbl.cumul_index, non-TP morphed into TP */
jgbl.cumul_index = 0;
else
assert(0 == jgbl.cumul_index);
TREF(tp_restart_dont_counts) = 0;
# endif
assert(0 == jgbl.cu_jnl_index);
GTMTRIG_ONLY(memcpy(&dollar_ztslate, &literal_null, SIZEOF(mval))); /* Zap $ZTSLate at start of lvl 1 trans */
GTMTRIG_ONLY(if (!implicit_tstart || !implicit_trigger))
{ /* This is the path for all non-implicit-trigger type TP fences including the implicit fences
* created by the update process and by mupip recover forward.
* Note: For recover/rollback, t_tries is set to CDB_STAGNATE because unlike Non-TP restarts, TP restarts
* need more context to determine the restart point which is non-trivial for recover/rollback since the
* updates are done with journal records extracted by sequentially reading the journal file.
*/
t_tries = (FALSE == mupip_jnl_recover) ? 0 : CDB_STAGNATE;
t_fail_hist[t_tries] = cdb_sc_normal;
/* ensure that we don't have crit on any region at the beginning of a TP transaction (be it GT.M or MUPIP).
* The only exception is ONLINE ROLLBACK which holds crit for the entire duration
*/
assert(0 == have_crit(CRIT_HAVE_ANY_REG) UNIX_ONLY(|| jgbl.onlnrlbk));
}
# ifdef GTM_TRIGGER
else
{
/* This is an implicit TP wrap created for an explicit update. In such case, we do not want to reset
* t_tries for below reasons:
* (a) If an explicit non-tp update undergoes 3 restarts (t_tries = 3) and on the final retry gvcst_put
* sees that triggers are defined (due to concurrent $ZTRIGGER or MUPIP TRIGGER), it would have invoked
* op_tstart to create a TP wrap in final retry in which case we should NOT reset t_tries to zero as we
* would be holding crit at that time.
*
* (b) If an explicit non-tp update underwent 1 restart and on the next try(t_tries = 1), gvcst_put
* sees that triggers are defined (due to concurrent $ZTRIGGER or MUPIP TRIGGER), it would have invoked
* op_tstart to create a TP wrap in which case we should NOT reset t_tries. In the event, we did the
* reset of t_tries and no triggers were defined for the update in question, op_tcommit would be done
* and we will be back in non-TP BUT with t_tries reset to 0. The above can continue in a cycle causing
* a live spin-lock that does not let the final-retry optimistic -> pessimistic concurrency scheme from
* kicking in at all.
*/
/* recovery logic does not invoke triggers */
assert(!(SFF_IMPLTSTART_CALLD & frame_pointer->flags) || (FALSE == mupip_jnl_recover));
}
# endif
for (tr = tp_reg_list; NULL != tr; tr = tr_next)
{ /* start with empty list, place all existing entries on free list */
tp_reg_list = tr_next = tr->fPtr; /* Remove from queue */
tr->fPtr = tp_reg_free_list;
tp_reg_free_list = tr; /* Place on free queue */
}
++local_tn; /* Begin new local transaction */
tstart_local_tn = local_tn;
/* In journal recovery forward phase, we set jgbl.tp_ztp_jnl_upd_num to whatever update_num the journal record
* has so it is ok for the global variable to be a non-zero value at the start of a TP transaction (possible if
* ZTP of one process is in progress when TP of another process starts in the journal file). But otherwise
* (in GT.M runtime) we expect it to be 0 at beginning of each TP or ZTP.
*/
assert((0 == jgbl.tp_ztp_jnl_upd_num) || jgbl.forw_phase_recovery);
INCR_TSTARTCYCLE;
jgbl.wait_for_jnl_hard = TRUE;
GTMTRIG_ONLY(
assert(NULL == jgbl.prev_ztworm_ptr); /* should have been cleared by tp_clean_up of previous TP */
assert(NULL == jgbl.save_ztworm_ptr);
/* should have been NULL almost always except for a small window in gvcst_put/gvcst_kill */
tstart_trigger_depth = gtm_trigger_depth; /* note down what trigger depth an outermost tstart occurs in */
)
memset(tcom_record.jnl_tid, 0, TID_STR_SIZE);
if (0 != tid->str.len)
{
if (!gtm_utf8_mode)
{
if (tid->str.len > TID_STR_SIZE)
tid->str.len = TID_STR_SIZE;
}
# ifdef UNICODE_SUPPORTED
else
{ /* In UTF8 mode, take only as many valid multi-byte characters as can fit in TID_STR_SIZE */
if (gtm_utf8_mode)
{
MV_FORCE_LEN(tid); /* issues BADCHAR error if appropriate */
if (tid->str.len > TID_STR_SIZE)
{
ptrstart = (unsigned char *)tid->str.addr;
ptrend = ptrstart + TID_STR_SIZE;
UTF8_LEADING_BYTE(ptrend, ptrstart, ptrinvalidbegin)
tid->str.len = INTCAST(ptrinvalidbegin - ptrstart);
}
}
}
# endif
assert(TID_STR_SIZE >= tid->str.len);
memcpy(tcom_record.jnl_tid, (char *)tid->str.addr, tid->str.len);
if ((TP_BATCH_SHRT == tid->str.len) || (TP_BATCH_LEN == tid->str.len))
{
lower_to_upper(tp_bat, (uchar_ptr_t)tid->str.addr, (int)tid->str.len);
if (0 == memcmp(TP_BATCH_ID, tp_bat, tid->str.len))
jgbl.wait_for_jnl_hard = FALSE;
}
}
VMS_ONLY(tp_has_kill_t_cse = FALSE;)
assert(!TREF(donot_commit));
}
/* either cw_stagnate has not been initialized at all or previous-non-TP or tp_hist should have done CWS_RESET */
assert((0 == cw_stagnate.size) || cw_stagnate_reinitialized);
if (prescnt > 0)
{
VAR_COPY(lvname, varlst);
for (pres = 0; pres < prescnt; ++pres)
{
preserve = va_arg(lvname, mval *);
assert(MV_IS_STRING(preserve)); /* Check if this loop can be eliminated */
MV_FORCE_STR(preserve);
}
va_end(lvname);
}
if (NULL == gd_header)
gvinit();
assert(NULL != gd_header);
if (!tphold_noshift && (SFT_DM & frame_pointer->old_frame_pointer->type))
{ /* Put a TPHOLD underneath dmode frame */
assert(frame_pointer->old_frame_pointer->old_frame_pointer);
fp = frame_pointer->old_frame_pointer->old_frame_pointer;
top = (unsigned char *)(frame_pointer->old_frame_pointer + 1);
old_sp = msp;
shift_size = mvs_size[MVST_TPHOLD];
msp -= shift_size;
if (msp <= stackwarn)
{
va_end(varlst);
if (msp <= stacktop)
{
msp = old_sp;
rts_error(VARLSTCNT(1) ERR_STACKOFLOW);
} else
rts_error(VARLSTCNT(1) ERR_STACKCRIT);
}
memmove(msp, old_sp, top - (unsigned char *)old_sp); /* Shift stack w/possible overlapping ranges */
mv_st_ent = (mv_stent *)(top - shift_size);
mv_st_ent->mv_st_type = MVST_TPHOLD;
ADJUST_FRAME_POINTER(frame_pointer, shift_size);
for (fp_fix = frame_pointer; fp_fix != fp; fp_fix = fp_fix->old_frame_pointer)
{
if ((unsigned char *)fp_fix->l_symtab < top && (unsigned char *)fp_fix->l_symtab > stacktop)
fp_fix->l_symtab = (ht_ent_mname **)((char *)fp_fix->l_symtab - shift_size);
if (fp_fix->temps_ptr < top && fp_fix->temps_ptr > stacktop)
fp_fix->temps_ptr -= shift_size;
if (fp_fix->vartab_ptr < (char *)top && fp_fix->vartab_ptr > (char *)stacktop)
fp_fix->vartab_ptr -= shift_size;
if ((unsigned char *)fp_fix->old_frame_pointer < top &&
(char *)fp_fix->old_frame_pointer > (char *)stacktop)
{
ADJUST_FRAME_POINTER(fp_fix->old_frame_pointer, shift_size);
}
}
if ((unsigned char *)mv_chain >= top)
{
mv_st_ent->mv_st_next = (unsigned int)((char *)mv_chain - (char *)mv_st_ent);
mv_chain = mv_st_ent;
} else
{
top -= shift_size + SIZEOF(stack_frame);
mv_chain = (mv_stent *)((char *)mv_chain - shift_size);
mvst_tmp = mv_chain;
mvst_prev = (mv_stent *)((char *)mvst_tmp + mvst_tmp->mv_st_next);
while (mvst_prev < (mv_stent *)top)
{
mvst_tmp = mvst_prev;
mvst_prev = (mv_stent *)((char *)mvst_tmp + mvst_tmp->mv_st_next);
}
mvst_tmp->mv_st_next = (unsigned int)((char *)mv_st_ent - (char *)mvst_tmp);
mv_st_ent->mv_st_next = (unsigned int)((char *)mvst_prev - (char *)mv_st_ent + shift_size);
}
} else
{
PUSH_MV_STENT(MVST_TPHOLD);
mv_st_ent = mv_chain;
fp = frame_pointer;
}
mv_st_ent->mv_st_cont.mvs_tp_holder.tphold_tlevel = dollar_tlevel;
# ifdef GTM_TRIGGER
if (!dollar_tlevel)
/* We only save this on level 0 - Note if this is made further conditional, be sure to visit
* stp_gcol_src.h where it is GC'd and tp_restart() to adjust the conditions there as well.
*/
memcpy(&mv_st_ent->mv_st_cont.mvs_tp_holder.ztwormhole_save, &dollar_ztwormhole, SIZEOF(mval));
# endif
if (NULL == tpstackbase)
{
tstack_ptr = (unsigned char *)malloc(TP_STACK_SIZE);
tp_sp = tpstackbase = tstack_ptr + TP_STACK_SIZE;
tpstacktop = tstack_ptr;
tp_pointer = NULL;
}
/* Add a new tp_frame in the TP stack */
DBGRFCT((stderr, "\n*** op_tstart: *** Entering $TLEVEL = %d\n", dollar_tlevel + 1));
tf = (tp_frame *)(tp_sp -= SIZEOF(tp_frame));
assert((unsigned char *)tf > tpstacktop); /* Block should lie entirely within tp stack area */
tf->dlr_t = dollar_truth;
tf->restart_pc = fp->mpc;
tf->restart_ctxt = fp->ctxt;
tf->fp = fp;
tf->serial = serial;
tf->trans_id = *tid;
tf->restartable = (NORESTART != prescnt);
tf->old_locks = (NULL != mlk_pvt_root);
tf->orig_gv_target = gv_target;
DBG_CHECK_GVTARGET_CSADDRS_IN_SYNC;
# ifdef DEBUG
if (!jgbl.forw_phase_recovery)
{ /* In case of forward phase of journal recovery, gv_currkey is set by caller (mur_forward) only
* after the call to op_tstart so avoid doing gv_currkey check.
*/
DBG_CHECK_GVTARGET_GVCURRKEY_IN_SYNC;
}
# endif
/* If the TP structures have not yet been initialized, do that now. */
if (NULL == TREF(gv_tporigkey_ptr))
{ /* This need only be set once */
TREF(gv_tporigkey_ptr) = (gv_orig_key_array *)malloc(SIZEOF(gv_orig_key_array));
memset(TREF(gv_tporigkey_ptr), 0, SIZEOF(gv_orig_key_array));
}
tf->orig_key = (gv_key *)&((TREF(gv_tporigkey_ptr))->gv_orig_key[dollar_tlevel][0]);
assert(NULL != gv_currkey);
MEMCPY_KEY(tf->orig_key, gv_currkey);
tf->gd_header = gd_header;
tf->gd_reg = gv_cur_region;
tf->zgbldir = dollar_zgbldir;
tf->mvc = mv_st_ent;
tf->sym = curr_symval;
tf->tp_save_all_flg = FALSE;
if (NULL == tp_pointer)
{
tf->implicit_tstart = implicit_tstart;
GTMTRIG_ONLY(tf->implicit_trigger = implicit_trigger);
} else
{
tf->implicit_tstart = tp_pointer->implicit_tstart;
GTMTRIG_ONLY(tf->implicit_trigger = tp_pointer->implicit_trigger);
}
GTMTRIG_ONLY(tf->cannot_commit = FALSE;)
tf->vars = (tp_var *)NULL;
tf->old_tp_frame = tp_pointer;
tp_pointer = tf;
if (prescnt > 0)
{
VAR_COPY(lvname, varlst);
for (pres = 0; pres < prescnt; ++pres)
{
preserve = va_arg(lvname, mval *);
/* Note: the assumption (according to the comment below) is that this mval points into the literal table
and thus could not possibly be undefined. In that case, I do not understand why the earlier loop to
do MV_FORCE_STR on these variables. Future todo -- verify if that loop is needed. On the assumption
that it is not, the below assert will verify that the mval is defined to catch any NOUNDEF case.
*/
assert(MV_DEFINED(preserve));
/* The incoming 'preserve' is the pointer to a literal mval table entry. For the indirect code
* (eg. Direct Mode), since the literal table is no longer on the M stack, we should not shift
* the incoming va_arg pointer (C9D01-002205) */
mvname = preserve;
if (0 != mvname->str.len)
{ /* Convert mval to mident and see if it's in the symbol table */
tpvent.var_name.len = mvname->str.len;
tpvent.var_name.addr = mvname->str.addr;
COMPUTE_HASH_MNAME(&tpvent);
tpvent.marked = FALSE;
if (add_hashtab_mname_symval(&curr_symval->h_symtab, &tpvent, NULL, &tabent))
lv_newname(tabent, curr_symval);
lv = (lv_val *)tabent->value;
assert(lv);
assert(LV_IS_BASE_VAR(lv));
assert(0 < lv->stats.trefcnt);
assert(lv->stats.crefcnt <= lv->stats.trefcnt);
/* In order to allow restart of a sub-transaction, this should chain rather than back stop,
with appropriate changes to lv_var_clone and tp_unwind */
if (NULL == lv->tp_var)
{
TP_SAVE_RESTART_VAR(lv, tf, &tabent->key);
if (LV_HAS_CHILD(lv))
TPSAV_CNTNRS_IN_TREE(lv);
} else
{ /* We have saved this var previously. But check if it got saved via a container var
and therefore has no name associated with it. If so, update the key in the tp_var
structure so it gets its name restored properly if necessary.
*/
if (0 == lv->tp_var->key.var_name.len)
lv->tp_var->key = tabent->key;
}
}
}
va_end(lvname);
} else if (ALLLOCAL == prescnt)
{ /* Preserve all variables */
tf->tp_save_all_flg = TRUE;
++curr_symval->tp_save_all;
for (curent = curr_symval->h_symtab.base, topent = curr_symval->h_symtab.top; curent < topent; curent++)
{
if (HTENT_VALID_MNAME(curent, lv_val, lv) && ('$' != *curent->key.var_name.addr))
{
assert(lv);
assert(LV_IS_BASE_VAR(lv));
assert(0 < lv->stats.trefcnt);
assert(lv->stats.crefcnt <= lv->stats.trefcnt);
if (NULL == lv->tp_var)
{
TP_SAVE_RESTART_VAR(lv, tf, &curent->key);
if (LV_HAS_CHILD(lv))
TPSAV_CNTNRS_IN_TREE(lv);
} else
{ /* We have saved this var previously. But check if it got saved via a container var
and therefore has no name associated with it. If so, update the key in the tp_var
structure so it gets its name restored properly if necessary.
*/
if (0 == lv->tp_var->key.var_name.len)
lv->tp_var->key = curent->key;
}
}
}
}
va_end(varlst);
/* Store existing state of locks */
for (pre_lock = mlk_pvt_root; NULL != pre_lock; pre_lock = pre_lock->next)
{
if ((NULL == pre_lock->tp) || (pre_lock->tp->level != pre_lock->level)
|| (pre_lock->tp->zalloc != pre_lock->zalloc))
{ /* Only stack locks that have changed since last TSTART */
lck_tp = (mlk_tp *)malloc(SIZEOF(mlk_tp));
lck_tp->tplevel = dollar_tlevel;
lck_tp->level = pre_lock->level;
lck_tp->zalloc = pre_lock->zalloc;
lck_tp->next = pre_lock->tp;
pre_lock->tp = lck_tp;
}
}
++dollar_tlevel;
/* Store the global (across all segments) dollar_tlevel specific information. */
if (NULL != first_sgm_info) /* database activity existed in prior levels */
{
for (prev_gtli = NULL, gtli = global_tlvl_info_head; gtli; gtli = gtli->next_global_tlvl_info)
prev_gtli = gtli;
new_gtli = (global_tlvl_info *)get_new_element(global_tlvl_info_list, 1);
new_gtli->global_tlvl_fence_info = jnl_fence_ctl.fence_list;
new_gtli->t_level = dollar_tlevel;
GTMTRIG_ONLY(new_gtli->tlvl_prev_ztworm_ptr = jgbl.prev_ztworm_ptr;)
new_gtli->tlvl_cumul_jrec_len = jgbl.cumul_jnl_rec_len;
DEBUG_ONLY(new_gtli->tlvl_cumul_index = jgbl.cumul_index;)
new_gtli->tlvl_tp_ztp_jnl_upd_num = jgbl.tp_ztp_jnl_upd_num;
/* Store current state of update_array global variables */
assert((NULL != first_ua) && (NULL != curr_ua)); /* Since first_sgm_info is NOT NULL, database activity existed */
new_gtli->curr_ua = (struct ua_list *)(curr_ua);
new_gtli->upd_array_ptr = update_array_ptr;
new_gtli->next_global_tlvl_info = NULL;
if (prev_gtli)
{
assert(prev_gtli->t_level + 1 == dollar_tlevel);
prev_gtli->next_global_tlvl_info = new_gtli;
} else
global_tlvl_info_head = new_gtli;
}
/* Store the dollar_tlevel specific information */
for (si = first_sgm_info; NULL != si; si = si->next_sgm_info)
{
for (prev_tli = NULL, tli = si->tlvl_info_head; (NULL != tli); tli = tli->next_tlevel_info)
prev_tli = tli;
new_tli = (tlevel_info *)get_new_element(si->tlvl_info_list, 1);
new_tli->t_level = dollar_tlevel;
for (prev_ks = NULL, ks = si->kill_set_head; ks; ks = ks->next_kill_set)
prev_ks = ks;
new_tli->tlvl_kill_set = prev_ks;
if (prev_ks)
new_tli->tlvl_kill_used = prev_ks->used;
else
new_tli->tlvl_kill_used = 0;
new_tli->tlvl_tp_hist_info = si->last_tp_hist;
/* Prepare for journaling logical records if journaling is enabled on this region OR if replication was
* allowed on this region (this is a case where replication was ON originally but later transitioned
* into WAS_ON state and journaling got turned OFF.
*/
csa = si->tp_csa;
if (JNL_WRITE_LOGICAL_RECS(csa))
{
assert((NULL != si->jnl_head) || (NULL == csa->next_fenced));
assert((NULL == si->jnl_head) || (NULL != csa->next_fenced));
assert((NULL == csa->next_fenced) || (JNL_FENCE_LIST_END == csa->next_fenced)
|| (NULL != csa->next_fenced->sgm_info_ptr->jnl_head));
assert(NULL == *si->jnl_tail);
SET_PREV_JFB(si, prev_jfb);
new_tli->tlvl_jfb_info = prev_jfb;
assert(NULL != si->jnl_list);
assert(NULL != si->format_buff_list);
new_tli->jnl_list_elems = si->jnl_list->nElems;
new_tli->jfb_list_elems = si->format_buff_list->nElems;
}
new_tli->next_tlevel_info = NULL;
new_tli->update_trans = si->update_trans;
if (prev_tli)
prev_tli->next_tlevel_info = new_tli;
else
si->tlvl_info_head = new_tli;
}
/* If starting first TP level, also start TP timer if set to non-default value */
assert(0 <= TREF(dollar_zmaxtptime));
if ((0 < TREF(dollar_zmaxtptime)) && (1 == dollar_tlevel))
(*tp_timeout_start_timer_ptr)(TREF(dollar_zmaxtptime));
DBGRFCT((stderr, "\nop_tstart: complete\n"));
}