fis-gtm/sr_port/dse_chng_bhead.c

262 lines
9.0 KiB
C

/****************************************************************
* *
* Copyright 2001, 2013 Fidelity Information Services, Inc *
* *
* This source code contains the intellectual property *
* of its copyright holder(s), and is made available *
* under a license. If you do not know the terms of *
* the license, please stop and do not read further. *
* *
****************************************************************/
#include "mdef.h"
#include "gtm_time.h"
#include "gdsroot.h"
#include "gdskill.h"
#include "gtm_facility.h"
#include "fileinfo.h"
#include "gdsbt.h"
#include "gdsfhead.h"
#include "gdsblk.h"
#include "gdsbml.h"
#include "min_max.h" /* needed for gdsblkops.h */
#include "gdsblkops.h"
#include "gdscc.h"
#include "filestruct.h"
#include "jnl.h"
#include "buddy_list.h" /* needed for tp.h */
#include "hashtab_int4.h" /* needed for tp.h */
#include "tp.h"
#include "cli.h"
#include "send_msg.h"
#include "dse.h"
/* Include prototypes */
#include "t_qread.h"
#include "t_write.h"
#include "t_end.h"
#include "t_begin_crit.h"
#include "process_deferred_stale.h"
#include "util.h"
#include "t_abort.h"
#include "gvcst_blk_build.h" /* for the BUILD_AIMG_IF_JNL_ENABLED macro */
#ifdef GTM_CRYPT
#include "gtmcrypt.h"
#endif
GBLREF char *update_array, *update_array_ptr;
GBLREF uint4 update_array_size;
GBLREF srch_hist dummy_hist;
GBLREF sgmnt_addrs *cs_addrs;
GBLREF sgmnt_data_ptr_t cs_data;
GBLREF block_id patch_curr_blk;
GBLREF gd_region *gv_cur_region;
GBLREF gd_addr *gd_header;
GBLREF cache_rec *cr_array[((MAX_BT_DEPTH * 2) - 1) * 2]; /* Maximum number of blocks that can be in transaction */
GBLREF boolean_t unhandled_stale_timer_pop;
GBLREF cw_set_element cw_set[];
error_def(ERR_DSEBLKRDFAIL);
error_def(ERR_DSEFAIL);
error_def(ERR_DBRDONLY);
void dse_chng_bhead(void)
{
block_id blk;
int4 x;
trans_num tn;
cache_rec_ptr_t cr;
blk_hdr new_hdr;
blk_segment *bs1, *bs_ptr;
int4 blk_seg_cnt, blk_size; /* needed for BLK_INIT,BLK_SEG and BLK_FINI macros */
boolean_t ismap;
boolean_t chng_blk;
boolean_t was_crit;
boolean_t was_hold_onto_crit;
uint4 mapsize;
srch_blk_status blkhist;
sgmnt_addrs *csa;
sgmnt_data_ptr_t csd;
# ifdef GTM_CRYPT
int req_enc_blk_size;
int gtmcrypt_errno;
blk_hdr_ptr_t bp, save_bp, save_old_block;
gd_segment *seg;
# endif
if (gv_cur_region->read_only)
rts_error(VARLSTCNT(4) ERR_DBRDONLY, 2, DB_LEN_STR(gv_cur_region));
CHECK_AND_RESET_UPDATE_ARRAY; /* reset update_array_ptr to update_array */
chng_blk = FALSE;
csa = cs_addrs;
if (cli_present("BLOCK") == CLI_PRESENT)
{
if (!cli_get_hex("BLOCK", (uint4 *)&blk))
return;
if (blk < 0 || blk > csa->ti->total_blks)
{
util_out_print("Error: invalid block number.", TRUE);
return;
}
patch_curr_blk = blk;
}
csd = csa->hdr;
assert(csd == cs_data);
blk_size = csd->blk_size;
ismap = (patch_curr_blk / csd->bplmap * csd->bplmap == patch_curr_blk);
mapsize = BM_SIZE(csd->bplmap);
t_begin_crit(ERR_DSEFAIL);
blkhist.blk_num = patch_curr_blk;
if (!(blkhist.buffaddr = t_qread(blkhist.blk_num, &blkhist.cycle, &blkhist.cr)))
rts_error(VARLSTCNT(1) ERR_DSEBLKRDFAIL);
new_hdr = *(blk_hdr_ptr_t)blkhist.buffaddr;
if (cli_present("LEVEL") == CLI_PRESENT)
{
if (!cli_get_hex("LEVEL", (uint4 *)&x))
{
t_abort(gv_cur_region, csa);
return;
}
if (ismap && (unsigned char)x != LCL_MAP_LEVL)
{
util_out_print("Error: invalid level for a bit map block.", TRUE);
t_abort(gv_cur_region, csa);
return;
}
if (!ismap && (x < 0 || x > MAX_BT_DEPTH + 1))
{
util_out_print("Error: invalid level.", TRUE);
t_abort(gv_cur_region, csa);
return;
}
new_hdr.levl = (unsigned char)x;
chng_blk = TRUE;
if (new_hdr.bsiz < SIZEOF(blk_hdr))
new_hdr.bsiz = SIZEOF(blk_hdr);
if (new_hdr.bsiz > blk_size)
new_hdr.bsiz = blk_size;
}
if (cli_present("BSIZ") == CLI_PRESENT)
{
if (!cli_get_hex("BSIZ", (uint4 *)&x))
{
t_abort(gv_cur_region, csa);
return;
}
if (ismap && x != mapsize)
{
util_out_print("Error: invalid bsiz.", TRUE);
t_abort(gv_cur_region, csa);
return;
} else if (x < SIZEOF(blk_hdr) || x > blk_size)
{
util_out_print("Error: invalid bsiz.", TRUE);
t_abort(gv_cur_region, csa);
return;
}
chng_blk = TRUE;
new_hdr.bsiz = x;
}
if (!chng_blk)
t_abort(gv_cur_region, csa);
else
{
BLK_INIT(bs_ptr, bs1);
BLK_SEG(bs_ptr, blkhist.buffaddr + SIZEOF(new_hdr), new_hdr.bsiz - SIZEOF(new_hdr));
if (!BLK_FINI(bs_ptr, bs1))
{
util_out_print("Error: bad block build.", TRUE);
t_abort(gv_cur_region, csa);
return;
}
t_write(&blkhist, (unsigned char *)bs1, 0, 0, new_hdr.levl, TRUE, FALSE, GDS_WRITE_KILLTN);
BUILD_AIMG_IF_JNL_ENABLED(csd, csa->ti->curr_tn);
t_end(&dummy_hist, NULL, TN_NOT_SPECIFIED);
}
if (cli_present("TN") == CLI_PRESENT)
{
if (!cli_get_hex64("TN", &tn))
return;
was_crit = csa->now_crit;
t_begin_crit(ERR_DSEFAIL);
CHECK_TN(csa, csd, csd->trans_hist.curr_tn); /* can issue rts_error TNTOOLARGE */
assert(csa->ti->early_tn == csa->ti->curr_tn);
if (NULL == (blkhist.buffaddr = t_qread(blkhist.blk_num, &blkhist.cycle, &blkhist.cr)))
{
util_out_print("Error: Unable to read buffer.", TRUE);
t_abort(gv_cur_region, csa);
return;
}
if (new_hdr.bsiz < SIZEOF(blk_hdr))
new_hdr.bsiz = SIZEOF(blk_hdr);
if (new_hdr.bsiz > blk_size)
new_hdr.bsiz = blk_size;
BLK_INIT(bs_ptr, bs1);
BLK_SEG(bs_ptr, blkhist.buffaddr + SIZEOF(new_hdr), new_hdr.bsiz - SIZEOF(new_hdr));
BLK_FINI(bs_ptr, bs1);
t_write(&blkhist, (unsigned char *)bs1, 0, 0,
((blk_hdr_ptr_t)blkhist.buffaddr)->levl, TRUE, FALSE, GDS_WRITE_KILLTN);
/* Pass the desired tn as argument to bg_update/mm_update below */
BUILD_AIMG_IF_JNL_ENABLED(csd, tn);
was_hold_onto_crit = csa->hold_onto_crit;
csa->hold_onto_crit = TRUE; /* need this so t_end doesn't release crit (see below comment for why) */
t_end(&dummy_hist, NULL, tn);
# ifdef GTM_CRYPT
if (csd->is_encrypted && (tn < csa->ti->curr_tn))
{ /* BG and db encryption is enabled and the DSE update caused the block-header to potentially have a tn
* that is LESS than what it had before. At this point, the global buffer (corresponding to blkhist.blk_num)
* reflects the contents of the block AFTER the dse update (bg_update would have touched this) whereas
* the corresponding encryption global buffer reflects the contents of the block BEFORE the update.
* Normally wcs_wtstart takes care of propagating the tn update from the regular global buffer to the
* corresponding encryption buffer. But if before it gets a chance, let us say a process goes to t_end
* as part of a subsequent transaction and updates this same block. Since the blk-hdr-tn potentially
* decreased, it is possible that the PBLK writing check (comparing blk-hdr-tn with the epoch_tn) decides
* to write a PBLK for this block (even though a PBLK was already written for this block as part of a
* previous DSE CHANGE -BL -TN in the same epoch). In this case, since the db is encrypted, the logic
* will assume there were no updates to this block since the last time wcs_wtstart updated the encryption
* buffer and therefore use that to write the pblk, which is incorrect since it does not yet contain the
* tn update. The consequence of this is would be writing an older before-image PBLK) record to the
* journal file. To prevent this situation, we update the encryption buffer here (before releasing crit)
* using logic like that in wcs_wtstart to ensure it is in sync with the regular global buffer. To ensure
* that t_end doesn't release crit, we set csa->hold_onto_crit to TRUE
* Note:
* Although we use cw_set[0] to access the global buffer corresponding to the block number being updated,
* cw_set_depth at this point is 0 because t_end resets it. This is considered safe since cw_set is a
* static array (as opposed to malloc'ed memory) and hence is always available and valid until it gets
* overwritten by subsequent updates.
*/
bp = (blk_hdr_ptr_t)GDS_ANY_REL2ABS(csa, cw_set[0].cr->buffaddr);
DBG_ENSURE_PTR_IS_VALID_GLOBUFF(csa, csd, (sm_uc_ptr_t)bp);
save_bp = (blk_hdr_ptr_t)GDS_ANY_ENCRYPTGLOBUF(bp, csa);
DBG_ENSURE_PTR_IS_VALID_ENCTWINGLOBUFF(csa, csd, (sm_uc_ptr_t)save_bp);
assert((bp->bsiz <= csd->blk_size) && (bp->bsiz >= SIZEOF(*bp)));
req_enc_blk_size = MIN(csd->blk_size, bp->bsiz) - SIZEOF(*bp);
if (BLK_NEEDS_ENCRYPTION(bp->levl, req_enc_blk_size))
{
ASSERT_ENCRYPTION_INITIALIZED;
memcpy(save_bp, bp, SIZEOF(blk_hdr));
GTMCRYPT_ENCRYPT(csa, csa->encr_key_handle, (char *)(bp + 1), req_enc_blk_size,
(char *)(save_bp + 1), gtmcrypt_errno);
if (0 != gtmcrypt_errno)
{
seg = gv_cur_region->dyn.addr;
GTMCRYPT_REPORT_ERROR(gtmcrypt_errno, gtm_putmsg, seg->fname_len, seg->fname);
}
} else
memcpy(save_bp, bp, bp->bsiz);
}
# endif
csa->hold_onto_crit = was_hold_onto_crit;
if (!was_crit)
rel_crit(gv_cur_region);
if (unhandled_stale_timer_pop)
process_deferred_stale();
}
return;
}