1330 lines
53 KiB
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;
|
|
}
|