/****************************************************************
 *								*
 *	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"

#ifdef VMS
#include <ssdef.h>
#include <psldef.h>
#include <descrip.h>
#endif

#include "gtm_inet.h"
#include "gtm_string.h"

#include "gdsroot.h"
#include "gtm_facility.h"
#include "fileinfo.h"
#include "gdsbt.h"
#include "gdsblk.h"
#include "gdsfhead.h"
#include "filestruct.h"
#include "gdscc.h"
#include "min_max.h"
#include "gdsblkops.h"
#include "gdsbml.h"
#include "gdskill.h"
#include "copy.h"
#ifdef VMS
#include "lockconst.h"
#endif
#include "interlock.h"
#include "jnl.h"
#include "probe.h"
#include "buddy_list.h"		/* needed for tp.h */
#include "hashtab_int4.h"	/* needed for tp.h */
#include "tp.h"
#include "io.h"
#include "gtmsecshr.h"
#include "repl_msg.h"
#include "gtmsource.h"
#include "is_proc_alive.h"
#include "aswp.h"
#include "util.h"
#include "compswap.h"
#ifdef UNIX
#include "mutex.h"
#include "repl_instance.h"	/* needed for JNLDATA_BASE_OFF macro */
#include "mupipbckup.h"		/* needed for backup_block prototype */
#include "cert_blk.h"		/* for CERT_BLK_IF_NEEDED macro */
#include "relqueopi.h"		/* for INSQTI and INSQHI macros */
#include "caller_id.h"
#endif
#include "sec_shr_blk_build.h"
#include "sec_shr_map_build.h"
#include "add_inter.h"
#include "send_msg.h"	/* for send_msg prototype */
#include "secshr_db_clnup.h"
#include "gdsbgtr.h"
#include "memcoherency.h"
#include "shmpool.h"
#include "wbox_test_init.h"
#ifdef GTM_SNAPSHOT
#include "db_snapshot.h"
#endif

/* This section documents DOs and DONTs about code used by GTMSECSHR on Alpha VMS. Any module linked into GTMSECSHR (see
 * secshrlink.axp for the current list) must follow certain rules as GTMSECSHR provides user-defined system services
 * (privileged image that runs in kernel mode). See "Creating User Written System Sevice" chapter of the "Programming Concepts"
 * OpenVMS manual and the "Shareable Images Cookbook" available from the OpenVMS Wizard's page. SYS$EXAMPLES:uwss*.* is also a
 * good reference.
 *
 ** DO NOT use modulo (%) operation. If % is used, GTMSECSHR links with LIBOTS.EXE - an external shared image. This will result
 *  in "-SYSTEM-F-NOSHRIMG, privileged shareable image cannot have outbound calls" errors when GTMSECSHR is invoked. We might as
 *  well avoid division too.
 *
 ** The only library/system calls allowed are SYS$ calls.
 *
 ** No I/O allowed - any device, including operator console.
 *
 ** Always PROBE memory before accessing it. If not, should SECSHR access invalid memory (out of bounds for instance) the machine
 *  will crash (BUGCHECK in VMS parlance). Remember, SECSHR is running in kernel mode!
 *
 ** Both secshr_db_clnup.c and sec_shr_blk_build.c are compiled with /prefix=except=memmove. If any of the other modules used
 *  memmove, they would need special treatment as well.
 */

#define FLUSH 1

#define	WCBLOCKED_NOW_CRIT_LIT		"wcb_secshr_db_clnup_now_crit"
#define	WCBLOCKED_WBUF_DQD_LIT		"wcb_secshr_db_clnup_wbuf_dqd"
#define	WCBLOCKED_PHASE2_CLNUP_LIT	"wcb_secshr_db_clnup_phase2_clnup"

/* IMPORTANT : SECSHR_PROBE_REGION sets csa */
#define	SECSHR_PROBE_REGION(reg)									\
	if (!GTM_PROBE(SIZEOF(gd_region), (reg), READ))							\
		continue; /* would be nice to notify the world of a problem but where and how?? */	\
	if (!reg->open || reg->was_open)								\
		continue;										\
	if (!GTM_PROBE(SIZEOF(gd_segment), (reg)->dyn.addr, READ))					\
		continue; /* would be nice to notify the world of a problem but where and how? */	\
	if ((dba_bg != (reg)->dyn.addr->acc_meth) && (dba_mm != (reg)->dyn.addr->acc_meth))		\
		continue;										\
	if (!GTM_PROBE(SIZEOF(file_control), (reg)->dyn.addr->file_cntl, READ))				\
		continue; /* would be nice to notify the world of a problem but where and how? */	\
	if (!GTM_PROBE(SIZEOF(GDS_INFO), (reg)->dyn.addr->file_cntl->file_info, READ))			\
		continue; /* would be nice to notify the world of a problem but where and how? */	\
	csa = &(FILE_INFO((reg)))->s_addrs;								\
	if (!GTM_PROBE(SIZEOF(sgmnt_addrs), csa, WRITE))						\
		continue; /* would be nice to notify the world of a problem but where and how? */	\
	assert(reg->read_only && !csa->read_write || !reg->read_only && csa->read_write);

#ifdef DEBUG_CHECK_LATCH
#  define DEBUG_LATCH(x) x
#else
#  define DEBUG_LATCH(x)
#endif

#ifdef VMS
/* Use compswap_secshr instead of compswap in our expansions */
#  define compswap compswap_secshr
#  define SALVAGE_UNIX_LATCH(X, is_exiting)
#else
#  define SALVAGE_UNIX_LATCH_DBCRIT(X, is_exiting, wcblocked)								\
{	/* "wcblocked" is relevant only if X is the database crit semaphore. In this case, BEFORE salvaging crit,	\
	 * (but AFTER ensuring the previous holder pid is dead) we need to set cnl->wc_blocked to TRUE to		\
	 * ensure whoever grabs crit next does a cache-recovery. This is necessary in case previous holder of crit	\
	 * had set some cr->in_cw_set to a non-zero value. Not doing cache recovery could cause incorrect GTMASSERTs	\
	 * in PIN_CACHE_RECORD macro in t_end/tp_tend.									\
	 */														\
	uint4 pid;													\
															\
	if ((pid = (X)->u.parts.latch_pid) == rundown_process_id)							\
	{														\
		if (is_exiting)												\
		{													\
			SET_LATCH_GLOBAL(X, LOCK_AVAILABLE);								\
			DEBUG_LATCH(util_out_print("Latch cleaned up", FLUSH));						\
		}													\
	} else if (0 != pid && FALSE == is_proc_alive(pid, UNIX_ONLY(0) VMS_ONLY((X)->u.parts.latch_image_count)))	\
	{														\
		(wcblocked) = TRUE;											\
		DEBUG_LATCH(util_out_print("Orphaned latch cleaned up", TRUE));						\
		COMPSWAP_UNLOCK((X), pid, (X)->u.parts.latch_image_count, LOCK_AVAILABLE, 0);				\
	}														\
}

/* The SALVAGE_UNIX_LATCH macro needs to do exactly the same thing as done by the SALVAGE_UNIX_LATCH_DBCRIT	\
 * macro except that we dont need any special set of wc_blocked to TRUE. So we pass in a dummy variable		\
 * (instead of cnl->wc_blocked) to be set to TRUE in case the latch is salvaged.				\
 */														\
#define	SALVAGE_UNIX_LATCH(X, is_exiting)									\
{														\
	boolean_t	dummy;											\
														\
	SALVAGE_UNIX_LATCH_DBCRIT(X, is_exiting, dummy);							\
}

GBLREF	uint4		process_id;	/* Used in xxx_SWAPLOCK macros .. has same value as rundown_process_id on UNIX */
GBLREF	volatile int4	crit_count;
#endif

GBLDEF gd_addr_fn_ptr	get_next_gdr_addrs;
GBLDEF cw_set_element	*cw_set_addrs;
GBLDEF sgm_info		**first_sgm_info_addrs;
GBLDEF sgm_info		**first_tp_si_by_ftok_addrs;
GBLDEF unsigned char	*cw_depth_addrs;
GBLDEF uint4		rundown_process_id;
GBLDEF uint4		rundown_image_count;
GBLDEF int4		rundown_os_page_size;
GBLDEF gd_region	**jnlpool_reg_addrs;
GBLDEF inctn_opcode_t	*inctn_opcode_addrs;
GBLDEF inctn_detail_t	*inctn_detail_addrs;
GBLDEF uint4		*dollar_tlevel_addrs;
GBLDEF uint4		*update_trans_addrs;
GBLDEF sgmnt_addrs	**cs_addrs_addrs;
GBLDEF sgmnt_addrs 	**kip_csa_addrs;
GBLDEF boolean_t	*need_kip_incr_addrs;
GBLDEF trans_num	*start_tn_addrs;

#ifdef UNIX
GBLREF	short			crash_count;
GBLREF	node_local_ptr_t	locknl;
GBLREF	inctn_opcode_t		inctn_opcode;
GBLREF	inctn_detail_t		inctn_detail;			/* holds detail to fill in to inctn jnl record */
GBLREF	boolean_t		dse_running;
GBLREF	boolean_t		certify_all_blocks;
GBLREF	gd_region		*gv_cur_region;		/* for the LOCK_HIST macro in the RELEASE_BUFF_UPDATE_LOCK macro */
GBLREF	node_local_ptr_t	locknl;			/* set explicitly before invoking RELEASE_BUFF_UPDATE_LOCK macro */
GBLREF	int4			strm_index;
GBLREF	jnl_gbls_t		jgbl;
#endif

#ifdef DEBUG
GBLREF	sgmnt_addrs		*cs_addrs;
#endif

error_def(ERR_WCBLOCKED);

typedef enum
{
	REG_COMMIT_UNSTARTED = 0,/* indicates that GT.M has not committed even one cse in this region */
	REG_COMMIT_PARTIAL,	 /* indicates that GT.M has committed at least one but not all cses for this region */
	REG_COMMIT_COMPLETE	 /* indicates that GT.M has already committed all cw-set-elements for this region */
} commit_type;

boolean_t	secshr_tp_get_cw(cw_set_element *cs, int depth, cw_set_element **cs1);

void secshr_db_clnup(enum secshr_db_state secshr_state)
{
	unsigned char		*chain_ptr;
	char			*wcblocked_ptr;
	uint4			dlr_tlevel;
	boolean_t		is_bg, jnlpool_reg, do_accounting, first_time = TRUE, is_exiting;
	boolean_t		kip_csa_usable, needkipincr;
	uint4			upd_trans; /* a copy of the global variable "update_trans" which is needed for VMS STOP/ID case */
	boolean_t		tp_update_underway = FALSE;	/* set to TRUE if TP commit was in progress or complete */
	boolean_t		non_tp_update_underway = FALSE;	/* set to TRUE if non-TP commit was in progress or complete */
	boolean_t		update_underway = FALSE;	/* set to TRUE if either TP or non-TP commit was underway */
	boolean_t		set_wc_blocked = FALSE;		/* set to TRUE if cnl->wc_blocked needs to be set */
	boolean_t		dont_reset_data_invalid;	/* set to TRUE in case cr->data_invalid was TRUE in phase2 */
	int			max_bts;
	unsigned int		lcnt;
	cache_rec_ptr_t		clru, cr, cr_alt, cr_top, start_cr, actual_cr;
	cache_que_heads_ptr_t	cache_state;
	cw_set_element		*cs, *cs_ptr, *cs_top, *first_cw_set, *nxt, *orig_cs;
	gd_addr			*gd_header;
	gd_region		*reg, *reg_top;
	jnl_buffer_ptr_t	jbp;
	off_chain		chain;
	sgm_info		*si, *firstsgminfo;
	sgmnt_addrs		*csa, *csaddrs;
	sgmnt_data_ptr_t	csd;
	node_local_ptr_t	cnl;
	sm_uc_ptr_t		blk_ptr;
	blk_hdr_ptr_t		blk_hdr_ptr;
	jnlpool_ctl_ptr_t	jpl;
	jnldata_hdr_ptr_t	jh;
	uint4			cumul_jnl_rec_len, jsize, new_write, imgcnt;
	pid_t			pid;
	sm_uc_ptr_t		bufstart;
	int4			bufindx;	/* should be the same type as "csd->bt_buckets" */
	commit_type		this_reg_commit_type;	/* indicate the type of commit of a given region in a TP transaction */
	gv_namehead		*gvt = NULL, *gvtarget;
	srch_blk_status		*t1;
	trans_num		currtn;
	int4			n;
#	ifdef VMS
	uint4			process_id;	/* needed for the UNPIN_CACHE_RECORD macro */
#	endif
	GTM_SNAPSHOT_ONLY(
		snapshot_context_ptr_t	lcl_ss_ctx;
		cache_rec_ptr_t		snapshot_cr;
	)
#	ifdef UNIX
	DCL_THREADGBL_ACCESS;

	SETUP_THREADGBL_ACCESS;
#	endif
	if (NULL == get_next_gdr_addrs)
		return;
	/*
	 * secshr_db_clnup can be called with one of the following three values for "secshr_state"
	 *
	 * 	a) NORMAL_TERMINATION   --> We are called from the exit-handler for precautionary cleanup.
	 * 				    We should NEVER be in the midst of a database update in this case.
	 * 	b) COMMIT_INCOMPLETE    --> We are called from t_commit_cleanup.
	 * 				    We should ALWAYS be in the midst of a database update in this case.
	 * 	c) ABNORMAL_TERMINATION --> This is currently VMS ONLY. This process received a STOP/ID.
	 * 				    We can POSSIBLY be in the midst of a database update in this case.
	 * 				    When UNIX boxes allow kernel extensions, this can be made to handle "kill -9" too.
	 *
	 * If we are in the midst of a database update, then depending on the stage of the commit we are in,
	 * 	we need to ROLL-BACK (undo the partial commit) or ROLL-FORWARD (complete the partial commit) the database update.
	 *
	 * t_commit_cleanup handles the ROLL-BACK and secshr_db_clnup handles the ROLL-FORWARD
	 *
	 * For all error conditions in the database commit logic, t_commit_cleanup gets control first.
	 * If then determines whether to do a ROLL-BACK or a ROLL-FORWARD.
	 * If a ROLL-BACK needs to be done, then t_commit_cleanup handles it all by itself and we will not come here.
	 * If a ROLL-FORWARD needs to be done, then t_commit_cleanup invokes secshr_db_clnup.
	 * 	In this case, secshr_db_clnup will be called with a "secshr_state" value of "COMMIT_INCOMPLETE".
	 *
	 * In case of a STOP/ID in VMS, secshr_db_clnup is directly invoked with a "secshr_state" value of "ABNORMAL_TERMINATION".
	 * Irrespective of whether we are in the midst of a database commit or not, t_commit_cleanup does not get control.
	 * Since the process can POSSIBLY be in the midst of a database update while it was STOP/IDed,
	 * 	the logic for determining whether it is a ROLL-BACK or a ROLL-FORWARD needs to also be in secshr_db_clnup.
	 * If it is determined that a ROLL-FORWARD needs to be done, secshr_db_clnup takes care of it by itself.
	 * But if a ROLL-BACK needs to be done, then secshr_db_clnup DOES NOT invoke t_commit_cleanup.
	 * Instead it sets cnl->wc_blocked to TRUE thereby ensuring the next process that gets CRIT does a cache recovery
	 * 	which will take care of doing more than the ROLL-BACK that t_commit_cleanup would have otherwise done.
	 *
	 * The logic for determining if it is a ROLL-BACK or ROLL-FORWARD is explained below.
	 * The commit logic flow in tp_tend and t_end can be captured as follows. Note that in t_end there is only one region.
	 *
	 *  1) Get crit on all regions
	 *  2) Get crit on jnlpool
	 *  3) jnlpool_ctl->early_write_addr += delta;
	 *       For each participating region being UPDATED
	 *       {
	 *  4)     csd->trans_hist.early_tn++;
	 *         Write journal records
	 *  5)     csa->hdr->reg_seqno = jnlpool_ctl->jnl_seqno + 1;
	 *       }
	 *       For each participating region being UPDATED
	 *       {
	 *  6)	    csa->t_commit_crit = T_COMMIT_CRIT_PHASE1;
	 *             For every cw-set-element of this region
	 *             {
	 *  6a)          Commit this particular block PHASE1 (inside crit).
	 *             }
	 *  7)       csa->t_commit_crit = T_COMMIT_CRIT_PHASE2;
	 *  8)     csd->trans_hist.curr_tn++;
	 *       }
	 *  9) jnlpool_ctl->write_addr = jnlpool_ctl->early_write_addr;
	 * 10) jnlpool_ctl->jnl_seqno++;
	 * 11) Release crit on all db regions
	 * 12) Release crit on jnlpool
	 *       For each participating region being UPDATED
	 *       {
	 *             For every cw-set-element of this region
	 *             {
	 * 13)           Commit this particular block PHASE2 (outside crit).
	 * 14)           cs->mode = gds_t_committed;
	 *             }
	 * 15)       csa->t_commit_crit = FALSE;
	 *       }
	 *
	 * If a TP transaction has proceeded to step (6) for at least one region, then "tp_update_underway" is set to TRUE
	 * and the transaction cannot be rolled back but has to be committed. Otherwise the transaction is rolled back.
	 *
	 * If a non-TP transaction has proceeded to step (6), then "non_tp_update_underway" is set to TRUE
	 * and the transaction cannot be rolled back but has to be committed. Otherwise the transaction is rolled back.
	 */
	UNIX_ONLY(assert(rundown_process_id == process_id);)
	VMS_ONLY(assert(rundown_process_id);)
	VMS_ONLY(process_id = rundown_process_id;)	/* used by the UNPIN_CACHE_RECORD macro */
	is_exiting = (ABNORMAL_TERMINATION == secshr_state) || (NORMAL_TERMINATION == secshr_state);
	if (GTM_PROBE(SIZEOF(*dollar_tlevel_addrs), dollar_tlevel_addrs, READ))
		dlr_tlevel = *dollar_tlevel_addrs;
	else
	{
		assert(FALSE);
		dlr_tlevel = FALSE;
	}
	if (dlr_tlevel && GTM_PROBE(SIZEOF(*first_tp_si_by_ftok_addrs), first_tp_si_by_ftok_addrs, READ))
	{	/* Determine update_underway for TP transaction. A similar check is done in t_commit_cleanup as well.
		 * Regions are committed in the ftok order using "first_tp_si_by_ftok". Also crit is released on each region
		 * as the commit completes. Take that into account while determining if update is underway.
		 */
		for (si = *first_tp_si_by_ftok_addrs; NULL != si; si = si->next_tp_si_by_ftok)
		{
			if (GTM_PROBE(SIZEOF(sgm_info), si, READ))
			{
				assert(GTM_PROBE(SIZEOF(cw_set_element), si->first_cw_set, READ) || (NULL == si->first_cw_set));
				if (UPDTRNS_TCOMMIT_STARTED_MASK & si->update_trans)
				{	/* Two possibilities.
					 *	(a) case of duplicate set not creating any cw-sets but updating db curr_tn++.
					 *	(b) Have completed commit for this region and have released crit on this region.
					 *		(in a potentially multi-region TP transaction).
					 * In either case, update is underway and the transaction cannot be rolled back.
					 */
					tp_update_underway = TRUE;
					update_underway = TRUE;
					break;
				}
				if (GTM_PROBE(SIZEOF(cw_set_element), si->first_cw_set, READ))
				{	/* Note that SECSHR_PROBE_REGION does a "continue" if any probes fail. */
					csa = si->tp_csa;
					if (!GTM_PROBE(SIZEOF(sgmnt_addrs), csa, READ))
						continue;
					if (T_UPDATE_UNDERWAY(csa))
					{
						tp_update_underway = TRUE;
						update_underway = TRUE;
						break;
					}
				}
			} else
			{
				assert(FALSE);
				break;
			}
		}
	}
	if (!dlr_tlevel)
	{	/* determine update_underway for non-TP transaction */
		upd_trans = FALSE;
		if (GTM_PROBE(SIZEOF(*update_trans_addrs), update_trans_addrs, READ))
			upd_trans = *update_trans_addrs;
		csaddrs = NULL;
		if (GTM_PROBE(SIZEOF(*cs_addrs_addrs), cs_addrs_addrs, READ))
			csaddrs = *cs_addrs_addrs;
		if (GTM_PROBE(SIZEOF(sgmnt_addrs), csaddrs, READ))
		{
			if (csaddrs->now_crit && (UPDTRNS_TCOMMIT_STARTED_MASK & upd_trans) || T_UPDATE_UNDERWAY(csaddrs))
			{
				non_tp_update_underway = TRUE;	/* non-tp update was underway */
				update_underway = TRUE;
			}
		}
	}
	/* Assert that if we had been called from t_commit_cleanup, we independently concluded that update is underway
	 * (as otherwise t_commit_cleanup would not have called us)
	 */
	assert((COMMIT_INCOMPLETE != secshr_state) || update_underway);
	for (gd_header = (*get_next_gdr_addrs)(NULL);  NULL != gd_header;  gd_header = (*get_next_gdr_addrs)(gd_header))
	{
		if (!GTM_PROBE(SIZEOF(gd_addr), gd_header, READ))
			break;	/* if gd_header is accessible */
		for (reg = gd_header->regions, reg_top = reg + gd_header->n_regions;  reg < reg_top;  reg++)
		{
			SECSHR_PROBE_REGION(reg);	/* SECSHR_PROBE_REGION sets csa */
			csd = csa->hdr;
			if (!GTM_PROBE(SIZEOF(sgmnt_data), csd, WRITE))
			{
				assert(FALSE);
				continue; /* would be nice to notify the world of a problem but where and how? */
			}
			cnl = csa->nl;
			if (!GTM_PROBE(NODE_LOCAL_SIZE_DBS, cnl, WRITE))
			{
				assert(FALSE);
				continue; /* would be nice to notify the world of a problem but where and how? */
			}
			is_bg = (csd->acc_meth == dba_bg);
			do_accounting = FALSE;	/* used by SECSHR_ACCOUNTING macro */
			/* do SECSHR_ACCOUNTING only if holding crit (to avoid another process' normal termination call
			 * to secshr_db_clnup from overwriting whatever important information we wrote. if we are in
			 * crit, for the next process to overwrite us it needs to get crit which in turn will invoke
			 * wcs_recover which in turn will send whatever we wrote to the operator log).
			 * also cannot update csd if MM and read-only. take care of that too. */
			if (csa->now_crit && (csa->read_write || is_bg))
			{	/* start accounting */
				cnl->secshr_ops_index = 0;
				do_accounting = TRUE;	/* used by SECSHR_ACCOUNTING macro */
			}
			SECSHR_ACCOUNTING(4);	/* 4 is the number of arguments following including self */
			SECSHR_ACCOUNTING(__LINE__);
			SECSHR_ACCOUNTING(rundown_process_id);
			SECSHR_ACCOUNTING(secshr_state);
			if (csa->ti != &csd->trans_hist)
			{
				SECSHR_ACCOUNTING(4);
				SECSHR_ACCOUNTING(__LINE__);
				SECSHR_ACCOUNTING((INTPTR_T)csa->ti);
				SECSHR_ACCOUNTING((INTPTR_T)&csd->trans_hist);
				csa->ti = &csd->trans_hist;	/* better to correct and proceed than to stop */
			}
			SECSHR_ACCOUNTING(3);	/* 3 is the number of arguments following including self */
			SECSHR_ACCOUNTING(__LINE__);
			SECSHR_ACCOUNTING(csd->trans_hist.curr_tn);
			if (is_exiting)
			{	/* If we hold any latches in the node_local area, release them. Note we do not check
				   db_latch here because it is never used by the compare and swap logic but rather
				   the aswp logic. Since it is only used for the 3 state cache record lock and
				   separate recovery exists for it, we do not do anything with it here.
				*/
				SALVAGE_UNIX_LATCH(&cnl->wc_var_lock, is_exiting);
				if (ABNORMAL_TERMINATION == secshr_state)
				{
					if (csa->timer)
					{
						if (-1 < cnl->wcs_timers) /* private flag is optimistic: dont overdo */
							CAREFUL_DECR_CNT(&cnl->wcs_timers, &cnl->wc_var_lock);
						csa->timer = FALSE;
					}
					if (csa->read_write && csa->ref_cnt)
					{
						assert(0 < cnl->ref_cnt);
						csa->ref_cnt--;
						assert(!csa->ref_cnt);
						CAREFUL_DECR_CNT(&cnl->ref_cnt, &cnl->wc_var_lock);
					}
				}
				if ((csa->in_wtstart) && (0 < cnl->in_wtstart))
				{
					CAREFUL_DECR_CNT(&cnl->in_wtstart, &cnl->wc_var_lock);
					assert(0 < cnl->intent_wtstart);
					if (0 < cnl->intent_wtstart)
						CAREFUL_DECR_CNT(&cnl->intent_wtstart, &cnl->wc_var_lock);
				}
				csa->in_wtstart = FALSE;	/* Let wcs_wtstart run for exit processing */
				if (cnl->wcsflu_pid == rundown_process_id)
					cnl->wcsflu_pid = 0;
			}
			set_wc_blocked = FALSE;
			if (is_bg)
			{
				if ((0 == cnl->sec_size) || !GTM_PROBE(cnl->sec_size VMS_ONLY(* OS_PAGELET_SIZE), cnl, WRITE))
				{
					SECSHR_ACCOUNTING(3);
					SECSHR_ACCOUNTING(__LINE__);
					SECSHR_ACCOUNTING(cnl->sec_size VMS_ONLY(* OS_PAGELET_SIZE));
					assert(FALSE);
					continue;
				}
				cache_state = csa->acc_meth.bg.cache_state;
				if (!GTM_PROBE(SIZEOF(cache_que_heads), cache_state, WRITE))
				{
					SECSHR_ACCOUNTING(3);
					SECSHR_ACCOUNTING(__LINE__);
					SECSHR_ACCOUNTING((INTPTR_T)cache_state);
					assert(FALSE);
					continue;
				}
				SALVAGE_UNIX_LATCH(&cache_state->cacheq_active.latch, is_exiting);
				start_cr = cache_state->cache_array + csd->bt_buckets;
				max_bts = csd->n_bts;
				if (!GTM_PROBE((uint4)(max_bts * SIZEOF(cache_rec)), start_cr, WRITE))
				{
					SECSHR_ACCOUNTING(3);
					SECSHR_ACCOUNTING(__LINE__);
					SECSHR_ACCOUNTING((INTPTR_T)start_cr);
					assert(FALSE);
					continue;
				}
				cr_top = start_cr + max_bts;
				if (is_exiting)
				{
					for (cr = start_cr;  cr < cr_top;  cr++)
					{	/* walk the cache looking for incomplete writes and reads issued by self */
						VMS_ONLY(
							if ((0 == cr->iosb.cond) && (cr->epid == rundown_process_id))
						        {
								cr->shmpool_blk_off = 0;	/* Cut link to reformat blk */
								cr->wip_stopped = TRUE;
							}
						)
						SALVAGE_UNIX_LATCH(&cr->rip_latch, is_exiting);
						if ((cr->r_epid == rundown_process_id) && (0 == cr->dirty) && (0 == cr->in_cw_set))
						{	/* increment cycle for blk number changes (for tp_hist) */
							cr->cycle++;
							cr->blk = CR_BLKEMPTY;
							/* ensure no bt points to this cr for empty blk */
							assert(0 == cr->bt_index);
							/* don't mess with ownership the I/O may not yet be cancelled;
							 * ownership will be cleared by whoever gets stuck waiting
							 * for the buffer */
						}
					}
				}
			}
			first_cw_set = cs = NULL;
			/* If tp_update_underway has been determined to be TRUE, then we are guaranteed we have a well formed
			 * ftok ordered linked list ("first_tp_si_by_ftok") so we can safely use this.
			 */
			if (tp_update_underway)
			{	/* this is constructed to deal with the issue of reg != si->gv_cur_region
				 * due to the possibility of multiple global directories pointing to regions
				 * that resolve to the same physical file; was_open prevents processing the segment
				 * more than once, so this code matches on the file rather than the region to make sure
				 * that it gets processed at least once */
				for (si = *first_tp_si_by_ftok_addrs; NULL != si; si = si->next_tp_si_by_ftok)
				{
					if (!GTM_PROBE(SIZEOF(sgm_info), si, READ))
					{
						SECSHR_ACCOUNTING(3);
						SECSHR_ACCOUNTING(__LINE__);
						SECSHR_ACCOUNTING((INTPTR_T)si);
						assert(FALSE);
						break;
					} else if (!GTM_PROBE(SIZEOF(gd_region), si->gv_cur_region, READ))
					{
						SECSHR_ACCOUNTING(3);
						SECSHR_ACCOUNTING(__LINE__);
						SECSHR_ACCOUNTING((INTPTR_T)si->gv_cur_region);
						assert(FALSE);
						continue;
					} else if (!GTM_PROBE(SIZEOF(gd_segment), si->gv_cur_region->dyn.addr, READ))
					{
						SECSHR_ACCOUNTING(3);
						SECSHR_ACCOUNTING(__LINE__);
						SECSHR_ACCOUNTING((INTPTR_T)si->gv_cur_region->dyn.addr);
						assert(FALSE);
						continue;
					} else if (si->gv_cur_region->dyn.addr->file_cntl == reg->dyn.addr->file_cntl)
					{
						cs = si->first_cw_set;
						if (cs && GTM_PROBE(SIZEOF(cw_set_element), cs, READ))
						{
							while (cs->high_tlevel)
							{
								if (GTM_PROBE(SIZEOF(cw_set_element),
											cs->high_tlevel, READ))
									cs = cs->high_tlevel;
								else
								{
									SECSHR_ACCOUNTING(3);
									SECSHR_ACCOUNTING(__LINE__);
									SECSHR_ACCOUNTING((INTPTR_T)cs->high_tlevel);
									assert(FALSE);
									first_cw_set = cs = NULL;
									break;
								}
							}
						}
						first_cw_set = cs;
						break;
					}
				}
			} else if (!dlr_tlevel && csa->t_commit_crit)
			{
				if (!GTM_PROBE(SIZEOF(unsigned char), cw_depth_addrs, READ))
				{
					SECSHR_ACCOUNTING(3);
					SECSHR_ACCOUNTING(__LINE__);
					SECSHR_ACCOUNTING((INTPTR_T)cw_depth_addrs);
					assert(FALSE);
				} else
				{	/* csa->t_commit_crit being TRUE is a clear cut indication that we have
					 * reached stage (6). ROLL-FORWARD the commit unconditionally.
					 */
					if (0 != *cw_depth_addrs)
					{
						first_cw_set = cs = cw_set_addrs;
						cs_top = cs + *cw_depth_addrs;
					}
					/* else is the case where we had a duplicate set that did not update any cw-set */
					assert(!tp_update_underway);
					assert(non_tp_update_underway);	/* should have already determined update is underway */
					if (!non_tp_update_underway)
					{	/* This is a situation where we are in non-TP and have a region that we hold
						 * crit in and are in the midst of commit but this region was not the current
						 * region when we entered secshr_db_clnup. This is an out-of-design situation
						 * that we want to catch in Unix (not VMS because it runs in kernel mode).
						 */
						UNIX_ONLY(GTMASSERT;)	/* in Unix we want to catch this situation even in pro */
					}
					non_tp_update_underway = TRUE;	/* just in case */
					update_underway = TRUE;		/* just in case */
				}
			}
			assert(!tp_update_underway || (NULL == first_cw_set) || (NULL != si));
			/* It is possible that we were in the midst of a non-TP commit for this region at or past stage (7)
			 * but first_cw_set is NULL. This is a case of duplicate SET with zero cw_set_depth. In this case,
			 * dont have any cw-set-elements to commit. The only thing remaining to do is steps (9) through (12)
			 * which are done later in this function.
			 */
			assert((FALSE == csa->t_commit_crit) || (T_COMMIT_CRIT_PHASE1 == csa->t_commit_crit)
								|| (T_COMMIT_CRIT_PHASE2 == csa->t_commit_crit));
			assert(!csa->t_commit_crit || (NULL != first_cw_set));	/* dont miss out committing a region */
			/* Skip processing region in case of a multi-region TP transaction where this region is already committed */
			assert((NULL == first_cw_set) || csa->now_crit || csa->t_commit_crit || tp_update_underway);
			if ((csa->now_crit || csa->t_commit_crit) && (NULL != first_cw_set))
			{
				SECSHR_ACCOUNTING(6);
				SECSHR_ACCOUNTING(__LINE__);
				SECSHR_ACCOUNTING(csa->now_crit);
				SECSHR_ACCOUNTING(csa->t_commit_crit);
				SECSHR_ACCOUNTING(csd->trans_hist.early_tn);
				SECSHR_ACCOUNTING(csd->trans_hist.curr_tn);
				assert(non_tp_update_underway || tp_update_underway);
				assert(!non_tp_update_underway || !tp_update_underway);
				if (is_bg)
				{
					clru = (cache_rec_ptr_t)GDS_ANY_REL2ABS(csa, cnl->cur_lru_cache_rec_off);
					lcnt = 0;
				}
				assert((T_COMMIT_CRIT_PHASE2 == csa->t_commit_crit) || csa->now_crit);
				if (T_COMMIT_CRIT_PHASE1 == csa->t_commit_crit)
				{	/* in PHASE1 so hold crit AND have noted down valid value in csa->prev_free_blks */
					assert(NORMAL_TERMINATION != secshr_state); /* for normal termination we should not
										     * have been in the midst of commit */
					assert(csa->now_crit);
					csd->trans_hist.free_blocks = csa->prev_free_blks;
				}
				SECSHR_ACCOUNTING(tp_update_underway ? 6 : 7);
				SECSHR_ACCOUNTING(__LINE__);
				SECSHR_ACCOUNTING((INTPTR_T)first_cw_set);
				SECSHR_ACCOUNTING(tp_update_underway);
				SECSHR_ACCOUNTING(non_tp_update_underway);
				if (!tp_update_underway)
				{
					SECSHR_ACCOUNTING((INTPTR_T)cs_top);
					SECSHR_ACCOUNTING(*cw_depth_addrs);
				} else
				{
					SECSHR_ACCOUNTING(si->cw_set_depth);
					this_reg_commit_type = REG_COMMIT_UNSTARTED; /* assume GT.M did no commits in this region */
					/* Note that "this_reg_commit_type" is uninitialized if "tp_update_underway" is not TRUE
					 * so should always be used within an "if (tp_update_underway)" */
				}
				/* Determine transaction number to use for the gvcst_*_build functions.
				 * If not phase2, then we have crit, so it is the same as the current database transaction number.
				 * If phase2, then we dont have crit, so use value stored in "start_tn" or "si->start_tn".
				 */
				if (T_COMMIT_CRIT_PHASE2 != csa->t_commit_crit)
					currtn = csd->trans_hist.curr_tn;
				else
				{
					if (!tp_update_underway)
					{
						if (GTM_PROBE(SIZEOF(*start_tn_addrs), start_tn_addrs, READ))
							currtn = *start_tn_addrs;
						else
						{
							assert(FALSE);
							/* dont know how this is possible, but in this case use curr db tn - 1 */
							currtn = csd->trans_hist.curr_tn - 1;
						}
					} else
						currtn = si->start_tn;
					assert(currtn < csd->trans_hist.curr_tn);
				}
				for (; (tp_update_underway  &&  NULL != cs) || (!tp_update_underway  &&  cs < cs_top);
					cs = tp_update_underway ? orig_cs->next_cw_set : (cs + 1))
				{
					dont_reset_data_invalid = FALSE;
					if (tp_update_underway)
					{
						orig_cs = cs;
						if (cs && GTM_PROBE(SIZEOF(cw_set_element), cs, READ))
						{
							while (cs->high_tlevel)
							{
								if (GTM_PROBE(SIZEOF(cw_set_element),
											cs->high_tlevel, READ))
									cs = cs->high_tlevel;
								else
								{
									SECSHR_ACCOUNTING(3);
									SECSHR_ACCOUNTING(__LINE__);
									SECSHR_ACCOUNTING((INTPTR_T)cs->high_tlevel);
									assert(FALSE);
									cs = NULL;
									break;
								}
							}
						}
					}
					if (!GTM_PROBE(SIZEOF(cw_set_element), cs, WRITE))
					{
						SECSHR_ACCOUNTING(3);
						SECSHR_ACCOUNTING(__LINE__);
						SECSHR_ACCOUNTING((INTPTR_T)cs);
						assert(FALSE);
						break;
					}
					if (gds_t_committed < cs->mode)
					{
						assert(n_gds_t_op != cs->mode);
						if (n_gds_t_op > cs->mode)
						{	/* Currently there are only three possibilities and each is in NON-TP.
							 * In each case, no need to do any block update so simulate commit.
							 */
							assert(!tp_update_underway);
							assert((gds_t_write_root == cs->mode) || (gds_t_busy2free == cs->mode)
									|| (gds_t_recycled2free == cs->mode));
							/* Check if BG AND gds_t_busy2free and if so UNPIN corresponding
							 * cache-record. This needs to be done only if we hold crit as otherwise
							 * it means we have already done it in t_end. But to do this we need to
							 * pass the global variable array "cr_array" from GTM to GTMSECSHR which
							 * is better avoided. Since anyways we have crit at this point, we are
							 * going to set wc_blocked later which is going to trigger cache recovery
							 * that is going to unpin all the cache-records so we dont take the
							 * trouble to do it here.
							 */
						} else
						{	/* Currently there are only two possibilities and both are in TP.
							 * In either case, need to simulate what tp_tend would have done which
							 * is to build a private copy right now if this is the first phase of
							 * commit (i.e. we hold crit) as this could be needed in the 2nd phase
							 * of KILL.
							 */
							assert(tp_update_underway);
							assert((kill_t_write == cs->mode) || (kill_t_create == cs->mode));
							if (csa->now_crit && (!cs->done))
							{
#								ifdef UNIX
								/* Initialize cs->new_buff to non-NULL since sec_shr_blk_build
								 * expects this. For VMS, tp_tend would have done this already.
								 */
								if (NULL == cs->new_buff)
									cs->new_buff = (unsigned char *)
											get_new_free_element(si->new_buff_list);
#								endif
								assert(NULL != cs->new_buff);
								blk_ptr = (sm_uc_ptr_t)cs->new_buff;
								/* No need to probe blk_ptr as sec_shr_blk_build does that */
								if (FALSE == sec_shr_blk_build(csa, csd, is_bg, cs, blk_ptr,
												currtn))
								{
									SECSHR_ACCOUNTING(10);
									SECSHR_ACCOUNTING(__LINE__);
									SECSHR_ACCOUNTING((INTPTR_T)cs);
									SECSHR_ACCOUNTING(cs->blk);
									SECSHR_ACCOUNTING(cs->level);
									SECSHR_ACCOUNTING(cs->done);
									SECSHR_ACCOUNTING(cs->forward_process);
									SECSHR_ACCOUNTING(cs->first_copy);
									SECSHR_ACCOUNTING((INTPTR_T)cs->upd_addr);
									SECSHR_ACCOUNTING((INTPTR_T)cs->new_buff);
									assert(FALSE);
									continue;
								} else if (cs->ins_off != 0)
								{
									if ((cs->ins_off
										> ((blk_hdr *)blk_ptr)->bsiz - SIZEOF(block_id))
										|| (cs->ins_off
										 < (SIZEOF(blk_hdr) + SIZEOF(rec_hdr))))
									{
										SECSHR_ACCOUNTING(7);
										SECSHR_ACCOUNTING(__LINE__);
										SECSHR_ACCOUNTING((INTPTR_T)cs);
										SECSHR_ACCOUNTING(cs->blk);
										SECSHR_ACCOUNTING(cs->index);
										SECSHR_ACCOUNTING(cs->ins_off);
										SECSHR_ACCOUNTING(
											((blk_hdr *)blk_ptr)->bsiz);
										assert(FALSE);
										continue;
									}
									if (cs->first_off == 0)
										cs->first_off = cs->ins_off;
									chain_ptr = blk_ptr + cs->ins_off;
									chain.flag = 1;
									/* note: currently only assert check of cs->index */
									assert(tp_update_underway || (0 <= (short)cs->index));
									assert(tp_update_underway
										|| (&first_cw_set[cs->index] < cs));
									chain.cw_index = cs->index;
									chain.next_off = cs->next_off;
									if (!(GTM_PROBE(SIZEOF(int4), chain_ptr, WRITE)))
									{
										SECSHR_ACCOUNTING(5);
										SECSHR_ACCOUNTING(__LINE__);
										SECSHR_ACCOUNTING((INTPTR_T)cs);
										SECSHR_ACCOUNTING(cs->ins_off);
										SECSHR_ACCOUNTING((INTPTR_T)chain_ptr);
										assert(FALSE);
										continue;
									}
									GET_LONGP(chain_ptr, &chain);
									cs->ins_off = cs->next_off = 0;
								}
								cs->done = TRUE;
								assert(NULL != cs->blk_target);
								/* cert_blk cannot be done in VMS as it is a heavyweight routine
								 * and cannot be pulled into GTMSECSHR. Hence do it only in Unix.
								 */
								UNIX_ONLY(assert(NULL == gvt);)
								UNIX_ONLY(CERT_BLK_IF_NEEDED(certify_all_blocks, gv_cur_region,
											cs, cs->new_buff, gvt);)
							}
						}
						cs->old_mode = (int4)cs->mode;
						assert(0 < cs->old_mode);
						cs->mode = gds_t_committed;
						continue;
					}
					if (gds_t_committed == cs->mode)
					{	/* already processed */
						assert(0 < cs->old_mode);
						if (T_COMMIT_CRIT_PHASE1 == csa->t_commit_crit)
						{
							assert(csa->now_crit);
							csd->trans_hist.free_blocks -= cs->reference_cnt;
						}
						if (tp_update_underway)
						{	/* We have seen at least one already-committed cse. Assume GT.M has
							 * committed ALL cses if this is the first one we are seeing. This
							 * will be later overridden if we see an uncommitted cse in this region.
							 * If we have already decided that the region is only partially committed,
							 * do not change that. It is possible to see uncommitted cses followed by
							 * committed cses in case of an error during phase2 because bitmaps
							 * (later cses) are committed in phase1 while the rest (early cses)
							 * are completely committed only in phase2.
							 */
							if (REG_COMMIT_UNSTARTED == this_reg_commit_type)
								this_reg_commit_type = REG_COMMIT_COMPLETE;
						}
						cr = cs->cr;
						assert(!dlr_tlevel || (gds_t_write_root != cs->old_mode));
						assert(gds_t_committed != cs->old_mode);
						if (gds_t_committed > cs->old_mode)
						{
							if (!GTM_PROBE(SIZEOF(cache_rec), cr, WRITE))
							{
								SECSHR_ACCOUNTING(4);
								SECSHR_ACCOUNTING(__LINE__);
								SECSHR_ACCOUNTING((INTPTR_T)cs);
								SECSHR_ACCOUNTING((INTPTR_T)cr);
								assert(FALSE);
							} else if (rundown_process_id == cr->in_tend)
							{	/* Not sure how this is possible */
								assert(FALSE);
							}
						} else
						{	/* For the kill_t_* case, cs->cr will be NULL as bg_update was not invoked
							 * and the cw-set-elements were memset to 0 in TP. But for gds_t_write_root
							 * and gds_t_busy2free, they are non-TP ONLY modes and cses are not
							 * initialized so cant check for NULL cr. Thankfully "n_gds_t_op" demarcates
							 * the boundaries between non-TP only and TP only modes. So use that.
							 */
							assert((n_gds_t_op > cs->old_mode) || (NULL == cr));
						}
						continue;
					}
					/* Since we are going to build blocks at this point, unconditionally set wc_blocked
					 * (after finishing commits) to trigger wcs_recover even though we might not be
					 * holding crit at this point.
					 */
					set_wc_blocked = TRUE;
					assert(NORMAL_TERMINATION != secshr_state); /* for normal termination we should not
										     * have been in the midst of commit */
					if (tp_update_underway)
					{	/* Since the current cse has not been committed, this is a partial
						 * GT.M commit in this region even if we have already seen committed cses.
						 */
						this_reg_commit_type = REG_COMMIT_PARTIAL;
					}
					if (is_bg)
					{
						if (T_COMMIT_CRIT_PHASE2 != csa->t_commit_crit)
						{	/* We are not yet in phase2 which means we hold crit on this region,
							 * so have to find out a free cache-record we can dump our updates onto.
							 */
							for ( ; lcnt++ < max_bts; )
							{	/* find any available cr */
								if (++clru >= cr_top)
									clru = start_cr;
								assert(!clru->stopped);
								if (!clru->stopped && (0 == clru->dirty)
									&& (0 == clru->in_cw_set)
									&& (!clru->in_tend)
									&& (-1 == clru->read_in_progress)
									&& GTM_PROBE(csd->blk_size,
										GDS_ANY_REL2ABS(csa, clru->buffaddr), WRITE))
									break;
							}
							if (lcnt >= max_bts)
							{
								SECSHR_ACCOUNTING(9);
								SECSHR_ACCOUNTING(__LINE__);
								SECSHR_ACCOUNTING((INTPTR_T)cs);
								SECSHR_ACCOUNTING(cs->blk);
								SECSHR_ACCOUNTING(cs->tn);
								SECSHR_ACCOUNTING(cs->level);
								SECSHR_ACCOUNTING(cs->done);
								SECSHR_ACCOUNTING(cs->forward_process);
								SECSHR_ACCOUNTING(cs->first_copy);
								assert(FALSE);
								continue;
							}
							cr = clru;
							cr->cycle++;	/* increment cycle for blk number changes (for tp_hist) */
							assert(cs->blk < csd->trans_hist.total_blks);
							cr->blk = cs->blk;
							assert(CR_BLKEMPTY != cr->blk);
							cr->jnl_addr = cs->jnl_freeaddr;
							cr->stopped = TRUE;
							/* Keep cs->cr and t1->cr uptodate to ensure clue will be accurate */
							cs->cr = cr;
							cs->cycle = cr->cycle;
							if (!IS_BITMAP_BLK(cs->blk))
							{	/* Not a bitmap block, update clue history to reflect new cr */
								assert((0 <= cs->level) && (MAX_BT_DEPTH > cs->level));
								gvtarget = cs->blk_target;
								assert((MAX_BT_DEPTH + 1)
									== (SIZEOF(gvtarget->hist.h)
										/ SIZEOF(gvtarget->hist.h[0])));
								if ((0 <= cs->level) && (MAX_BT_DEPTH > cs->level)
									&& GTM_PROBE(SIZEOF(gv_namehead), gvtarget, WRITE)
									&& (0 != gvtarget->clue.end))
								{
									t1 = &gvtarget->hist.h[cs->level];
									if (t1->blk_num == cs->blk)
									{
										t1->cr = cr;
										t1->cycle = cs->cycle;
										t1->buffaddr = (sm_uc_ptr_t)
												GDS_ANY_REL2ABS(csa, cr->buffaddr);
									}
								}
							}
						} else
						{	/* We are in PHASE2 of the commit (i.e. have completed PHASE1 for ALL cses)
							 * We have already picked out a cr for the commit. Use that.
							 */
							cr = cs->cr;
							if (!GTM_PROBE(SIZEOF(cache_rec), cr, WRITE))
							{
								SECSHR_ACCOUNTING(4);
								SECSHR_ACCOUNTING(__LINE__);
								SECSHR_ACCOUNTING((INTPTR_T)cs);
								SECSHR_ACCOUNTING((INTPTR_T)cr);
								assert(FALSE);
								continue;
							}
							if (rundown_process_id != cr->in_tend)
							{	/* phase2 commit is already complete for this cse but we got
								 * interrupted before setting cs->mode to gds_t_committed.
								 * Possible that this cache-record is not placed in the active
								 * queue properly. Any case set_wc_blocked is already set so that
								 * should take care of invoking wcs_recover to fix the queues.
								 */
								assert(rundown_process_id != cr->in_cw_set);
								assert(rundown_process_id != cr->data_invalid);
								continue;
							}
							assert(rundown_process_id == cr->in_cw_set);
							assert(cr->blk == cs->cr->blk);
							if (cr->data_invalid)
							{	/* Buffer is already in middle of update. Since blk builds are
								 * not redoable, db is in danger whether or not we redo the build.
								 * Since, skipping the build is guaranteed to give us integrity
								 * errors, we redo the build hoping it will have at least a 50%
								 * chance of resulting in a clean block. Make sure data_invalid
								 * flag is set until the next cache-recovery (wcs_recover will
								 * send a DBDANGER syslog message for this block to alert of
								 * potential database damage) by setting dont_reset_data_invalid.
								 */
								SECSHR_ACCOUNTING(6);
								SECSHR_ACCOUNTING(__LINE__);
								SECSHR_ACCOUNTING((INTPTR_T)cs);
								SECSHR_ACCOUNTING((INTPTR_T)cr);
								SECSHR_ACCOUNTING(cr->blk);
								SECSHR_ACCOUNTING(cr->data_invalid);
								assert(FALSE);
								dont_reset_data_invalid = TRUE;
							}
						}
						/* Check if online backup is in progress and if there is a before-image to write.
						 * If so need to store link to it so wcs_recover can back it up later. Cannot
						 * rely on precomputed value csa->backup_in_prog since it is not initialized
						 * if (cw_depth == 0) (see t_end.c). Hence using cnl->nbb explicitly in check.
						 * However, for snapshots we can rely on csa as it is computed under
						 * if (update_trans). Use cs->blk_prior_state's free status to ensure that FREE
						 * blocks are not back'ed up either by secshr_db_clnup or wcs_recover.
						 */
						if ((SNAPSHOTS_IN_PROG(csa) || (BACKUP_NOT_IN_PROGRESS != cnl->nbb))
							&& (NULL != cs->old_block))
						{
							DEBUG_ONLY(GTM_SNAPSHOT_ONLY(snapshot_cr = NULL;)) /* Will be set below */
							if (T_COMMIT_CRIT_PHASE2 != csa->t_commit_crit)
							{	/* Set "cr->twin" to point to "cs->old_block". This is not normal
								 * usage since "twin" usually points to a cache-record. But this
								 * is a special case where we want to record the before-image
								 * somewhere for wcs_recover to see and we are not allowed division
								 * operations in secshr_db_clnup (which is required to find out the
								 * corresponding cache-record). Hence we store the relative offset
								 * of "cs->old_block". This is a special case where "cr->twin" can
								 * be non-zero even in Unix. wcs_recover will recognize this special
								 * usage of "twin" (since cr->stopped is non-zero as well) and fix
								 * it. Note that in VMS, it is possible to have two other crs for
								 * the same block cr1, cr2 which are each twinned so we could end
								 * up with the following twin configuration.
								 *	cr1 <---> cr2 <--- cr
								 * Note cr->twin = cr2 is a one way link and stores "cs->old_block",
								 * while "cr1->twin" and "cr2->twin" store each other's cacherecord
								 * pointers.
								 */
#								ifdef UNIX
								bufstart = (sm_uc_ptr_t)GDS_ANY_REL2ABS(csa, start_cr->buffaddr);
								bufindx = (int4)(cs->old_block - bufstart) / csd->blk_size;
								assert(0 <= bufindx);
								assert(bufindx < csd->n_bts);
								cr_alt = &start_cr[bufindx];
								assert(cr_alt != cr);
								assert(cs->blk == cr_alt->blk);
								assert(rundown_process_id == cr_alt->in_cw_set);
								snapshot_cr = cr_alt;
#								endif
								/* wcs_recover need not copy before images of FREE blocks
								 * to the backup buffer */
								if (!WAS_FREE(cs))
									cr->twin = GDS_ANY_ABS2REL(csa, cs->old_block);
							} else
							{	/* We have to finish phase2 update.
								 * If Unix, we backup the block right here instead of waiting for
								 * wcs_recover to do it. If VMS, we dont need to do anything as
								 * the block has already been backed up in phase1. See end of
								 * bg_update_phase1 for comment on why.
								 */
#								ifdef UNIX
								/* The following check is similar to the one in BG_BACKUP_BLOCK
								 * and the one in wcs_recover (where backup_block is invoked)
								 */
								blk_hdr_ptr = (blk_hdr_ptr_t)cs->old_block;
								assert(GDS_ANY_REL2ABS(csa, cr->buffaddr)
										== (sm_uc_ptr_t)blk_hdr_ptr);
								if (!WAS_FREE(cs) && (cr->blk >= cnl->nbb)
									&& (0 == csa->shmpool_buffer->failed)
									&& (blk_hdr_ptr->tn < csa->shmpool_buffer->backup_tn)
									&& (blk_hdr_ptr->tn >= csa->shmpool_buffer->inc_backup_tn))
								{
									backup_block(csa, cr->blk, cr, NULL);
									/* No need for us to flush the backup buffer.
									 * MUPIP BACKUP will anyways flush it at the end.
									 */
								}
								snapshot_cr = cr;
#								endif
							}
#							ifdef GTM_SNAPSHOT
							if (SNAPSHOTS_IN_PROG(csa))
							{
								lcl_ss_ctx = SS_CTX_CAST(csa->ss_ctx);
								assert(NULL != snapshot_cr);
								assert((snapshot_cr == cr) || (snapshot_cr == cr_alt));
								WRITE_SNAPSHOT_BLOCK(csa, snapshot_cr, NULL, snapshot_cr->blk,
											lcl_ss_ctx);
							}
#							endif
						}
						if (T_COMMIT_CRIT_PHASE2 != csa->t_commit_crit)
						{	/* Adjust blks_to_upgrd counter if not already done in phase1. The value of
							 * cs->old_mode if negative implies phase1 is complete on this cse so we
							 * dont need to do this adjustment again. If not we do the adjustment.
							 */
							assert((0 <= cs->old_mode) || (cs->old_mode == -cs->mode));
							if (0 <= cs->old_mode)
							{	/* the following code is very similar to that in bg_update */
								if (gds_t_acquired == cs->mode)
								{
									if (GDSV4 == csd->desired_db_format)
									{
										INCR_BLKS_TO_UPGRD(csa, csd, 1);
									}
								} else
								{
#									ifdef DEBUG
									/* secshr_db_clnup relies on the fact that cs->ondsk_blkver
									 * accurately reflects the on-disk block version of the
									 * block and therefore can be used to set cr->ondsk_blkver.
									 * Confirm this by checking that if a cr exists for this
									 * block, then that cr's ondsk_blkver matches with the cs.
									 * db_csh_get uses the global variable cs_addrs to determine
									 * the region. So make it uptodate temporarily holding its
									 * value in the local variable csaddrs.
									 */
									csaddrs = cs_addrs;	/* save cs_addrs in local */
									cs_addrs = csa;		/* set cs_addrs for db_csh_get */
									actual_cr = db_csh_get(cs->blk);
									cs_addrs = csaddrs;	/* restore cs_addrs */
									/* actual_cr can be NULL if the block is NOT in the cache.
									 * It can be CR_NOTVALID if the cache record originally
									 * containing this block got reused for a different block
									 * (i.e. cr->stopped = 1) as part of secshr_db_clnup.
									 */
									assert((NULL == actual_cr)
										|| ((cache_rec_ptr_t)CR_NOTVALID == actual_cr)
										|| (cs->ondsk_blkver == actual_cr->ondsk_blkver));
#									endif
									cr->ondsk_blkver = cs->ondsk_blkver;
									if (cr->ondsk_blkver != csd->desired_db_format)
									{
										if (GDSV4 == csd->desired_db_format)
										{
											if (gds_t_write_recycled != cs->mode)
												INCR_BLKS_TO_UPGRD(csa, csd, 1);
										} else
										{
											if (gds_t_write_recycled != cs->mode)
												DECR_BLKS_TO_UPGRD(csa, csd, 1);
										}
									}
								}
							}
						}
						/* Before resetting cr->ondsk_blkver, ensure db_format in file header did not
						 * change in between phase1 (inside of crit) and phase2 (outside of crit).
						 * This is needed to ensure the correctness of the blks_to_upgrd counter.
						 */
						assert(currtn > csd->desired_db_format_tn);
						cr->ondsk_blkver = csd->desired_db_format;
						/* else we are in phase2 and all blks_to_upgrd manipulation is already done */
						blk_ptr = (sm_uc_ptr_t)GDS_ANY_REL2ABS(csa, cr->buffaddr);
					} else
					{	/* access method is MM */
						blk_ptr = (sm_uc_ptr_t)csa->acc_meth.mm.base_addr + (off_t)csd->blk_size * cs->blk;
						if (!GTM_PROBE(csd->blk_size, blk_ptr, WRITE))
						{
							SECSHR_ACCOUNTING(7);
							SECSHR_ACCOUNTING(__LINE__);
							SECSHR_ACCOUNTING((INTPTR_T)cs);
							SECSHR_ACCOUNTING(cs->blk);
							SECSHR_ACCOUNTING((INTPTR_T)blk_ptr);
							SECSHR_ACCOUNTING(csd->blk_size);
							SECSHR_ACCOUNTING((INTPTR_T)csa->acc_meth.mm.base_addr);
							assert(FALSE);
							continue;
						}
					}
					/* The following block of code rolls forward steps (6a) and/or (13) of the commit */
					if (cs->mode == gds_t_writemap)
					{
						if (!GTM_PROBE(csd->blk_size, cs->old_block, READ))
						{
							SECSHR_ACCOUNTING(11);
							SECSHR_ACCOUNTING(__LINE__);
							SECSHR_ACCOUNTING((INTPTR_T)cs);
							SECSHR_ACCOUNTING(cs->blk);
							SECSHR_ACCOUNTING(cs->tn);
							SECSHR_ACCOUNTING(cs->level);
							SECSHR_ACCOUNTING(cs->done);
							SECSHR_ACCOUNTING(cs->forward_process);
							SECSHR_ACCOUNTING(cs->first_copy);
							SECSHR_ACCOUNTING((INTPTR_T)cs->old_block);
							SECSHR_ACCOUNTING(csd->blk_size);
							assert(FALSE);
							continue;
						}
						memmove(blk_ptr, cs->old_block, csd->blk_size);
						if (FALSE == sec_shr_map_build(csa, (uint4*)cs->upd_addr, blk_ptr, cs,
							currtn, BM_SIZE(csd->bplmap)))
						{
							SECSHR_ACCOUNTING(11);
							SECSHR_ACCOUNTING(__LINE__);
							SECSHR_ACCOUNTING((INTPTR_T)cs);
							SECSHR_ACCOUNTING(cs->blk);
							SECSHR_ACCOUNTING(cs->tn);
							SECSHR_ACCOUNTING(cs->level);
							SECSHR_ACCOUNTING(cs->done);
							SECSHR_ACCOUNTING(cs->forward_process);
							SECSHR_ACCOUNTING(cs->first_copy);
							SECSHR_ACCOUNTING((INTPTR_T)cs->upd_addr);
							SECSHR_ACCOUNTING((INTPTR_T)blk_ptr);
							assert(FALSE);
							continue;
						}
					} else
					{
						if (!tp_update_underway)
						{
							if (FALSE == sec_shr_blk_build(csa, csd, is_bg, cs, blk_ptr, currtn))
							{
								SECSHR_ACCOUNTING(10);
								SECSHR_ACCOUNTING(__LINE__);
								SECSHR_ACCOUNTING((INTPTR_T)cs);
								SECSHR_ACCOUNTING(cs->blk);
								SECSHR_ACCOUNTING(cs->level);
								SECSHR_ACCOUNTING(cs->done);
								SECSHR_ACCOUNTING(cs->forward_process);
								SECSHR_ACCOUNTING(cs->first_copy);
								SECSHR_ACCOUNTING((INTPTR_T)cs->upd_addr);
								SECSHR_ACCOUNTING((INTPTR_T)blk_ptr);
								assert(FALSE);
								continue;
							} else if (cs->ins_off)
							{
								if ((cs->ins_off >
									((blk_hdr *)blk_ptr)->bsiz - SIZEOF(block_id))
									|| (cs->ins_off < (SIZEOF(blk_hdr)
										+ SIZEOF(rec_hdr)))
									|| (0 > (short)cs->index)
									|| ((cs - cw_set_addrs) <= cs->index))
								{
									SECSHR_ACCOUNTING(7);
									SECSHR_ACCOUNTING(__LINE__);
									SECSHR_ACCOUNTING((INTPTR_T)cs);
									SECSHR_ACCOUNTING(cs->blk);
									SECSHR_ACCOUNTING(cs->index);
									SECSHR_ACCOUNTING(cs->ins_off);
									SECSHR_ACCOUNTING(((blk_hdr *)blk_ptr)->bsiz);
									assert(FALSE);
									continue;
								}
								PUT_LONG((blk_ptr + cs->ins_off),
								 ((cw_set_element *)(cw_set_addrs + cs->index))->blk);
								if (((nxt = cs + 1) < cs_top)
									&& (gds_t_write_root == nxt->mode))
								{
									if ((nxt->ins_off >
									     ((blk_hdr *)blk_ptr)->bsiz - SIZEOF(block_id))
										|| (nxt->ins_off < (SIZEOF(blk_hdr)
											 + SIZEOF(rec_hdr)))
										|| (0 > (short)nxt->index)
										|| ((cs - cw_set_addrs) <= nxt->index))
									{
										SECSHR_ACCOUNTING(7);
										SECSHR_ACCOUNTING(__LINE__);
										SECSHR_ACCOUNTING((INTPTR_T)nxt);
										SECSHR_ACCOUNTING(cs->blk);
										SECSHR_ACCOUNTING(nxt->index);
										SECSHR_ACCOUNTING(nxt->ins_off);
										SECSHR_ACCOUNTING(
											((blk_hdr *)blk_ptr)->bsiz);
										assert(FALSE);
										continue;
									}
									PUT_LONG((blk_ptr + nxt->ins_off),
										 ((cw_set_element *)
										 (cw_set_addrs + nxt->index))->blk);
								}
							}
						} else
						{	/* TP */
							if (cs->done == 0)
							{
								if (FALSE == sec_shr_blk_build(csa, csd, is_bg, cs, blk_ptr,
												currtn))
								{
									SECSHR_ACCOUNTING(10);
									SECSHR_ACCOUNTING(__LINE__);
									SECSHR_ACCOUNTING((INTPTR_T)cs);
									SECSHR_ACCOUNTING(cs->blk);
									SECSHR_ACCOUNTING(cs->level);
									SECSHR_ACCOUNTING(cs->done);
									SECSHR_ACCOUNTING(cs->forward_process);
									SECSHR_ACCOUNTING(cs->first_copy);
									SECSHR_ACCOUNTING((INTPTR_T)cs->upd_addr);
									SECSHR_ACCOUNTING((INTPTR_T)blk_ptr);
									assert(FALSE);
									continue;
								}
								if (cs->ins_off != 0)
								{
									if ((cs->ins_off
										> ((blk_hdr *)blk_ptr)->bsiz
											- SIZEOF(block_id))
										|| (cs->ins_off
										 < (SIZEOF(blk_hdr) + SIZEOF(rec_hdr))))
									{
										SECSHR_ACCOUNTING(7);
										SECSHR_ACCOUNTING(__LINE__);
										SECSHR_ACCOUNTING((INTPTR_T)cs);
										SECSHR_ACCOUNTING(cs->blk);
										SECSHR_ACCOUNTING(cs->index);
										SECSHR_ACCOUNTING(cs->ins_off);
										SECSHR_ACCOUNTING(
											((blk_hdr *)blk_ptr)->bsiz);
										assert(FALSE);
										continue;
									}
									if (cs->first_off == 0)
										cs->first_off = cs->ins_off;
									chain_ptr = blk_ptr + cs->ins_off;
									chain.flag = 1;
									chain.cw_index = cs->index;
									/* note: currently no verification of cs->index */
									chain.next_off = cs->next_off;
									GET_LONGP(chain_ptr, &chain);
									cs->ins_off = cs->next_off = 0;
								}
							} else
							{
								memmove(blk_ptr, cs->new_buff,
									((blk_hdr *)cs->new_buff)->bsiz);
								((blk_hdr *)blk_ptr)->tn = currtn;
							}
							if (cs->first_off)
							{
								for (chain_ptr = blk_ptr + cs->first_off; ;
									chain_ptr += chain.next_off)
								{
									GET_LONGP(&chain, chain_ptr);
									if ((1 == chain.flag)
									   && ((chain_ptr - blk_ptr + SIZEOF(block_id))
										  <= ((blk_hdr *)blk_ptr)->bsiz)
									   && (chain.cw_index < si->cw_set_depth)
									   && (FALSE != secshr_tp_get_cw(
									      first_cw_set, chain.cw_index, &cs_ptr)))
									{
										PUT_LONG(chain_ptr, cs_ptr->blk);
										if (0 == chain.next_off)
											break;
									} else
									{
										SECSHR_ACCOUNTING(11);
										SECSHR_ACCOUNTING(__LINE__);
										SECSHR_ACCOUNTING((INTPTR_T)cs);
										SECSHR_ACCOUNTING(cs->blk);
										SECSHR_ACCOUNTING(cs->index);
										SECSHR_ACCOUNTING((INTPTR_T)blk_ptr);
										SECSHR_ACCOUNTING((INTPTR_T)chain_ptr);
										SECSHR_ACCOUNTING(chain.next_off);
										SECSHR_ACCOUNTING(chain.cw_index);
										SECSHR_ACCOUNTING(si->cw_set_depth);
										SECSHR_ACCOUNTING(
											((blk_hdr *)blk_ptr)->bsiz);
										assert(FALSE);
										break;
									}
								}
							}
						}	/* TP */
					}	/* non-map processing */
					if (0 > cs->reference_cnt)
					{	/* blocks were freed up */
						assert(non_tp_update_underway);
						UNIX_ONLY(
							assert((&inctn_opcode == inctn_opcode_addrs)
								&& (&inctn_detail == inctn_detail_addrs)
								&& ((inctn_bmp_mark_free_gtm == inctn_opcode)
									|| (inctn_bmp_mark_free_mu_reorg == inctn_opcode)
									|| (inctn_blkmarkfree == inctn_opcode)
									|| dse_running));
						)
						/* Check if we are freeing a V4 format block and if so decrement the
						 * blks_to_upgrd counter. Do not do this in case MUPIP REORG UPGRADE/DOWNGRADE
						 * is marking a recycled block as free (inctn_opcode is inctn_blkmarkfree).
						 */
						if ((NULL != inctn_opcode_addrs)
							&& (GTM_PROBE(SIZEOF(*inctn_opcode_addrs), inctn_opcode_addrs, READ))
							&& ((inctn_bmp_mark_free_gtm == *inctn_opcode_addrs)
								|| (inctn_bmp_mark_free_mu_reorg == *inctn_opcode_addrs))
							&& (NULL != inctn_detail_addrs)
							&& (GTM_PROBE(SIZEOF(*inctn_detail_addrs), inctn_detail_addrs, READ))
							&& (0 != inctn_detail_addrs->blknum_struct.blknum))
						{
							DECR_BLKS_TO_UPGRD(csa, csd, 1);
						}
					}
					assert(!cs->reference_cnt || (T_COMMIT_CRIT_PHASE2 != csa->t_commit_crit));
					if (csa->now_crit)
					{	/* Even though we know cs->reference_cnt is guaranteed to be 0 if we are in
						 * phase2 of commit (see above assert), we still do not want to be touching
						 * free_blocks in the file header outside of crit as it could potentially
						 * result in an incorrect value of the free_blocks counter. This is because
						 * in between the time we note down the current value of free_blocks on the
						 * right hand side of the below expression and assign the same value to the
						 * left side, it is possible that a concurrent process holding crit could
						 * have updated the free_blocks counter. In that case, our update would
						 * result in incorrect values. Hence dont touch this field if phase2.
						 */
						csd->trans_hist.free_blocks -= cs->reference_cnt;
					}
					cs->old_mode = (int4)cs->mode;
					assert(0 < cs->old_mode);
					cs->mode = gds_t_committed;	/* rolls forward step (14) */
					UNIX_ONLY(
						/* Do not do a cert_blk of bitmap here since it could give a DBBMMSTR error. The
						 * bitmap block build is COMPLETE only in wcs_recover so do the cert_blk there.
						 * Assert that the bitmap buffer will indeed go through cert_blk there.
						 */
						assert((cs->old_mode != gds_t_writemap) || !is_bg || cr->stopped);
						if (cs->old_mode != gds_t_writemap)
						{
							assert(NULL == gvt);
							CERT_BLK_IF_NEEDED(certify_all_blocks, reg, cs, blk_ptr, gvt);
						}
					)
					if (is_bg && (rundown_process_id == cr->in_tend))
					{	/* Reset cr->in_tend now that cr is uptodate. This way if at all wcs_recover
						 * sees cr->in_tend set, it can be sure that was leftover from an interrupted
						 * phase1 commit for which the complete commit happened in another cache-record
						 * which will have cr->stopped set so the in_tend cache-record can be discarded.
						 * Take this opportunity to reset data_invalid, in_cw_set and the write interlock
						 * as well thereby simulating exactly what bg_update_phase2 would have done.
						 * This is easily done in Unix using the INSQ*I macros. But in VMS, these macros
						 * will pull in extra routines (including wcs_sleep) into the privileged image
						 * GTMSECSHR which we want to avoid. Therefore in VMS, we decide to skip the
						 * part about re-inserting the dirty cache-record into the active queue.
						 * The VMS version of wcs_get_space.c needs to take this into account while
						 * it is waiting for a dirty cache-record (that it could not be in any queues).
						 */
						assert(T_COMMIT_CRIT_PHASE2 == csa->t_commit_crit);
						if (!dont_reset_data_invalid)
							cr->data_invalid = 0;
						if (PROBE_EVEN(cr))
						{	/* Release write interlock. The following code is very similar to that
							 * at the end of the function "bg_update_phase2".
							 */
							UNIX_ONLY(
								/* Avoid using gv_cur_region in the LOCK_HIST macro that is
								 * used by the RELEASE_BUFF_UPDATE_LOCK macro by setting locknl
								 */
								locknl = cnl;
							)
							if (!cr->tn)
							{
								cr->jnl_addr = cs->jnl_freeaddr;
								assert(LATCH_SET == WRITE_LATCH_VAL(cr));
#								ifdef UNIX
								/* cache-record was not dirty BEFORE this update.
								 * insert this in the active queue. See comment above for
								 * why this is done only in Unix and not VMS.
								 */
								n = INSQTI((que_ent_ptr_t)&cr->state_que,
										(que_head_ptr_t)&cache_state->cacheq_active);
								if (INTERLOCK_FAIL == n)
								{
									SECSHR_ACCOUNTING(7);
									SECSHR_ACCOUNTING(__LINE__);
									SECSHR_ACCOUNTING((INTPTR_T)cr);
									SECSHR_ACCOUNTING(cr->blk);
									SECSHR_ACCOUNTING(n);
									SECSHR_ACCOUNTING(cache_state->cacheq_active.fl);
									SECSHR_ACCOUNTING(cache_state->cacheq_active.bl);
									assert(FALSE);
								}
								ADD_ENT_TO_ACTIVE_QUE_CNT(&cnl->wcs_active_lvl, &cnl->wc_var_lock);
#								endif
							}
							RELEASE_BUFF_UPDATE_LOCK(cr, n, &cnl->db_latch);
							/* "n" holds the pre-release value in Unix and post-release value in VMS,
							 * so check that we did hold the lock before releasing it above */
							UNIX_ONLY(assert(LATCH_CONFLICT >= n);)
							UNIX_ONLY(assert(LATCH_CLEAR < n);)
							VMS_ONLY(assert(LATCH_SET >= n);)
							VMS_ONLY(assert(LATCH_CLEAR <= n);)
							if (WRITER_BLOCKED_BY_PROC(n))
							{
								VMS_ONLY(
									assert(LATCH_SET == WRITE_LATCH_VAL(cr));
									RELEASE_BUFF_UPDATE_LOCK(cr, n, &cnl->db_latch);
									assert(LATCH_CLEAR == n);
									assert(0 != cr->epid);
									assert(WRT_STRT_PNDNG == cr->iosb.cond);
									cr->epid = 0;
									cr->iosb.cond = 0;
									cr->wip_stopped = FALSE;
								)
#								ifdef UNIX
								n = INSQHI((que_ent_ptr_t)&cr->state_que,
										(que_head_ptr_t)&cache_state->cacheq_active);
								if (INTERLOCK_FAIL == n)
								{
									SECSHR_ACCOUNTING(7);
									SECSHR_ACCOUNTING(__LINE__);
									SECSHR_ACCOUNTING((INTPTR_T)cr);
									SECSHR_ACCOUNTING(cr->blk);
									SECSHR_ACCOUNTING(n);
									SECSHR_ACCOUNTING(cache_state->cacheq_active.fl);
									SECSHR_ACCOUNTING(cache_state->cacheq_active.bl);
									assert(FALSE);
								}
#								endif
							}
						}
						assert(process_id == cr->in_cw_set);
						UNPIN_CACHE_RECORD(cr);
						assert(!cr->in_cw_set);
						SECSHR_SHM_WRITE_MEMORY_BARRIER;
						cr->in_tend = 0;
					}
				}	/* for all cw_set entries */
				/* Check if kill_in_prog flag in file header has to be incremented. */
				if (tp_update_underway)
				{	/* TP : Do this only if GT.M has not already completed the commit on this region. */
					assert((REG_COMMIT_COMPLETE == this_reg_commit_type)
						|| (REG_COMMIT_PARTIAL == this_reg_commit_type)
						|| (REG_COMMIT_UNSTARTED == this_reg_commit_type));
					/* We have already checked that "si" is READABLE. Check that it is WRITABLE since
					 * we might need to set "si->kip_csa" in the CAREFUL_INCR_KIP macro.
					 */
					if (GTM_PROBE(SIZEOF(sgm_info), si, WRITE))
					{
						kip_csa_usable = TRUE;
						/* Take this opportunity to reset si->cr_array_index */
						si->cr_array_index = 0;
					} else
					{
						kip_csa_usable = FALSE;
						assert(FALSE);
					}
					if (REG_COMMIT_COMPLETE != this_reg_commit_type)
					{
						if (kip_csa_usable && (NULL != si->kill_set_head) && (NULL == si->kip_csa))
							CAREFUL_INCR_KIP(csd, csa, si->kip_csa);
					} else
						assert((NULL == si->kill_set_head) || (NULL != si->kip_csa));
					assert((NULL == si->kill_set_head) || (NULL != si->kip_csa));
				} else
				{	/* Non-TP. Check need_kip_incr and value pointed to by kip_csa. */
					assert(non_tp_update_underway);
					/* Note that *kip_csa_addrs could be NULL if we are in the
					 * 1st phase of the M-kill and NON NULL if we are in the 2nd phase of the kill.
					 * Only if it is NULL, should we increment the kill_in_prog flag.
					 */
					kip_csa_usable =
						(GTM_PROBE(SIZEOF(*kip_csa_addrs), kip_csa_addrs, WRITE))
							? TRUE : FALSE;
					assert(kip_csa_usable);
					if (GTM_PROBE(SIZEOF(*need_kip_incr_addrs), need_kip_incr_addrs, WRITE))
						needkipincr = *need_kip_incr_addrs;
					else
					{
						needkipincr = FALSE;
						assert(FALSE);
					}
					if (needkipincr && kip_csa_usable && (NULL == *kip_csa_addrs))
					{
						CAREFUL_INCR_KIP(csd, csa, *kip_csa_addrs);
						*need_kip_incr_addrs = FALSE;
					}
#					ifdef UNIX
					if (TREF(in_mu_swap_root))
						cnl->root_search_cycle++;
#					endif
				}
			}	/* if (NULL != first_cw_set) */
			/* If the process is about to exit AND any kills are in progress (bitmap freeup phase of kill), mark
			 * kill_in_prog as abandoned. Non-TP and TP maintain kill_in_prog information in different structures
			 * so access them appropriately. Note that even for a TP transaction, the bitmap freeup happens as a
			 * non-TP transaction so checking dollar_tlevel is not enough to determine if we are in TP or non-TP.
			 * Thankfully first_sgm_info is guaranteed to be non-NULL in the case of a TP transaction that is
			 * temporarily running its bitmap freeup phase as a non-TP transaction. And for true non-TP
			 * transactions, first_sgm_info is guaranteed to be NULL. So we use this for the determination.
			 * But this global variable value is obtained by dereferencing first_sgm_info_addrs (due to the way
			 * GTMSECSHR runs as a separate privileged image in VMS). If the probe of first_sgm_info_addrs does
			 * not succeed (due to some corruption), then we have no clue about the nullness of first_sgm_info.
			 * Therefore we also check for dlr_tlevel also since if that is TRUE, we are guaranteed it is a TP
			 * transaction irrespective of the value of first_sgm_info. Note that we store the value of the global
			 * variable first_sgm_info in a local variable firsgsgminfo (slightly different name) for clarity sake.
			 */
			if (is_exiting)
			{
				if (GTM_PROBE(SIZEOF(*first_sgm_info_addrs), first_sgm_info_addrs, READ))
					firstsgminfo = *first_sgm_info_addrs;
				else
				{
					assert(FALSE);
					firstsgminfo = NULL;
				}
				if (dlr_tlevel || (NULL != firstsgminfo))
				{
					si = csa->sgm_info_ptr;
					kip_csa_usable = (GTM_PROBE(SIZEOF(sgm_info), si, WRITE)) ? TRUE : FALSE;
					assert(kip_csa_usable);
					/* Since the kill process cannot be completed, we need to decerement KIP count
					 * and increment the abandoned_kills count.
					 */
					if (kip_csa_usable && (NULL != si->kill_set_head) && (NULL != si->kip_csa))
					{
						assert(csa == si->kip_csa);
						CAREFUL_DECR_KIP(csd, csa, si->kip_csa);
						CAREFUL_INCR_ABANDONED_KILLS(csd, csa);
					} else
						assert((NULL == si->kill_set_head) || (NULL == si->kip_csa));
				} else if (!dlr_tlevel)
				{
					kip_csa_usable =
						(GTM_PROBE(SIZEOF(*kip_csa_addrs), kip_csa_addrs, WRITE))
						? TRUE : FALSE;
					assert(kip_csa_usable);
					if (kip_csa_usable && (NULL != *kip_csa_addrs) && (csa == *kip_csa_addrs))
					{
						assert(0 < (*kip_csa_addrs)->hdr->kill_in_prog);
						CAREFUL_DECR_KIP(csd, csa, *kip_csa_addrs);
						CAREFUL_INCR_ABANDONED_KILLS(csd, csa);
					}
				}
			}
			if (JNL_ENABLED(csd))
			{
				if (GTM_PROBE(SIZEOF(jnl_private_control), csa->jnl, WRITE))
				{
					jbp = csa->jnl->jnl_buff;
					if (GTM_PROBE(SIZEOF(jnl_buffer), jbp, WRITE) && is_exiting)
					{
						SALVAGE_UNIX_LATCH(&jbp->fsync_in_prog_latch, is_exiting);
						if (VMS_ONLY(csa->jnl->qio_active)
							UNIX_ONLY(jbp->io_in_prog_latch.u.parts.latch_pid \
								  == rundown_process_id))
						{
							if (csa->jnl->dsk_update_inprog)
							{
								jbp->dsk = csa->jnl->new_dsk;
								jbp->dskaddr = csa->jnl->new_dskaddr;
							}
							VMS_ONLY(
								bci(&jbp->io_in_prog);
								csa->jnl->qio_active = FALSE;
							)
							UNIX_ONLY(RELEASE_SWAPLOCK(&jbp->io_in_prog_latch));
						}
						if (jbp->free_update_pid == rundown_process_id)
						{	/* Got shot in the midst of updating freeaddr/free in jnl_write.c
							 * Fix the values (possible only in VMS where we have kernel extension).
							 */
							UNIX_ONLY(assert(FALSE);)
							assert(csa->now_crit);
							jbp->free = csa->jnl->temp_free;
							jbp->freeaddr = csa->jnl->new_freeaddr;
							jbp->free_update_pid = 0;
							DBG_CHECK_JNL_BUFF_FREEADDR(jbp);
						}
						if (jbp->blocked == rundown_process_id)
						{
							assert(csa->now_crit);
							jbp->blocked = 0;
						}
					}
				} else
				{
					SECSHR_ACCOUNTING(4);
					SECSHR_ACCOUNTING(__LINE__);
					SECSHR_ACCOUNTING((INTPTR_T)csa->jnl);
					SECSHR_ACCOUNTING(SIZEOF(jnl_private_control));
					assert(FALSE);
				}
			}
			if (is_exiting && csa->freeze && csd->freeze == rundown_process_id && !csa->persistent_freeze)
			{
				csd->image_count = 0;
				csd->freeze = 0;
			}
			if (is_bg && (csa->wbuf_dqd || csa->now_crit || csa->t_commit_crit || set_wc_blocked))
			{	/* if csa->wbuf_dqd == TRUE, most likely failed during REMQHI in wcs_wtstart
				 * 	or db_csh_get.  cache corruption is suspected so set wc_blocked.
				 * if csa->now_crit is TRUE, someone else should clean the cache, so set wc_blocked.
				 * if csa->t_commit_crit is TRUE, even if csa->now_crit is FALSE, we might need cache
				 * 	cleanup (e.g. cleanup of orphaned cnl->wcs_phase2_commit_pidcnt counter in case
				 * 	a process gets shot in the midst of DECR_WCS_PHASE2_COMMIT_PIDCNT macro before
				 * 	decrementing the shared counter but after committing the transaction otherwise)
				 * 	so set wc_blocked. This case is folded into phase2 cleanup case below.
				 * if set_wc_blocked is TRUE, need to clean up queues after phase2 commits.
				 */
				SET_TRACEABLE_VAR(cnl->wc_blocked, TRUE);
				if (csa->now_crit)
				{
					wcblocked_ptr = WCBLOCKED_NOW_CRIT_LIT;
					BG_TRACE_PRO_ANY(csa, wcb_secshr_db_clnup_now_crit);
				} else if (csa->wbuf_dqd)
				{
					wcblocked_ptr = WCBLOCKED_WBUF_DQD_LIT;
					BG_TRACE_PRO_ANY(csa, wcb_secshr_db_clnup_wbuf_dqd);
				} else
				{
					wcblocked_ptr = WCBLOCKED_PHASE2_CLNUP_LIT;
					BG_TRACE_PRO_ANY(csa, wcb_secshr_db_clnup_phase2_clnup);
				}
				UNIX_ONLY(
					/* cannot send oplog message in VMS as privileged routines cannot do I/O */
					send_msg(VARLSTCNT(8) ERR_WCBLOCKED, 6, LEN_AND_STR(wcblocked_ptr),
						rundown_process_id, &csd->trans_hist.curr_tn, DB_LEN_STR(reg));
				)
			}
			csa->wbuf_dqd = 0;		/* We can clear the flag now */
			if (csa->wcs_pidcnt_incremented)
				CAREFUL_DECR_WCS_PHASE2_COMMIT_PIDCNT(csa, cnl);
			if (csa->now_crit)
			{
				if (csd->trans_hist.curr_tn == csd->trans_hist.early_tn - 1)
				{	/* there can be at most one region in non-TP with different curr_tn and early_tn */
					assert(!non_tp_update_underway || first_time);
					assert(NORMAL_TERMINATION != secshr_state); /* for normal termination we should not
										     * have been in the midst of commit */
					DEBUG_ONLY(first_time = FALSE;)
					if (update_underway)
					{
						INCREMENT_CURR_TN(csd);	/* roll forward step (8) */
					} else
						csd->trans_hist.early_tn = csd->trans_hist.curr_tn;
				}
				assert(csd->trans_hist.early_tn == csd->trans_hist.curr_tn);
				if (GTM_PROBE(CRIT_SPACE, csa->critical, WRITE))
				{
					/* ONLINE ROLLBACK can come here holding crit ONLY due to commit errors but NOT during
					 * process exiting as secshr_db_clnup during process exiting is always preceded by
					 * mur_close_files which does the rel_crit anyways. Assert that.
					 */
					UNIX_ONLY(assert(!csa->hold_onto_crit || !jgbl.onlnrlbk || !is_exiting));
					if (!csa->hold_onto_crit || is_exiting)
					{ 	/* Release crit but since it involves modifying more than one field, make sure
						 * we prevent interrupts while in this code. The global variable "crit_count"
						 * does this for us. See similar usage in rel_crit.c. We currently use this here
						 * only for Unix because in VMS, a global variable in GTMSHR is not accessible
						 * in GTMSECSHR image easily unless passed through init_secshr_addrs. Since in
						 * VMS, if we are here, we are already in a kernel level routine, we will not be
						 * interrupted by user level timer handlers (wcs_stale or wcs_clean_dbsync_ast)
						 * that care about the consistency of the crit values so it is okay not to
						 * explicitly prevent interrupts using "crit_count" in VMS.
						 */
						UNIX_ONLY(
							assert(0 == crit_count);
							crit_count++;	/* prevent interrupts */
							CRIT_TRACE(crit_ops_rw); /* see gdsbt.h for comment on placement */
						)
						if (cnl->in_crit == rundown_process_id)
							cnl->in_crit = 0;
						UNIX_ONLY(
							csa->hold_onto_crit = FALSE;
							DEBUG_ONLY(locknl = cnl;)	/* for DEBUG_ONLY LOCK_HIST macro */
							mutex_unlockw(reg, crash_count);/* roll forward step (11) */
							assert(!csa->now_crit);
							DEBUG_ONLY(locknl = NULL;)	/* restore "locknl" to default value */
							crit_count = 0;
						)
						VMS_ONLY(
							mutex_stoprelw(csa->critical);	/* roll forward step (11) */
							csa->now_crit = FALSE;
						)
						UNSUPPORTED_PLATFORM_CHECK;
					}
				} else
				{
					SECSHR_ACCOUNTING(6);
					SECSHR_ACCOUNTING(__LINE__);
					SECSHR_ACCOUNTING((INTPTR_T)cnl);
					SECSHR_ACCOUNTING(NODE_LOCAL_SIZE_DBS);
					SECSHR_ACCOUNTING((INTPTR_T)csa->critical);
					SECSHR_ACCOUNTING(CRIT_SPACE);
					assert(FALSE);
				}
			}
			csa->t_commit_crit = FALSE; /* ensure we don't process this region again (rolls forward step (15)) */
			if ((NORMAL_TERMINATION == secshr_state || ABNORMAL_TERMINATION == secshr_state)
			    && GTM_PROBE(SHMPOOL_BUFFER_SIZE, csa->shmpool_buffer, WRITE))
			{
				if ((pid = csa->shmpool_buffer->shmpool_crit_latch.u.parts.latch_pid)
				    == rundown_process_id VMS_ONLY(&&)
				    VMS_ONLY((imgcnt = csa->shmpool_buffer->shmpool_crit_latch.u.parts.latch_image_count) \
					     == rundown_image_count))
				{
					if (is_exiting)
					{	/* Tiz our lock. Force recovery to run and release */
						csa->shmpool_buffer->shmpool_blocked = TRUE;
						BG_TRACE_PRO_ANY(csa, shmpool_blkd_by_sdc);
						SET_LATCH_GLOBAL(&csa->shmpool_buffer->shmpool_crit_latch, LOCK_AVAILABLE);
						DEBUG_LATCH(util_out_print("Latch cleaned up", FLUSH));
					}
				} else if (0 != pid && FALSE == is_proc_alive(pid, 0))
				{
					/* Attempt to make it our lock so we can set blocked */
					if (COMPSWAP_LOCK(&csa->shmpool_buffer->shmpool_crit_latch, pid, imgcnt,
							  rundown_process_id, rundown_image_count))
					{	/* Now our lock .. set blocked and release.  */
						csa->shmpool_buffer->shmpool_blocked = TRUE;
						BG_TRACE_PRO_ANY(csa, shmpool_blkd_by_sdc);
						DEBUG_LATCH(util_out_print("Orphaned latch cleaned up", TRUE));
						COMPSWAP_UNLOCK(&csa->shmpool_buffer->shmpool_crit_latch, rundown_process_id,
								rundown_image_count, LOCK_AVAILABLE, 0);
					} /* Else someone else took care of it */
				}
			}
#ifdef UNIX
			/* All releases done now. Double check latch is really cleared */
			if (GTM_PROBE(CRIT_SPACE, csa->critical, WRITE))
			{
				/* as long as csa->hold_onto_crit is FALSE, we should have released crit if we held it at entry */
				assert(!csa->now_crit || csa->hold_onto_crit);
				SALVAGE_UNIX_LATCH_DBCRIT(&csa->critical->semaphore, is_exiting, cnl->wc_blocked);
				SALVAGE_UNIX_LATCH(&csa->critical->crashcnt_latch, is_exiting);
				SALVAGE_UNIX_LATCH(&csa->critical->prochead.latch, is_exiting);
				SALVAGE_UNIX_LATCH(&csa->critical->freehead.latch, is_exiting);
			}
#endif
		}	/* For all regions */
	}	/* For all glds */
	if (jnlpool_reg_addrs && (GTM_PROBE(SIZEOF(*jnlpool_reg_addrs), jnlpool_reg_addrs, READ)))
	{	/* although there is only one jnlpool reg, SECSHR_PROBE_REGION macro might do a "continue" and hence the for loop */
		for (reg = *jnlpool_reg_addrs, jnlpool_reg = TRUE; jnlpool_reg && reg; jnlpool_reg = FALSE) /* only jnlpool reg */
		{
			SECSHR_PROBE_REGION(reg);	/* SECSHR_PROBE_REGION sets csa */
			if (csa->now_crit)
			{
				/* for normal termination we should not have been holding the journal pool crit lock */
				assert((NORMAL_TERMINATION) != secshr_state || ((gtm_white_box_test_case_enabled
				       && (WBTEST_ANTIFREEZE_DSKNOSPCAVAIL == gtm_white_box_test_case_number))));
				jpl = (jnlpool_ctl_ptr_t)((sm_uc_ptr_t)csa->critical - JNLPOOL_CTL_SIZE); /* see jnlpool_init() for
													   * relationship between
													   * critical and jpl */
				if (GTM_PROBE(SIZEOF(jnlpool_ctl_struct), jpl, WRITE))
				{
					if ((jpl->early_write_addr > jpl->write_addr) && (update_underway))
					{	/* we need to update journal pool to reflect the increase in jnl-seqno */
						cumul_jnl_rec_len = (uint4)(jpl->early_write_addr - jpl->write_addr);
						jh = (jnldata_hdr_ptr_t)((sm_uc_ptr_t)jpl + JNLDATA_BASE_OFF + jpl->write);
						if (GTM_PROBE(SIZEOF(*jh), jh, WRITE) && 0 != (jsize = jpl->jnlpool_size))
						{	/* Below chunk of code mirrors  what is done in t_end/tp_tend */
							/* Begin atomic stmnts. Follow same order as in t_end/tp_tend */
							jh->jnldata_len = cumul_jnl_rec_len;
							jh->prev_jnldata_len = jpl->lastwrite_len;
#							ifdef UNIX
							if (INVALID_SUPPL_STRM != strm_index)
							{	/* Need to also update supplementary stream seqno */
								assert(0 <= strm_index);
								/* assert(strm_index < ARRAYSIZE(tjpl->strm_seqno)); */
								ASSERT_INST_FILE_HDR_HAS_HISTREC_FOR_STRM(strm_index);
								jpl->strm_seqno[strm_index]++;
							}
#							endif
							jpl->lastwrite_len = cumul_jnl_rec_len;
							SECSHR_SHM_WRITE_MEMORY_BARRIER;
							/* Emulate
							 * jpl->write = (jpl->write + cumul_jnl_rec_len) % jsize;
							 * See note in DOs and DONTs about using % operator
							 */
							for (new_write = jpl->write + cumul_jnl_rec_len;
								new_write >= jsize;
								new_write -= jsize)
								;
							jpl->write = new_write;
							jpl->write_addr += cumul_jnl_rec_len;
							jpl->jnl_seqno++;
							/* End atomic stmts */
							/* the above takes care of rolling forward steps (9) and (10) of the
							 * commit flow */
						}
					}
#ifdef DEBUG
					else if (jpl->early_write_addr > jpl->write_addr)
					{   /* PRO code will do the right thing by overwriting that exact space in the jnlpool with
					     * the current transaction's journal records. For dbg though, it is better if
					     * secshr_db_clnup (which is invoked as part of exit handling) does the cleanup.
					     */
						assert(!update_underway);
						jpl->early_write_addr = jpl->write_addr;
					}
#endif
				}
				cnl = csa->nl;
				if ((GTM_PROBE(NODE_LOCAL_SIZE_DBS, cnl, WRITE)) &&
					(GTM_PROBE(CRIT_SPACE, csa->critical, WRITE)))
				{
					/* ONLINE ROLLBACK can come here holding crit ONLY due to commit errors but NOT during
					 * process exiting as secshr_db_clnup during process exiting is always preceded by
					 * mur_close_files which does the rel_crit anyways. Assert that.
					 */
					UNIX_ONLY(assert(!csa->hold_onto_crit || !jgbl.onlnrlbk || !is_exiting));
					if (!csa->hold_onto_crit || is_exiting)
					{
						UNIX_ONLY(CRIT_TRACE(crit_ops_rw)); /* see gdsbt.h for comment on placement */
						if (cnl->in_crit == rundown_process_id)
							cnl->in_crit = 0;
						UNIX_ONLY(
							csa->hold_onto_crit = FALSE;
							DEBUG_ONLY(locknl = cnl;)	/* for DEBUG_ONLY LOCK_HIST macro */
							mutex_unlockw(reg, 0);		/* roll forward step (12) */
							assert(!csa->now_crit);
							DEBUG_ONLY(locknl = NULL;)	/* restore "locknl" to default value */
						)
						VMS_ONLY(
							mutex_stoprelw(csa->critical);	/* roll forward step (12) */
							csa->now_crit = FALSE;
						)
						/* the above takes care of rolling forward step (12) of the commit flow */
					}
				}
			}
			/* as long as csa->hold_onto_crit is FALSE, we should have released crit if we held it at entry */
			UNIX_ONLY(assert(!csa->now_crit || csa->hold_onto_crit));
		}
	}
	return;
}

boolean_t	secshr_tp_get_cw(cw_set_element *cs, int depth, cw_set_element **cs1)
{
	int	iter;

	*cs1 = cs;
	for (iter = 0; iter < depth; iter++)
	{
		if (!(GTM_PROBE(SIZEOF(cw_set_element), *cs1, READ)))
		{
			*cs1 = NULL;
			return FALSE;
		}
		*cs1 = (*cs1)->next_cw_set;
	}
	if (*cs1 && GTM_PROBE(SIZEOF(cw_set_element), *cs1, READ))
	{
		while ((*cs1)->high_tlevel)
		{
			if (GTM_PROBE(SIZEOF(cw_set_element), (*cs1)->high_tlevel, READ))
				*cs1 = (*cs1)->high_tlevel;
			else
			{
				*cs1 = NULL;
				return FALSE;
			}
		}
	}
	return TRUE;
}
