fis-gtm/sr_port/wcs_verify.c

1330 lines
53 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. *
* *
****************************************************************/
/* includes */
#include "mdef.h"
#include "gtm_stdio.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 "gdsbml.h"
#include "testpt.h"
#include "filestruct.h"
#include "interlock.h"
#include "jnl.h"
#include "min_max.h"
#include "send_msg.h"
#include "cert_blk.h"
#include "memcoherency.h"
/* global refs/defs */
/* defines */
#define FAKE_DIRTY ((trans_num)(-1))
GBLREF uint4 process_id;
error_def(ERR_DBADDRALIGN);
error_def(ERR_DBADDRANGE);
error_def(ERR_DBADDRANGE8);
error_def(ERR_DBCLNUPINFO);
error_def(ERR_DBCRERR);
error_def(ERR_DBCRERR8);
error_def(ERR_DBFHEADERR4);
error_def(ERR_DBFHEADERR8);
error_def(ERR_DBFHEADERRANY);
error_def(ERR_DBQUELINK);
error_def(ERR_DBWCVERIFYEND);
error_def(ERR_DBWCVERIFYSTART);
boolean_t wcs_verify(gd_region *reg, boolean_t expect_damage, boolean_t caller_is_wcs_recover)
{ /* This routine verifies the shared memory structures used to manage the buffers of the bg access method.
* Changes to those structures or the way that they are managed may require changes to this routine
* some fields may not be rigorously tested if their interrelationships did not seem
* important, well defined or well understood, i.e. feel free to make improvements.
* It *corrects* errors which have a point nature and
* returns a FALSE for systemic problems that require a wcs_recover or something more drastic.
*/
uint4 cnt, lcnt ;
ssize_t offset ;
trans_num max_tn, tmp_8byte;
INTPTR_T bp_lo, bp_top, bp, cr_base, cr_top, bt_top_off, bt_base_off;
sm_uc_ptr_t bptmp;
boolean_t ret;
sgmnt_addrs *csa;
sgmnt_data_ptr_t csd;
node_local_ptr_t cnl;
cache_rec_ptr_t cr, cr0, cr_tmp, cr_prev, cr_hi, cr_lo, cr_qbase;
bt_rec_ptr_t bt, bt0, bt_prev, bt_hi, bt_lo;
th_rec_ptr_t th, th_prev;
cache_que_head_ptr_t que_head;
cache_state_rec_ptr_t cstt, cstt_prev;
char secshr_string[2048];
char secshr_string_delta[256];
sm_uc_ptr_t jnl_buff_expected;
boolean_t (*blkque_array)[] = NULL; /* TRUE indicates we saw the cr or bt of that array index */
int4 i, n_bts; /* a copy of csd->n_bts since it is used frequently in this routine */
trans_num dummy_tn;
int4 in_wtstart, intent_wtstart, wcs_phase2_commit_pidcnt;
csa = &FILE_INFO(reg)->s_addrs;
csd = csa->hdr;
cnl = csa->nl;
ret = TRUE;
send_msg(VARLSTCNT(7) ERR_DBWCVERIFYSTART, 5, DB_LEN_STR(reg), process_id, process_id, &csd->trans_hist.curr_tn);
/* while some errors terminate loops, as of this writing, no errors are treated as terminal */
if ((csa->now_crit == FALSE) && (csd->clustered == FALSE))
{
assert(expect_damage);
assert(!csa->hold_onto_crit);
ret = FALSE;
send_msg(VARLSTCNT(8) ERR_DBFHEADERR4, 6, DB_LEN_STR(reg), RTS_ERROR_TEXT("now_crit"), csa->now_crit, TRUE);
grab_crit(reg); /* what if it has it but lost track of it ??? should there be a crit reset ??? */
}
if (dba_mm != csd->acc_meth)
{
offset = ROUND_UP(SIZEOF_FILE_HDR(csd), (SIZEOF(int4) * 2));
if (cnl->bt_header_off != offset) /* bt_header is "quadword-aligned" after the header */
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(8) ERR_DBFHEADERRANY, 6, DB_LEN_STR(reg),
RTS_ERROR_TEXT("bt_header_off"), cnl->bt_header_off, offset);
cnl->bt_header_off = offset;
}
if (csa->bt_header != (bt_rec_ptr_t)((sm_uc_ptr_t)csd + cnl->bt_header_off))
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(8) ERR_DBFHEADERRANY, 6, DB_LEN_STR(reg),
RTS_ERROR_TEXT("bt_header"), csa->bt_header, (sm_uc_ptr_t)csd + cnl->bt_header_off);
csa->bt_header = (bt_rec_ptr_t)((sm_uc_ptr_t)csd + cnl->bt_header_off);
}
offset += csd->bt_buckets * SIZEOF(bt_rec);
if (cnl->th_base_off != (offset + SIZEOF(bt->blkque))) /* th_base follows, skipping the initial blkque heads */
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(8) ERR_DBFHEADERRANY, 6, DB_LEN_STR(reg),
RTS_ERROR_TEXT("th_base_off"), cnl->th_base_off, offset + SIZEOF(bt->blkque));
cnl->th_base_off = (offset + SIZEOF(bt->blkque));
}
if (csa->th_base != (th_rec_ptr_t)((sm_uc_ptr_t)csd + cnl->th_base_off))
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(8) ERR_DBFHEADERRANY, 6, DB_LEN_STR(reg),
RTS_ERROR_TEXT("th_base"), csa->th_base, (sm_uc_ptr_t)csd + cnl->th_base_off);
csa->th_base = (th_rec_ptr_t)((sm_uc_ptr_t)csd + cnl->th_base_off);
}
offset += SIZEOF(bt_rec);
if (cnl->bt_base_off != offset) /* bt_base just skips the item used as the tnque head */
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(8) ERR_DBFHEADERRANY, 6, DB_LEN_STR(reg),
RTS_ERROR_TEXT("bt_base_off"), cnl->bt_base_off, offset);
cnl->bt_base_off = offset;
}
if (csa->bt_base != (bt_rec_ptr_t)((sm_uc_ptr_t)csd + cnl->bt_base_off))
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(8) ERR_DBFHEADERRANY, 6, DB_LEN_STR(reg),
RTS_ERROR_TEXT("bt_base"), csa->bt_base, (sm_uc_ptr_t)csd + cnl->bt_base_off);
csa->bt_base = (bt_rec_ptr_t)((sm_uc_ptr_t)csd + cnl->bt_base_off);
}
} else
n_bts = csd->n_bts;
if (csa->ti != &csd->trans_hist)
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(8) ERR_DBFHEADERRANY, 6, DB_LEN_STR(reg),
RTS_ERROR_TEXT("csa->ti"), (sm_uc_ptr_t)csa->ti, (sm_uc_ptr_t)&csd->trans_hist);
csa->ti = &csd->trans_hist;
}
if (dba_mm != csd->acc_meth)
{
n_bts = csd->n_bts;
offset += n_bts * SIZEOF(bt_rec);
if (0 != (cnl->cache_off + CACHE_CONTROL_SIZE(csd)))
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(8) ERR_DBFHEADERRANY, 6, DB_LEN_STR(reg),
RTS_ERROR_TEXT("cache_off"), cnl->cache_off, -CACHE_CONTROL_SIZE(csd));
cnl->cache_off = -CACHE_CONTROL_SIZE(csd);
}
if (csa->acc_meth.bg.cache_state != (cache_que_heads_ptr_t)((sm_uc_ptr_t)csd + cnl->cache_off))
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(8) ERR_DBFHEADERRANY, 6, DB_LEN_STR(reg),
RTS_ERROR_TEXT("cache_state"), csa->acc_meth.bg.cache_state, (sm_uc_ptr_t)csd + cnl->cache_off);
csa->acc_meth.bg.cache_state = (cache_que_heads_ptr_t)((sm_uc_ptr_t)csd + cnl->cache_off);
}
if (csd->bt_buckets != getprime(n_bts))
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(8) ERR_DBFHEADERR4, 6, DB_LEN_STR(reg),
RTS_ERROR_TEXT("bt_buckets"), csd->bt_buckets, getprime(n_bts));
csd->bt_buckets = getprime(n_bts);
}
}
if (JNL_ALLOWED(csd))
{
if (NULL == csa->jnl)
send_msg(VARLSTCNT(8) ERR_DBFHEADERRANY, 6, DB_LEN_STR(reg),
RTS_ERROR_TEXT("csa->jnl"), csa->jnl, (UINTPTR_T)-1);
else if (NULL == csa->jnl->jnl_buff)
send_msg(VARLSTCNT(8) ERR_DBFHEADERRANY, 6, DB_LEN_STR(reg),
RTS_ERROR_TEXT("csa->jnl->jnl_buff"), csa->jnl->jnl_buff, (UINTPTR_T)-1);
else
{
jnl_buff_expected = ((sm_uc_ptr_t)(cnl) + NODE_LOCAL_SPACE + JNL_NAME_EXP_SIZE);
if (csa->jnl->jnl_buff != (jnl_buffer_ptr_t)jnl_buff_expected)
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(8) ERR_DBFHEADERRANY, 6, DB_LEN_STR(reg),
RTS_ERROR_TEXT("csa->jnl->jnl_buff_expected"), csa->jnl->jnl_buff, jnl_buff_expected);
csa->jnl->jnl_buff = (jnl_buffer_ptr_t)jnl_buff_expected;
}
}
}
if (dba_mm != csd->acc_meth)
{
bt_lo = csa->bt_base;
bt_hi = bt_lo + n_bts;
cr_lo = (cache_rec_ptr_t)csa->acc_meth.bg.cache_state->cache_array + csd->bt_buckets;
cr_hi = cr_lo + n_bts;
cr_base = GDS_ANY_ABS2REL(csa, cr_lo);
cr_top = GDS_ANY_ABS2REL(csa, cr_hi);
if (caller_is_wcs_recover)
{ /* if wcs_recover is caller, it would have waited for the following fields to become 0.
* if called from DSE CACHE -VERIFY, none of these are guaranteed. So do these checks only for first case.
*/
if (FALSE == cnl->wc_blocked)
{ /* in UNIX this blocks the writer */
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(8) ERR_DBFHEADERR4, 6, DB_LEN_STR(reg),
RTS_ERROR_TEXT("wc_blocked"), cnl->wc_blocked, TRUE);
SET_TRACEABLE_VAR(cnl->wc_blocked, TRUE);
}
in_wtstart = cnl->in_wtstart; /* store value in local variable in case the following assert fails */
if (0 != in_wtstart)
{ /* caller should outwait active writers */
ret = FALSE;
send_msg(VARLSTCNT(8) ERR_DBFHEADERR4, 6, DB_LEN_STR(reg), RTS_ERROR_TEXT("in_wtstart"),
in_wtstart, 0);
assert(expect_damage);
cnl->in_wtstart = 0;
csa->in_wtstart = FALSE; /* To allow wcs_wtstart() after wcs_recover() */
}
intent_wtstart = cnl->intent_wtstart;
if (0 != intent_wtstart)
{ /* Two situations are possible.
* a) A wcs_wtstart() call is concurrently in progress and that the process has just now
* incremented intent_wtstart. It will notice cnl->wc_blocked to be TRUE and
* decrement intent_wtstart right away and return. So we dont need to do anything.
* b) A wcs_wtstart() call had previously increment intent_wtstart but got shot before it could
* get a chance to decrement the field. In this case, we need to clear the field to
* recover from this situation.
* Since the writer uses the DECR_INTENT_WTSTART macro which does not do DECR_CNTs if the value
* is already 0, it is okay to do the decrement even in case (a). There is a very small window
* that still exists. If the DECR_INTENT_WTSTART macro did the > 0 check when the field was
* positive and then wcs_verify reset the field, the DECR_CNT will happen and will cause it to
* become negative. But that will last until the next INCR_INTENT_WTSTART or wcs_recover which
* happens first. The INCR_INTENT_WTSTART macro has a double increment to take care of this
* case.
*/
send_msg(VARLSTCNT(8) ERR_DBFHEADERR4, 6, DB_LEN_STR(reg),
RTS_ERROR_TEXT("intent_wtstart"), intent_wtstart, 0);
cnl->intent_wtstart = 0;
SHM_WRITE_MEMORY_BARRIER;
}
wcs_phase2_commit_pidcnt = cnl->wcs_phase2_commit_pidcnt; /* store value in local in case assert fails */
if (0 != wcs_phase2_commit_pidcnt)
{ /* caller should outwait active committers */
ret = FALSE;
send_msg(VARLSTCNT(8) ERR_DBFHEADERR4, 6, DB_LEN_STR(reg),
RTS_ERROR_TEXT("wcs_phase2_commit_pidcnt"), wcs_phase2_commit_pidcnt, 0);
assert(expect_damage);
cnl->wcs_phase2_commit_pidcnt = 0;
csa->wcs_pidcnt_incremented = FALSE; /* Just to be safe */
}
}
th = csa->th_base;
if (th->blk != BT_QUEHEAD)
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(8) ERR_DBFHEADERR4, 6, DB_LEN_STR(reg),
RTS_ERROR_TEXT("th_base->blk"), th->blk, BT_QUEHEAD);
th->blk = BT_QUEHEAD;
}
/* loop through bt tnque */
for (th_prev = th, th = (th_rec_ptr_t)((sm_uc_ptr_t)th + th->tnque.fl), cnt = n_bts, max_tn = 0, cnt = n_bts + 1;
(th != csa->th_base) && (cnt > 0);
th_prev = th, cnt--, th = (th_rec_ptr_t)((sm_uc_ptr_t)th + th->tnque.fl))
{
bt = (bt_rec_ptr_t)((sm_uc_ptr_t)th - SIZEOF(bt->blkque));
if (BT_NOT_ALIGNED(bt, bt_lo))
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(11) ERR_DBADDRALIGN, 9, DB_LEN_STR(reg), th_prev, -1,
RTS_ERROR_TEXT("th->tnque"), bt, bt_lo, SIZEOF(bt_rec));
break;
}
if (BT_NOT_IN_RANGE(bt, bt_lo, bt_hi))
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(11) ERR_DBADDRANGE, 9, DB_LEN_STR(reg), th_prev, -1,
bt, RTS_ERROR_TEXT("th->tnque"), bt_lo, bt_hi);
break;
}
if ((th_rec_ptr_t)((sm_uc_ptr_t)th + th->tnque.bl) != th_prev)
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg), th, th->blk, RTS_ERROR_TEXT("tnque.bl"),
(UINTPTR_T)th->tnque.bl, (sm_uc_ptr_t)th_prev - (sm_uc_ptr_t)th);
}
if (th->tn != 0)
{
if (th->tn < max_tn)
{
assert(expect_damage);
ret = FALSE;
tmp_8byte = 1;
send_msg(VARLSTCNT(11) ERR_DBADDRANGE8, 9, DB_LEN_STR(reg), th, th->blk, &max_tn,
RTS_ERROR_TEXT("tnque transaction number"), &tmp_8byte, &th->tn);
}
/* ideally, the following max_tn assignment should have been in the else part of the above if. but
* the issue with doing that is if there is a sequence of non-decreasing transaction numbers
* except for one (or few) numbers in the middle of the sequence that is larger than all others,
* it is more likely that those hiccups are incorrect. in that case we do not want max_tn to end
* up being an incorrect large value. hence the unconditional assignment below.
*/
max_tn = th->tn;
}
if (((int)(th->blk) != BT_NOTVALID) &&
(((int)(th->blk) < 0) || ((int)(th->blk) > csd->trans_hist.total_blks)))
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(11) ERR_DBADDRANGE, 9, DB_LEN_STR(reg),
th, th->blk, th->blk, RTS_ERROR_TEXT("th->blk"), 0, csd->trans_hist.total_blks);
}
if (((int)(th->cache_index) != CR_NOTVALID) &&
(((int)(th->cache_index) < cr_base) || ((int)(th->cache_index) >= cr_top)))
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(11) ERR_DBADDRANGE, 9, DB_LEN_STR(reg),
th, th->blk, th->cache_index, RTS_ERROR_TEXT("th->cache_index"), cr_base, cr_top);
}
if (th->flushing != FALSE) /* ??? this is a gt.cx item that may require more synchronization at the top */
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(8) ERR_DBFHEADERR4, 6, DB_LEN_STR(reg),
RTS_ERROR_TEXT("th->flushing"), th->flushing, FALSE);
}
if (0 == th->tnque.fl)
{ /* No point proceeding to next iteration of loop as "th + th->tnque.fl" will be the same as "th" */
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg),
th, th->blk, RTS_ERROR_TEXT("tnque.fl"), (UINTPTR_T)th->tnque.fl, (UINTPTR_T)-1);
break;
}
}
if (cnt != 1)
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(8) ERR_DBFHEADERR4, 6, DB_LEN_STR(reg),
RTS_ERROR_TEXT("tnque entries"), n_bts - cnt, n_bts - 1);
} else if ((th == csa->th_base) && ((th_rec_ptr_t)((sm_uc_ptr_t)th + th->tnque.bl) != th_prev))
{ /* at this point "th" is csa->th_base and its backlink does not point to the last entry in the th queue */
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg), th, th->blk,
RTS_ERROR_TEXT("tnque th_base"), (UINTPTR_T)th->tnque.bl, (sm_uc_ptr_t)th_prev - (sm_uc_ptr_t)th);
}
if (max_tn > csd->trans_hist.curr_tn)
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(8) ERR_DBFHEADERR8, 6, DB_LEN_STR(reg),
RTS_ERROR_TEXT("MAX(th_base->tn)"), &max_tn, &csd->trans_hist.curr_tn);
}
/* loop through bt blkques */
blkque_array = malloc(n_bts * SIZEOF(boolean_t));
memset(blkque_array, 0, n_bts * SIZEOF(boolean_t)); /* initially, we did not find any bt in the bt blkques */
for (bt0 = csa->bt_header; bt0 < bt_lo; bt0++)
{
if (bt0->blk != BT_QUEHEAD)
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(8) ERR_DBFHEADERR4, 6, DB_LEN_STR(reg),
RTS_ERROR_TEXT("queue head bt->blk"), bt0->blk, BT_QUEHEAD);
bt0->blk = BT_QUEHEAD;
}
for (bt_prev = bt0, bt = (bt_rec_ptr_t)((sm_uc_ptr_t)bt0 + bt0->blkque.fl), cnt = n_bts + 1;
(bt != bt0) && (cnt > 0);
bt_prev = bt, cnt--, bt = (bt_rec_ptr_t)((sm_uc_ptr_t)bt + bt->blkque.fl))
{
if (BT_NOT_ALIGNED(bt, bt_lo))
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(11) ERR_DBADDRALIGN, 9, DB_LEN_STR(reg), bt_prev, -1,
RTS_ERROR_TEXT("bt->blkque"), bt, bt_lo, SIZEOF(bt_rec));
break;
}
if (BT_NOT_IN_RANGE(bt, bt_lo, bt_hi))
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(11) ERR_DBADDRANGE, 9, DB_LEN_STR(reg), bt_prev, -1,
bt, RTS_ERROR_TEXT("bt->blkque"), bt_lo, bt_hi);
break;
}
if ((bt_rec_ptr_t)((sm_uc_ptr_t)bt + bt->blkque.bl) != bt_prev)
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg), bt, bt->blk,
RTS_ERROR_TEXT("bt->blkque.bl"), (UINTPTR_T)bt->blkque.bl,
(sm_uc_ptr_t)bt_prev - (sm_uc_ptr_t)bt);
}
if ((int)(bt->blk) != BT_NOTVALID)
{
if ((csa->bt_header + (bt->blk % csd->bt_buckets)) != bt0)
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg), bt, bt->blk,
RTS_ERROR_TEXT("bt hash"), (bt0 - csa->bt_header),
(UINTPTR_T)(bt->blk % csd->bt_buckets));
}
if (CR_NOTVALID != bt->cache_index)
{
cr = (cache_rec_ptr_t)GDS_ANY_REL2ABS(csa, bt->cache_index);
/* Before checking if "cr->blk" is the same as "bt->blk", check if "cr" is valid */
if (CR_NOT_IN_RANGE(cr, cr_lo, cr_hi))
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(11) ERR_DBADDRANGE, 9, DB_LEN_STR(reg),
bt, bt->blk, cr, RTS_ERROR_TEXT("bt->cache_index"), cr_lo, cr_hi);
} else if (CR_NOT_ALIGNED(cr, cr_lo))
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(11) ERR_DBADDRALIGN, 9, DB_LEN_STR(reg), bt, bt->blk,
RTS_ERROR_TEXT("bt->cache_index"), cr, cr_lo, SIZEOF(cache_rec));
} else if (cr->blk != bt->blk)
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(13) ERR_DBCRERR, 11, DB_LEN_STR(reg), cr,
bt->blk, RTS_ERROR_TEXT("bt block"), cr->blk, bt->blk,
CALLFROM);
}
}
}
(*blkque_array)[bt - bt_lo] = TRUE; /* note: this bt's blkque hash validity is already checked */
if (0 == bt->blkque.fl)
{ /* No point proceeding to next iteration as "bt + bt->blkque.fl" will be the same as "bt" */
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg), bt, bt->blk,
RTS_ERROR_TEXT("bt->blkque.fl"), (UINTPTR_T)bt->blkque.fl, (UINTPTR_T)-1);
break;
}
}
if (cnt == 0)
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(8) ERR_DBFHEADERR4, 6, DB_LEN_STR(reg),
RTS_ERROR_TEXT("btque entries"), n_bts + 1 - cnt, n_bts + 1);
} else if ((bt == bt0) && ((bt_rec_ptr_t)((sm_uc_ptr_t)bt + bt->blkque.bl) != bt_prev))
{ /* at this point "bt" is bt0 and its backlink does not point to last entry in the bt0'th queue */
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg), bt, bt->blk,
RTS_ERROR_TEXT("btque bt_base"), (UINTPTR_T)bt->blkque.bl,
(sm_uc_ptr_t)bt_prev - (sm_uc_ptr_t)bt);
}
}
/* scan all bts looking for valid bt->blks whose bts were not in any blkque */
for (bt = bt_lo; bt < bt_hi; bt++)
{
if ((FALSE == (*blkque_array)[bt - bt_lo]) && ((int)(bt->blk) != BT_NOTVALID))
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg), bt, bt->blk,
RTS_ERROR_TEXT("bt blkque hash"), (UINTPTR_T)-1, (UINTPTR_T)(bt->blk % csd->bt_buckets));
}
}
bp_lo = ROUND_UP(cr_top, OS_PAGE_SIZE);
bp_top = bp_lo + ((gtm_uint64_t)n_bts * csd->blk_size);
bt_base_off = GDS_ANY_ABS2REL(csa, (sm_uc_ptr_t)csd + cnl->bt_base_off);
bt_top_off = GDS_ANY_ABS2REL(csa, (sm_uc_ptr_t)csd + offset);
/* print info. that secshr_db_clnup stored */
if (0 != cnl->secshr_ops_index)
{
assert(expect_damage);
if (SECSHR_OPS_ARRAY_SIZE < cnl->secshr_ops_index)
{
SPRINTF(secshr_string,
"secshr_max_index exceeded. max_index = %d [0x%08x] : ops_index = %d [0x%08x]",
SECSHR_OPS_ARRAY_SIZE, SECSHR_OPS_ARRAY_SIZE,
cnl->secshr_ops_index, cnl->secshr_ops_index);
send_msg(VARLSTCNT(6) ERR_DBCLNUPINFO, 4, DB_LEN_STR(reg), RTS_ERROR_TEXT(secshr_string));
cnl->secshr_ops_index = SECSHR_OPS_ARRAY_SIZE;
}
for (i = 0; (i + 1) < cnl->secshr_ops_index; i += (int4)cnl->secshr_ops_array[i])
{
SPRINTF(secshr_string, "Line %3ld ", cnl->secshr_ops_array[i + 1]);
for (lcnt = i + 2; lcnt < MIN(cnl->secshr_ops_index, i + cnl->secshr_ops_array[i]); lcnt++)
{
SPRINTF(secshr_string_delta, " : [0x%08lx]", cnl->secshr_ops_array[lcnt]);
strcat(secshr_string, secshr_string_delta);
}
send_msg(VARLSTCNT(6) ERR_DBCLNUPINFO, 4, DB_LEN_STR(reg), RTS_ERROR_TEXT(secshr_string));
}
cnl->secshr_ops_index = 0;
}
/* loop through the cache_recs */
memset(blkque_array, 0, n_bts * SIZEOF(boolean_t)); /* initially, we did not find any cr in the cr blkques */
for (bp = bp_lo, cr = cr_lo, cnt = n_bts; cnt > 0; cr++, bp += csd->blk_size, cnt--)
{
if (((int)(cr->blk) != CR_BLKEMPTY) &&
(((int)(cr->blk) < 0) || ((int)(cr->blk) >= csd->trans_hist.total_blks)))
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(11) ERR_DBADDRANGE, 9, DB_LEN_STR(reg),
cr, cr->blk, cr->blk, RTS_ERROR_TEXT("cr->blk"), 0, csd->trans_hist.total_blks);
}
if (cr->tn > csd->trans_hist.curr_tn)
{
assert(expect_damage);
ret = FALSE;
tmp_8byte = 0;
send_msg(VARLSTCNT(11) ERR_DBADDRANGE8, 9, DB_LEN_STR(reg),
cr, cr->blk, &cr->tn, RTS_ERROR_TEXT("cr->tn"), &tmp_8byte, &csd->trans_hist.curr_tn);
}
if (0 != cr->bt_index)
{
if (!IS_PTR_IN_RANGE(cr->bt_index, bt_base_off, bt_top_off))
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(11) ERR_DBADDRANGE, 9, DB_LEN_STR(reg),
cr, cr->blk, cr->bt_index, RTS_ERROR_TEXT("cr->bt_index"), bt_base_off,
bt_top_off);
} else if (!IS_PTR_ALIGNED(cr->bt_index, bt_base_off, SIZEOF(bt_rec)))
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(11) ERR_DBADDRALIGN, 9, DB_LEN_STR(reg),
cr, cr->blk, RTS_ERROR_TEXT("cr->bt_index"), cr->bt_index, bt_base_off,
SIZEOF(bt_rec));
} else
{
bt = (bt_rec_ptr_t)GDS_ANY_REL2ABS(csa, cr->bt_index);
if (cr->blk != bt->blk)
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(13) ERR_DBCRERR, 11, DB_LEN_STR(reg), cr,
cr->blk, RTS_ERROR_TEXT("cr block"), cr->blk, bt->blk,
CALLFROM);
}
}
}
if (!IS_PTR_IN_RANGE(cr->buffaddr, bp_lo, bp_top))
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(11) ERR_DBADDRANGE, 9, DB_LEN_STR(reg),
cr, cr->blk, cr->buffaddr, RTS_ERROR_TEXT("cr->buffaddr"), bp_lo, bp_top);
} else if (!IS_PTR_ALIGNED(cr->buffaddr, bp_lo, csd->blk_size))
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(11) ERR_DBADDRALIGN, 9, DB_LEN_STR(reg),
cr, cr->blk, RTS_ERROR_TEXT("cr->buffaddr"), cr->buffaddr, bp_lo, csd->blk_size);
} else if (cr->buffaddr != bp)
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(13) ERR_DBCRERR, 11, DB_LEN_STR(reg),
cr, cr->blk, RTS_ERROR_TEXT("cr->buffaddr"), cr->buffaddr, bp, CALLFROM);
}
if (cr->in_tend)
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(13) ERR_DBCRERR, 11, DB_LEN_STR(reg),
cr, cr->blk, RTS_ERROR_TEXT("cr->in_tend"), cr->in_tend, FALSE, CALLFROM);
}
if (cr->data_invalid)
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(13) ERR_DBCRERR, 11, DB_LEN_STR(reg),
cr, cr->blk, RTS_ERROR_TEXT("cr->data_invalid"), cr->data_invalid, FALSE, CALLFROM);
}
if (cr->r_epid != 0)
{
if (cr->read_in_progress < 0)
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(13) ERR_DBCRERR, 11, DB_LEN_STR(reg),
cr, cr->blk, RTS_ERROR_TEXT("cr->r_epid"), cr->r_epid, 0, CALLFROM);
}
} else if ((-1 == cr->read_in_progress) && !caller_is_wcs_recover && (CR_BLKEMPTY != cr->blk)
&& !cr->data_invalid
VMS_ONLY(&& (!IS_BITMAP_BLK(cr->blk) || (0 == cr->twin) || (0 != cr->bt_index))))
{ /* If the buffer is not being read into currently (checked both by cr->r_epid being 0 and
* cr->read_in_progress being -1) and we are being called from DSE CACHE -VERIFY and cr points
* to a valid non-empty block, check the content of cr->buffaddr through a cert_blk().
* In VMS, if it is a bitmap block, we could have twins so do check only on newtest twin as
* older twin could have an incorrect masterbitmap full/free status (DBBMMSTR error).
* Use "bp" as the buffer as cr->buffaddr might be detected as corrupt by the buffaddr checks
* above. The reason why the cert_blk() is done only from a DSE CACHE -VERIFY call and not from a
* wcs_recover() call is that wcs_recover() is supposed to check the integrity of the data
* structures in the cache and not the integrity of the data (global buffers) in the cache. If the
* database has an integrity error, a global buffer will fail cert_blk() but the cache structures
* as such are not damaged. wcs_recover() should not return failure in that case.
*/
bptmp = (sm_uc_ptr_t)GDS_ANY_REL2ABS(csa, bp);
if (!cert_blk(reg, cr->blk, (blk_hdr_ptr_t)bptmp, 0, FALSE))
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(13) ERR_DBCRERR, 11, DB_LEN_STR(reg),
cr, cr->blk, RTS_ERROR_TEXT("Block certification result"),
FALSE, TRUE, CALLFROM);
send_msg(VARLSTCNT(13) ERR_DBCRERR, 11, DB_LEN_STR(reg), cr, cr->blk,
RTS_ERROR_TEXT("Block certification result buffer"),
bptmp, csa->lock_addrs[0], CALLFROM);
}
}
if (0 != cr->in_cw_set)
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(13) ERR_DBCRERR, 11, DB_LEN_STR(reg),
cr, cr->blk, RTS_ERROR_TEXT("cr->in_cw_set"), (uint4)cr->in_cw_set, 0, CALLFROM);
}
assert(!JNL_ALLOWED(csd) || (NULL != csa->jnl) && (NULL != csa->jnl->jnl_buff));
if (JNL_ENABLED(csd))
{
if ((NULL != csa->jnl) && (NULL != csa->jnl->jnl_buff)
&& (0 != cr->dirty) && (cr->jnl_addr > csa->jnl->jnl_buff->freeaddr))
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(11) ERR_DBADDRANGE, 9, DB_LEN_STR(reg), cr, cr->blk,
(uint4)cr->jnl_addr, RTS_ERROR_TEXT("cr->jnl_addr"), 0,
csa->jnl->jnl_buff->freeaddr);
}
} else if (!JNL_ALLOWED(csd) && (cr->jnl_addr != 0))
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(13) ERR_DBCRERR, 11, DB_LEN_STR(reg),
cr, cr->blk, RTS_ERROR_TEXT("cr->jnl_addr"), (uint4)cr->jnl_addr, 0, CALLFROM);
}
if ((WRITE_LATCH_VAL(cr) < LATCH_CLEAR) || (WRITE_LATCH_VAL(cr) > LATCH_CONFLICT))
{ /* the message would read cr->interlock.semaphore although in Unix it means cr->interlock.latch */
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(11) ERR_DBADDRANGE, 9, DB_LEN_STR(reg), cr, cr->blk,
WRITE_LATCH_VAL(cr), RTS_ERROR_TEXT("cr->interlock.semaphore"), LATCH_CLEAR,
LATCH_CONFLICT);
}
/* as of this time cycle is believed to be a relative timestamp with no characteristics useful to verify */
#ifdef VMS
if (cr->rip_latch.u.parts.latch_pid != 0)
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(13) ERR_DBCRERR, 11, DB_LEN_STR(reg), cr, cr->blk,
RTS_ERROR_TEXT("cr->rip_latch"), cr->rip_latch.u.parts.latch_pid, 0, CALLFROM);
}
if (cr->iosb.cond != 0)
{ /* do not set "ret" to FALSE in both cases below. this is because it seems like VMS can set
* iosb.cond to the qio status much after a process that issued the qio died. our current
* suspicion is that this occurs because the iosb is in shared memory which is available even
* after the process dies. although the two cases below are unexpected, wcs_wtstart()/wcs_wtfini()
* handle this well enough that we do not see any need to consider this as a damaged cache. see
* D9B11-001992 for details. -- nars - July 2003.
*/
if (0 == cr->dirty)
{
assert(expect_damage);
dummy_tn = (trans_num)TRUE;
send_msg(VARLSTCNT(13) ERR_DBCRERR8, 11, DB_LEN_STR(reg),
cr, cr->blk, RTS_ERROR_TEXT("cr->cr->dirty"), &cr->dirty, &dummy_tn, CALLFROM);
send_msg(VARLSTCNT(13) ERR_DBCRERR, 11, DB_LEN_STR(reg),
cr, cr->blk, RTS_ERROR_TEXT("cr->cr->iosb"), cr->iosb.cond, 0, CALLFROM);
}
if (0 == cr->epid)
{
assert(expect_damage);
send_msg(VARLSTCNT(13) ERR_DBCRERR, 11, DB_LEN_STR(reg),
cr, cr->blk, RTS_ERROR_TEXT("cr->epid"), cr->epid, -1, CALLFROM);
send_msg(VARLSTCNT(13) ERR_DBCRERR, 11, DB_LEN_STR(reg),
cr, cr->blk, RTS_ERROR_TEXT("cr->iosb"), cr->iosb.cond, 0, CALLFROM);
}
}
if ((WRT_STRT_PNDNG == cr->iosb.cond) && (0 == cr->dirty))
{
assert(expect_damage);
ret = FALSE;
dummy_tn = (trans_num)TRUE;
send_msg(VARLSTCNT(13) ERR_DBCRERR8, 11, DB_LEN_STR(reg), cr, cr->blk,
RTS_ERROR_TEXT("cr->dirty"), &cr->dirty, &dummy_tn, CALLFROM);
}
if (cr->twin != 0)
{
cr_tmp = (cache_rec_ptr_t)GDS_ANY_REL2ABS(csa, cr->twin);
if (CR_NOT_IN_RANGE(cr_tmp, cr_lo, cr_hi))
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(11) ERR_DBADDRANGE, 9, DB_LEN_STR(reg),
cr, cr->blk, cr_tmp, RTS_ERROR_TEXT("cr->twin"), cr_lo, cr_hi);
} else if (CR_NOT_ALIGNED(cr_tmp, cr_lo))
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(11) ERR_DBADDRALIGN, 9, DB_LEN_STR(reg),
cr, cr->blk, RTS_ERROR_TEXT("cr->twin"), cr_tmp, cr_lo, SIZEOF(cache_rec));
} else if (cr != (cache_rec_ptr_t)GDS_ANY_REL2ABS(csa, cr_tmp->twin))
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(13) ERR_DBCRERR, 11, DB_LEN_STR(reg), cr_tmp, cr->blk,
RTS_ERROR_TEXT("cr->twin->twin"), GDS_ANY_REL2ABS(csa, cr_tmp->twin), cr,
CALLFROM);
}
}
#else
/* iosb, twin, image_count, wip_stopped are currently used in VMS only */
if (0 != cr->twin)
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(13) ERR_DBCRERR, 11, DB_LEN_STR(reg),
cr, cr->blk, RTS_ERROR_TEXT("cr->twin"), cr->twin, 0, CALLFROM);
}
if (0 != cr->image_count)
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(13) ERR_DBCRERR, 11, DB_LEN_STR(reg),
cr, cr->blk, RTS_ERROR_TEXT("cr->image_count"), cr->image_count, 0, CALLFROM);
}
if ((0 != cr->epid) && caller_is_wcs_recover)
{ /* if called from DSE CACHE -VERIFY, we do not wait for concurrent writers to finish */
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(13) ERR_DBCRERR, 11, DB_LEN_STR(reg),
cr, cr->blk, RTS_ERROR_TEXT("cr->epid"), cr->epid, 0, CALLFROM);
}
#endif
if (FALSE != cr->wip_stopped)
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(13) ERR_DBCRERR, 11, DB_LEN_STR(reg), cr, cr->blk,
RTS_ERROR_TEXT("cr->wip_stopped"), cr->wip_stopped, FALSE, CALLFROM);
}
if (FALSE != cr->stopped)
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(13) ERR_DBCRERR, 11, DB_LEN_STR(reg), cr, cr->blk,
RTS_ERROR_TEXT("cr->stopped"), cr->stopped, FALSE, CALLFROM);
}
}
/* loop through the cr blkques */
for (cr0 = (cache_rec_ptr_t)csa->acc_meth.bg.cache_state->cache_array, cr_qbase = cr0; cr0 < cr_lo; cr0++)
{
if (cr0->blk != BT_QUEHEAD)
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(13) ERR_DBCRERR, 11, DB_LEN_STR(reg), cr0, cr0->blk,
RTS_ERROR_TEXT("queue head cr->blk"), cr0->blk, BT_QUEHEAD, CALLFROM);
cr0->blk = BT_QUEHEAD;
}
for (cr_prev = cr0, cr = (cache_rec_ptr_t)((sm_uc_ptr_t)cr0 + cr0->blkque.fl), cnt = n_bts + 1;
(cr != cr0) && (cnt > 0);
cr_prev = cr, cnt--, cr = (cache_rec_ptr_t)((sm_uc_ptr_t)cr + cr->blkque.fl))
{
if (CR_NOT_IN_RANGE(cr, cr_lo, cr_hi))
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(11) ERR_DBADDRANGE, 9, DB_LEN_STR(reg),
cr0, -1, cr, RTS_ERROR_TEXT("cr->blkque"), cr_lo, cr_hi);
break;
}
if (CR_NOT_ALIGNED(cr, cr_lo))
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(11) ERR_DBADDRALIGN, 9, DB_LEN_STR(reg),
cr0, -1, RTS_ERROR_TEXT("cr->blkque"), cr, cr_lo, SIZEOF(cache_rec));
break;
}
if ((cache_rec_ptr_t)((sm_uc_ptr_t)cr + cr->blkque.bl) != cr_prev)
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg), cr, cr->blk,
RTS_ERROR_TEXT("cr->blkque.bl"), (UINTPTR_T)cr->blkque.bl,
(sm_uc_ptr_t)cr_prev - (sm_uc_ptr_t)cr);
}
if (((int)(cr->blk) != CR_BLKEMPTY) && ((cr_qbase + (cr->blk % csd->bt_buckets)) != cr0))
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(13) ERR_DBCRERR, 11, DB_LEN_STR(reg), cr, cr->blk,
RTS_ERROR_TEXT("cr hash"), cr0 - cr_qbase, cr->blk % csd->bt_buckets, CALLFROM);
if (caller_is_wcs_recover && !cr->stopped)
{ /* if cr->stopped is TRUE, then the buffer was created by secshr_db_clnup(),
* and hence it is ok to have different hash value, but otherwise we believe
* the hash value and consider cr->blk to be invalid and hence make this buffer
* empty
*
* Possible causes of this condition are if a process gets shot (kill -9 or STOP/ID)
* in the midst of shuffling a cache-record from one blkque to another blkque (done
* through a call to shuffqth in db_csh_getn.c). Since the act of removing a
* cache-record from one hashqueue and adding it to another hashqueue is not
* atomic, we can end up with a cache-record that is not in the proper hashqueue
* if we get shot in the middle.
*
* Ideally we would like to dump the contents of this broken buffer to a file for
* later analysis. Since we hold crit now, we do not want to do that. It might be
* better to copy this buffer into another area in shared-memory dedicated to
* holding such information so a later DSE session can then dump the information.
*
* Ideally, it should be wcs_recover() that fixes the cache-record, but then the
* blk_que traversing logic has to be redone there in order to determine this
* disparity in the hash value. To avoid that we reset cr->blk here itself but
* do it only if called from wcs_recover().
*/
assert(expect_damage);
cr->blk = CR_BLKEMPTY;
}
}
(*blkque_array)[cr - cr_lo] = TRUE; /* note: this cr's blkque hash validity is already checked */
if (0 == cr->blkque.fl)
{ /* No point proceeding to next iteration as "cr + cr->blkque.fl" will be the same as "cr" */
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg), cr, cr->blk,
RTS_ERROR_TEXT("cr->blkque.fl"), (UINTPTR_T)cr->blkque.fl, (UINTPTR_T)-1);
break;
}
}
if (cnt == 0)
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg),
cr_qbase, 0, RTS_ERROR_TEXT("crque entries"), (UINTPTR_T)(n_bts + 1), (UINTPTR_T)(n_bts));
} else if ((cr == cr0) && ((cache_rec_ptr_t)((sm_uc_ptr_t)cr + cr->blkque.bl) != cr_prev))
{ /* at this point "cr" is cr0 and its backlink does not point to last entry in the cr0'th queue */
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg), cr, cr->blk,
RTS_ERROR_TEXT("crque cr_base"), (UINTPTR_T)cr->blkque.bl,
(sm_uc_ptr_t)cr_prev - (sm_uc_ptr_t)cr);
}
}
/* scan all crs looking for non-empty cr->blks whose crs were not in any blkque */
for (cr = cr_lo; cr < cr_hi; cr++)
{
if ((FALSE == (*blkque_array)[cr - cr_lo]) && ((int)(cr->blk) != CR_BLKEMPTY))
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(13) ERR_DBCRERR, 11, DB_LEN_STR(reg), cr, cr->blk,
RTS_ERROR_TEXT("cr blkque hash"), -1, cr->blk % csd->bt_buckets, CALLFROM);
if (caller_is_wcs_recover && !cr->stopped) /* see comment above ("cr hash") for similar handling */
{
assert(expect_damage);
cr->blk = CR_BLKEMPTY;
}
}
}
que_head = &csa->acc_meth.bg.cache_state->cacheq_active;
if ((sm_long_t)que_head % SIZEOF(que_head->fl) != 0)
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg), que_head, 0, RTS_ERROR_TEXT("cacheq_active"),
que_head, ((sm_long_t)que_head / SIZEOF(que_head->fl)) * SIZEOF(que_head->fl));
}
/* loop through the active queue */
for (cstt_prev = (cache_state_rec_ptr_t)que_head,
cstt = (cache_state_rec_ptr_t)((sm_uc_ptr_t)que_head + que_head->fl), cnt = n_bts;
(cstt != (cache_state_rec_ptr_t)que_head) && (cnt > 0);
cstt_prev = cstt, cnt--, cstt = (cache_state_rec_ptr_t)((sm_uc_ptr_t)cstt + cstt->state_que.fl))
{
cr = (cache_rec_ptr_t)((sm_uc_ptr_t)cstt - SIZEOF(cr->blkque));
if (CR_NOT_IN_RANGE((cache_rec_ptr_t)cr, cr_lo, cr_hi))
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(11) ERR_DBADDRANGE, 9, DB_LEN_STR(reg), que_head, -1,
cr, RTS_ERROR_TEXT("active cstt->state_que"), cr_lo, cr_hi);
break;
}
if (CR_NOT_ALIGNED(cr, cr_lo))
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(11) ERR_DBADDRALIGN, 9, DB_LEN_STR(reg), que_head, -1,
RTS_ERROR_TEXT("active cstt->state_que"), cr, cr_lo, SIZEOF(cache_rec));
break;
}
if ((cache_state_rec_ptr_t)((sm_uc_ptr_t)cstt + cstt->state_que.bl) != cstt_prev)
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg), cstt, cstt->blk,
RTS_ERROR_TEXT("active queue.bl"), (UINTPTR_T)cstt->state_que.bl,
(sm_uc_ptr_t)cstt_prev - (sm_uc_ptr_t)cstt);
}
if (0 == cstt->dirty)
{
assert(expect_damage);
ret = FALSE;
dummy_tn = (trans_num)TRUE;
send_msg(VARLSTCNT(13) ERR_DBCRERR8, 11, DB_LEN_STR(reg), cr, cstt->blk,
RTS_ERROR_TEXT("active cr->dirty"), &cstt->dirty, &dummy_tn, CALLFROM);
}
if (((0 != cstt->flushed_dirty_tn) && (cstt->dirty <= cstt->flushed_dirty_tn))
|| (cstt->dirty > csd->trans_hist.curr_tn))
{
assert(expect_damage);
ret = FALSE;
dummy_tn = cstt->flushed_dirty_tn + 1;
send_msg(VARLSTCNT(11) ERR_DBADDRANGE8, 9, DB_LEN_STR(reg), cstt + SIZEOF(que_head), cstt->blk,
&cstt->dirty, RTS_ERROR_TEXT("active dirty (tn)"), &dummy_tn, &csd->trans_hist.curr_tn);
}
/* if caller_is_wcs_recover, we would have waited for all writers to stop manipulating the active/wip queues
* and so it is ok to do the FAKE_DIRTY check. but otherwise it is not.
*/
if (caller_is_wcs_recover)
cstt->dirty = FAKE_DIRTY; /* change the flag to indicate it was found in a state queue */
if (0 == cstt->state_que.fl)
{ /* No point proceeding to next iteration as "cstt + cstt->state_que.fl" will be same as "cstt" */
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg), cstt, cstt->blk,
RTS_ERROR_TEXT("active queue.fl"), (UINTPTR_T)cstt->state_que.fl, (UINTPTR_T)-1);
break;
}
}
if (cnt == 0)
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg),
que_head, 0, RTS_ERROR_TEXT("active queue entries"), (UINTPTR_T)(n_bts + 1), (UINTPTR_T)n_bts);
} else if ((cstt == (cache_state_rec_ptr_t)que_head)
&& ((cache_state_rec_ptr_t)((sm_uc_ptr_t)cstt + cstt->state_que.bl) != cstt_prev))
{ /* at this point "cstt" is active que_head and its backlink does not point to last entry in active queue */
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg), cstt, 0, RTS_ERROR_TEXT("active queue base"),
(UINTPTR_T)cstt->state_que.bl, (sm_uc_ptr_t)cstt_prev - (sm_uc_ptr_t)cstt);
}
/* loop through the wip queue */
que_head = &csa->acc_meth.bg.cache_state->cacheq_wip;
if ((sm_long_t)que_head % SIZEOF(que_head->fl) != 0)
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg), que_head, 0, RTS_ERROR_TEXT("cacheq_wip"),
que_head, ((sm_long_t)que_head / SIZEOF(que_head->fl)) * SIZEOF(que_head->fl));
}
#ifdef VMS
for (cstt_prev = que_head, cstt = (cache_state_rec_ptr_t)((sm_uc_ptr_t)que_head + que_head->fl), cnt = n_bts;
(cstt != (cache_state_rec_ptr_t)que_head) && (cnt > 0);
cstt_prev = cstt, cnt--, cstt = (cache_state_rec_ptr_t)((sm_uc_ptr_t)cstt + cstt->state_que.fl))
{
cr = (cache_rec_ptr_t)((sm_uc_ptr_t)cstt - SIZEOF(cr->blkque));
if (CR_NOT_IN_RANGE((cache_rec_ptr_t)cr, cr_lo, cr_hi))
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(11) ERR_DBADDRANGE, 9, DB_LEN_STR(reg), que_head, -1,
cr, RTS_ERROR_TEXT("wip cstt->state_que"), cr_lo, cr_hi);
break;
}
if (CR_NOT_ALIGNED(cr, cr_lo))
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(11) ERR_DBADDRALIGN, 9, DB_LEN_STR(reg), que_head, -1,
RTS_ERROR_TEXT("wip cstt->state_que"), cr, cr_lo, SIZEOF(cache_rec));
break;
}
if ((cache_state_rec_ptr_t)((sm_uc_ptr_t)cstt + cstt->state_que.bl) != cstt_prev)
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg), cstt, cstt->blk,
RTS_ERROR_TEXT("wip queue.bl"), (UINTPTR_T)cstt->state_que.bl,
(sm_uc_ptr_t)cstt_prev - (sm_uc_ptr_t)cstt);
}
/* Secondary failure @ ipb - not yet determined if it was a legal state or a recover problem
* if (cstt->epid == 0)
* {
* assert(expect_damage);
* ret = FALSE;
* send_msg(VARLSTCNT(13) ERR_DBCRERR, 11, DB_LEN_STR(reg),
* cr, cstt->blk, RTS_ERROR_TEXT("wip cr->epid"), cstt->epid, -1, CALLFROM);
* }
*/
if (0 == cstt->dirty)
{
assert(expect_damage);
ret = FALSE;
dummy_tn = (trans_num)TRUE;
send_msg(VARLSTCNT(13) ERR_DBCRERR8, 11, DB_LEN_STR(reg), cr, cstt->blk,
RTS_ERROR_TEXT("wip cr->dirty"), &cstt->dirty, &dummy_tn, CALLFROM);
}
if (((0 != cstt->flushed_dirty_tn) && (cstt->dirty <= cstt->flushed_dirty_tn))
|| (cstt->dirty > csd->trans_hist.curr_tn))
{
assert(expect_damage);
ret = FALSE;
dummy_tn = cstt->flushed_dirty_tn + 1;
send_msg(VARLSTCNT(11) ERR_DBADDRANGE8, 9, DB_LEN_STR(reg), (int)cstt + SIZEOF(que_head), cstt->blk,
&cstt->dirty, RTS_ERROR_TEXT("wip dirty (tn)"), &dummy_tn, &csd->trans_hist.curr_tn);
}
/* if caller_is_wcs_recover, we would have waited for all writers to stop manipulating the active/wip queues
* and so it is ok to do the FAKE_DIRTY check. but otherwise it is not.
*/
if (caller_is_wcs_recover)
cstt->dirty = FAKE_DIRTY; /* change the flag to indicate it was found in a state queue */
if (0 == cstt->state_que.fl)
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg), cstt, cstt->blk,
RTS_ERROR_TEXT("wip queue.fl"), (UINTPTR_T)cstt->state_que.fl, (UINTPTR_T)-1);
break;
}
}
if (cnt == 0)
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg),
que_head, 0, RTS_ERROR_TEXT("wip queue entries"), (UINTPTR_T)(n_bts + 1), (UINTPTR_T)n_bts);
} else if ((cstt == (cache_state_rec_ptr_t)que_head)
&& ((cache_state_rec_ptr_t)((sm_uc_ptr_t)cstt + cstt->state_que.bl) != cstt_prev))
{ /* at this point "cstt" is wip que_head and its backlink does not point to last entry in the wip queue */
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg), cstt, 0, RTS_ERROR_TEXT("active queue base"),
(UINTPTR_T)cstt->state_que.bl, (sm_uc_ptr_t)cstt_prev - (sm_uc_ptr_t)cstt);
}
#else
if (que_head->fl != 0)
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg),
que_head, 0, RTS_ERROR_TEXT("wip queue head fl"), (UINTPTR_T)que_head->fl, (UINTPTR_T)0);
que_head->fl = 0;
}
if (que_head->bl != 0)
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg),
que_head, 0, RTS_ERROR_TEXT("wip queue head bl"), (UINTPTR_T)que_head->bl, (UINTPTR_T)0);
que_head->bl = 0;
}
#endif
/* if caller_is_wcs_recover, we would have waited for all writers to stop manipulating the active/wip queues
* and so it is ok to do the FAKE_DIRTY check. but otherwise it is not.
*/
if (caller_is_wcs_recover)
{ /* loop through the cache_recs again to look for lost dirties */
for (cr = cr_lo, cnt = n_bts; cnt > 0; cr++, cnt--)
{
if (cr->dirty == FAKE_DIRTY)
cr->dirty = cr->flushed_dirty_tn + 1;
else
{
if (0 != cr->dirty)
{
assert(expect_damage);
ret = FALSE;
dummy_tn = (trans_num)FALSE;
send_msg(VARLSTCNT(13) ERR_DBCRERR8, 11, DB_LEN_STR(reg), cr, cr->blk,
RTS_ERROR_TEXT("non-state cr->dirty"), &cr->dirty, &dummy_tn, CALLFROM);
}
}
}
}
} else
{
# if defined(UNIX) && !defined(UNTARGETED_MSYNC) && !defined(NO_MSYNC)
mbr_lo = (mmblk_rec_ptr_t)csa->acc_meth.mm.mmblk_state->mmblk_array + csd->bt_buckets;
mbr_hi = mbr_lo + n_bts;
/* loop through the mbr blkques */
for (mbr0 = (mmblk_rec_ptr_t)csa->acc_meth.mm.mmblk_state->mmblk_array, mbr_qbase = mbr0; mbr0 < mbr_lo; mbr0++)
{
if (mbr0->blk != BT_QUEHEAD)
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(13) ERR_DBCRERR, 11, DB_LEN_STR(reg), mbr0, mbr0->blk,
RTS_ERROR_TEXT("queue head mbr->blk"), mbr0->blk, BT_QUEHEAD, CALLFROM);
mbr0->blk = BT_QUEHEAD;
}
for (mbr_prev = mbr0, mbr = (mmblk_rec_ptr_t)((sm_uc_ptr_t)mbr0 + mbr0->blkque.fl), cnt = n_bts + 1;
(mbr != mbr0) && (cnt > 0);
mbr_prev = mbr, cnt--, mbr = (mmblk_rec_ptr_t)((sm_uc_ptr_t)mbr + mbr->blkque.fl))
{
if (MBR_NOT_IN_RANGE(mbr, mbr_lo, mbr_hi))
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(11) ERR_DBADDRANGE, 9, DB_LEN_STR(reg),
mbr0, -1, mbr, RTS_ERROR_TEXT("mbr->blkque"), mbr_lo, mbr_hi);
break;
}
if (MBR_NOT_ALIGNED(mbr, mbr_lo))
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(11) ERR_DBADDRALIGN, 9, DB_LEN_STR(reg),
mbr0, -1, RTS_ERROR_TEXT("mbr->blkque"), mbr, mbr_lo, SIZEOF(mmblk_rec));
break;
}
if ((mmblk_rec_ptr_t)((sm_uc_ptr_t)mbr + mbr->blkque.bl) != mbr_prev)
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg), mbr, mbr->blk,
RTS_ERROR_TEXT("mbr->blkque.bl"), (UINTPTR_T)mbr->blkque.bl,
(sm_uc_ptr_t)mbr_prev - (sm_uc_ptr_t)mbr);
}
if (((int)(mbr->blk) != MBR_BLKEMPTY) && ((mbr_qbase + (mbr->blk % csd->bt_buckets)) != mbr0))
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(13) ERR_DBCRERR, 11, DB_LEN_STR(reg), mbr, mbr->blk,
RTS_ERROR_TEXT("mbr hash"), mbr0 - mbr_qbase, mbr->blk % csd->bt_buckets,
CALLFROM);
}
(*blkque_array)[mbr - mbr_lo] = TRUE; /* note: mbr's blkque hash validity is already checked */
if (0 == mbr->blkque.fl)
{ /* Don't proceed to next iteration as "mbr + mbr->blkque.fl" will be the same as "mbr" */
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg), mbr, mbr->blk,
RTS_ERROR_TEXT("mbr->blkque.fl"), (UINTPTR_T)mbr->blkque.fl, (UINTPTR_T)-1);
break;
}
}
if (cnt == 0)
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg),
mbr_qbase, 0, RTS_ERROR_TEXT("mbrque entries"), (UINTPTR_T)(n_bts + 1),
(UINTPTR_T)(n_bts));
} else if ((mbr == mbr0) && ((mmblk_rec_ptr_t)((sm_uc_ptr_t)mbr + mbr->blkque.bl) != mbr_prev))
{ /* at this point "mbr" is mbr0 and its backlink does not point to last entry in the mbr0'th queue */
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg), mbr, mbr->blk,
RTS_ERROR_TEXT("mbrque mbr_base"), (UINTPTR_T)mbr->blkque.bl,
(sm_uc_ptr_t)mbr_prev - (sm_uc_ptr_t)mbr);
}
}
/* scan all mbrs looking for non-empty mbr->blks whose mbrs were not in any blkque */
for (mbr = mbr_lo; mbr < mbr_hi; mbr++)
{
if ((FALSE == (*blkque_array)[mbr - mbr_lo]) && ((int)(mbr->blk) != MBR_BLKEMPTY))
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(13) ERR_DBCRERR, 11, DB_LEN_STR(reg), mbr, mbr->blk,
RTS_ERROR_TEXT("mbr blkque hash"), -1, mbr->blk % csd->bt_buckets, CALLFROM);
}
}
que_head = &csa->acc_meth.mm.mmblk_state->mmblkq_active;
if ((sm_long_t)que_head % SIZEOF(que_head->fl) != 0)
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg), que_head, 0, RTS_ERROR_TEXT("mmblkq_active"),
que_head, ((sm_long_t)que_head / SIZEOF(que_head->fl)) * SIZEOF(que_head->fl));
}
/* loop through the active queue */
for (mbstt_prev = (mmblk_state_rec_ptr_t)que_head,
mbstt = (mmblk_state_rec_ptr_t)((sm_uc_ptr_t)que_head + que_head->fl), cnt = n_bts;
(mbstt != (mmblk_state_rec_ptr_t)que_head) && (cnt > 0);
mbstt_prev = mbstt, cnt--, mbstt = (mmblk_state_rec_ptr_t)((sm_uc_ptr_t)mbstt + mbstt->state_que.fl))
{
mbr = (mmblk_rec_ptr_t)((sm_uc_ptr_t)mbstt - SIZEOF(mbr->blkque));
if (MBR_NOT_IN_RANGE((mmblk_rec_ptr_t)mbr, mbr_lo, mbr_hi))
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(11) ERR_DBADDRANGE, 9, DB_LEN_STR(reg), que_head, -1,
mbr, RTS_ERROR_TEXT("active mbstt->state_que"), mbr_lo, mbr_hi);
break;
}
if (MBR_NOT_ALIGNED(mbr, mbr_lo))
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(11) ERR_DBADDRALIGN, 9, DB_LEN_STR(reg), que_head, -1,
RTS_ERROR_TEXT("active mbstt->state_que"), mbr, mbr_lo, SIZEOF(mmblk_rec));
break;
}
if ((mmblk_state_rec_ptr_t)((sm_uc_ptr_t)mbstt + mbstt->state_que.bl) != mbstt_prev)
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg), mbstt, mbstt->blk,
RTS_ERROR_TEXT("active queue.bl"), (UINTPTR_T)mbstt->state_que.bl,
(sm_uc_ptr_t)mbstt_prev - (sm_uc_ptr_t)mbstt);
}
if (0 == mbstt->dirty)
{
assert(expect_damage);
ret = FALSE;
dummy_tn = (trans_num)TRUE;
send_msg(VARLSTCNT(13) ERR_DBCRERR8, 11, DB_LEN_STR(reg), mbr, mbstt->blk,
RTS_ERROR_TEXT("active mbr->dirty"), &mbstt->dirty, &dummy_tn, CALLFROM);
}
/* if caller_is_wcs_recover, we would have waited for all writers to stop manipulating the active/wip queues
* and so it is ok to do the FAKE_DIRTY check. but otherwise it is not.
*/
if (caller_is_wcs_recover)
mbstt->dirty = FAKE_DIRTY; /* change the flag to indicate it was found in a state queue */
if (0 == mbstt->state_que.fl)
{ /* No point proceeding to next iteration as "mbstt + mbstt->state_que.fl" will be same as "mbstt" */
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg), mbstt, mbstt->blk,
RTS_ERROR_TEXT("active queue.fl"), (UINTPTR_T)mbstt->state_que.fl, (UINTPTR_T)-1);
break;
}
}
if (cnt == 0)
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg),
que_head, 0, RTS_ERROR_TEXT("active queue entries"), (UINTPTR_T)(n_bts + 1), (UINTPTR_T)n_bts);
} else if ((mbstt == (mmblk_state_rec_ptr_t)que_head)
&& ((mmblk_state_rec_ptr_t)((sm_uc_ptr_t)mbstt + mbstt->state_que.bl) != mbstt_prev))
{ /* at this point "mbstt" is active que_head and its backlink does not point to last entry in active queue */
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg), mbstt, 0, RTS_ERROR_TEXT("active queue base"),
(UINTPTR_T)mbstt->state_que.bl, (sm_uc_ptr_t)mbstt_prev - (sm_uc_ptr_t)mbstt);
}
/* loop through the wip queue */
que_head = &csa->acc_meth.mm.mmblk_state->mmblkq_wip;
if ((sm_long_t)que_head % SIZEOF(que_head->fl) != 0)
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg), que_head, 0, RTS_ERROR_TEXT("mmblkq_wip"),
que_head, ((sm_long_t)que_head / SIZEOF(que_head->fl)) * SIZEOF(que_head->fl));
}
if (que_head->fl != 0)
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg),
que_head, 0, RTS_ERROR_TEXT("wip queue head fl"), (UINTPTR_T)que_head->fl, (UINTPTR_T)0);
que_head->fl = 0;
}
if (que_head->bl != 0)
{
assert(expect_damage);
ret = FALSE;
send_msg(VARLSTCNT(10) ERR_DBQUELINK, 8, DB_LEN_STR(reg),
que_head, 0, RTS_ERROR_TEXT("wip queue head bl"), (UINTPTR_T)que_head->bl, (UINTPTR_T)0);
que_head->bl = 0;
}
/* if caller_is_wcs_recover, we would have waited for all writers to stop manipulating the active/wip queues
* and so it is ok to do the FAKE_DIRTY check. but otherwise it is not.
*/
if (caller_is_wcs_recover)
{ /* loop through the mmblk_recs again to look for lost dirties */
for (mbr = mbr_lo, cnt = n_bts; cnt > 0; mbr++, cnt--)
{
if (0 != mbr->dirty)
{
assert(expect_damage);
ret = FALSE;
dummy_tn = (trans_num)FALSE;
send_msg(VARLSTCNT(13) ERR_DBCRERR8, 11, DB_LEN_STR(reg), mbr, mbr->blk,
RTS_ERROR_TEXT("non-state mbr->dirty"), &mbr->dirty, &dummy_tn, CALLFROM);
}
}
}
# endif
}
send_msg(VARLSTCNT(7) ERR_DBWCVERIFYEND, 5, DB_LEN_STR(reg), process_id, process_id, &csd->trans_hist.curr_tn);
if (NULL != blkque_array) free(blkque_array);
return ret;
}