fis-gtm/sr_unix/ss_release.c

227 lines
7.6 KiB
C

/****************************************************************
* *
* Copyright 2009, 2012 Fidelity Information Services, Inc *
* *
* This source code contains the intellectual property *
* of its copyright holder(s), and is made available *
* under a license. If you do not know the terms of *
* the license, please stop and do not read further. *
* *
****************************************************************/
#include "mdef.h"
#include "gtm_fcntl.h"
#include "gtm_stat.h"
#include "gtm_unistd.h"
#include <sys/mman.h>
#include <sys/shm.h>
#include "gtm_permissions.h"
#include "gtm_string.h"
#include "gtm_stdio.h"
#include "gtm_stdlib.h"
#include "gtm_tempnam.h"
#include "gtm_time.h"
#include "gdsroot.h"
#include "gdskill.h"
#include "gtm_facility.h"
#include "fileinfo.h"
#include "gdsbt.h"
#include "gdsblk.h"
#include "gdsfhead.h"
#include "filestruct.h"
#include "iosp.h"
#include "error.h"
#include "cli.h"
#include "eintr_wrappers.h"
#include "gtmio.h"
#include "gtm_file_stat.h"
#include "gtmimagename.h"
#include "interlock.h"
#include "wcs_phase2_commit_wait.h"
#include "util.h"
#include "ipcrmid.h"
#include "shmpool.h"
#include "db_snapshot.h"
#include "ss_lock_facility.h"
GBLREF sgmnt_addrs *cs_addrs;
GBLREF sgmnt_data *cs_data;
GBLREF gd_region *gv_cur_region;
GBLREF uint4 process_id;
GBLREF enum gtmImageTypes image_type;
error_def(ERR_COMMITWAITSTUCK);
error_def(ERR_SYSCALL);
error_def(ERR_SSFILCLNUPFAIL);
error_def(ERR_SSSHMCLNUPFAIL);
#define CLEANUP_SHADOW_FILE(preserve_snapshot, shadow_file) \
{ \
int status; \
struct stat stat_buf; \
int save_errno; \
\
if (!preserve_snapshot && (-1 != stat(shadow_file, &stat_buf))) \
{ \
status = UNLINK(shadow_file); \
if (0 != status) \
{ \
save_errno = errno; \
if (IS_MUMPS_IMAGE) \
send_msg(VARLSTCNT(5) ERR_SSFILCLNUPFAIL, 2, LEN_AND_STR(shadow_file), save_errno); \
else \
gtm_putmsg(VARLSTCNT(5) ERR_SSFILCLNUPFAIL, 2, LEN_AND_STR(shadow_file), save_errno); \
} \
} \
} \
#define CLEANUP_SHARED_MEMORY(ss_shmid) \
{ \
int save_errno; \
struct shmid_ds ss_shmstat; \
\
if ((INVALID_SHMID != ss_shmid) && (0 == shmctl((int)ss_shmid, IPC_STAT, &ss_shmstat))) \
{ \
if (0 != shmctl((int)ss_shmid, IPC_RMID, &ss_shmstat)) \
{ \
save_errno = errno; \
if (IS_MUMPS_IMAGE) \
send_msg(VARLSTCNT(6) ERR_SSSHMCLNUPFAIL, 3, LEN_AND_LIT("shmctl"), ss_shmid, save_errno); \
else \
gtm_putmsg(VARLSTCNT(6) ERR_SSSHMCLNUPFAIL, 3, LEN_AND_LIT("shmctl"), ss_shmid, save_errno); \
} \
} \
} \
#define ADJUST_SHARED_MEMORY_FIELDS(cnl, ss_shm_ptr) \
{ \
if (0 < cnl->num_snapshots_in_effect) \
cnl->num_snapshots_in_effect--; \
if (0 == cnl->num_snapshots_in_effect) \
{ \
CLEAR_SNAPSHOTS_IN_PROG(cnl); \
cnl->ss_shmid = INVALID_SHMID; \
} \
SS_DEFAULT_INIT_POOL(ss_shm_ptr); \
cnl->ss_shmcycle++; \
}
void ss_release(snapshot_context_ptr_t *ss_ctx)
{
int status, ss_shmsize;
long ss_shmid;
boolean_t preserve_snapshot = FALSE;
sgmnt_addrs *csa;
DEBUG_ONLY(sgmnt_data *csd;)
node_local_ptr_t cnl;
shm_snapshot_ptr_t ss_shm_ptr;
snapshot_context_ptr_t lcl_ss_ctx;
boolean_t was_crit;
struct shmid_ds ss_shmstat;
struct stat stat_buf;
char shadow_file[GTM_PATH_MAX];
ss_proc_status cur_state;
assert(NULL != cs_addrs && (NULL != gv_cur_region));
csa = cs_addrs;
cnl = csa->nl;
was_crit = csa->now_crit;
DEBUG_ONLY(csd = csa->hdr;)
if (NULL == ss_ctx) /* orphaned cleanup */
{
assert(ss_lock_held_by_us(gv_cur_region));
ss_shm_ptr = (shm_snapshot_ptr_t)(SS_GETSTARTPTR(csa));
DBG_ENSURE_PTR_WITHIN_SS_BOUNDS(csa, (sm_uc_ptr_t)ss_shm_ptr);
ss_shmid = ss_shm_ptr->ss_info.ss_shmid;
CLEANUP_SHADOW_FILE(FALSE, ss_shm_ptr->ss_info.shadow_file);
CLEANUP_SHARED_MEMORY(ss_shmid);
ADJUST_SHARED_MEMORY_FIELDS(cnl, ss_shm_ptr);
return;
}
assert(NULL != *ss_ctx);
lcl_ss_ctx = *ss_ctx;
cur_state = lcl_ss_ctx->cur_state;
if (SNAPSHOT_INIT_DONE == cur_state)
{
/* Ensure that we don't try to grab_crit and wait for Phase2 commits to complete if we are dying */
assert(!process_exiting);
ss_shm_ptr = lcl_ss_ctx->ss_shm_ptr;
DBG_ENSURE_PTR_WITHIN_SS_BOUNDS(csa, (sm_uc_ptr_t)ss_shm_ptr);
ss_shmsize = ss_shm_ptr->ss_info.ss_shmsize;
assert(SNAPSHOTS_IN_PROG(cnl));
assert(ss_shm_ptr->in_use);
preserve_snapshot = ss_shm_ptr->preserve_snapshot;
/* Irrespective of whether there was an error or not, announce GT.M that it should now stop writing before images.
* After rel_crit below any process that enters transaction logic will either see cnl->snapshot_in_prog set to FALSE
* or a change in cnl->ss_shmid and cnl->ss_shmcycle in which case it will restart itself in t_end or tp_tend.
*/
if (!was_crit)
grab_crit(gv_cur_region);
assert(cs_data == cs_addrs->hdr);
if (dba_bg == cs_data->acc_meth)
{
/* Now that we have crit, wait for any pending phase2 updates to finish. Since phase2 updates happen
* outside of crit, we dont want them to keep writing to the snapshot file even after the snapshot
* is complete. This is needed as otherwise a GT.M process might see the value of csa->snapshot_in_prog
* as TRUE and before it can proceed any further(starvation, maybe), we went ahead and removed the
* snapshot file(below). Now, if the GT.M process resumes execution, it might end up writing
* the before image to a temporary file which is no longer available.
*/
if (cnl->wcs_phase2_commit_pidcnt && !wcs_phase2_commit_wait(csa, NULL))
{
assert(FALSE);
gtm_putmsg(VARLSTCNT(7) ERR_COMMITWAITSTUCK, 5, process_id, 1,
cnl->wcs_phase2_commit_pidcnt, DB_LEN_STR(gv_cur_region));
}
}
ADJUST_SHARED_MEMORY_FIELDS(cnl, ss_shm_ptr);
if (!was_crit)
rel_crit(gv_cur_region);
if (preserve_snapshot)
{
assert(lcl_ss_ctx->shadow_vbn >= csd->start_vbn);
assert(ss_shmsize == (lcl_ss_ctx->shadow_vbn - csd->start_vbn) * DISK_BLOCK_SIZE);
LSEEKWRITE(lcl_ss_ctx->shdw_fd, 0, (sm_uc_ptr_t)(lcl_ss_ctx->start_shmaddr), ss_shmsize, status);
if (0 != status)
{
util_out_print("!/Failed while writing the snapshot file header. ",
TRUE);
assert(FALSE);
}
}
}
STRCPY(shadow_file, lcl_ss_ctx->shadow_file);
ss_shmid = lcl_ss_ctx->attach_shmid;
ss_destroy_context(lcl_ss_ctx);
/* Do cleanup depending on what state of the snapshot init we are in */
switch(cur_state)
{
case SNAPSHOT_INIT_DONE:
case AFTER_SHM_CREAT:
/* Remove shared memory identifier. Note, because of a race condition, it is possible that some GT.M
* process is still attached to this identifier. In such a case, the identifier will be marked to be
* delted by the OS until number of processes attached to this shared segment becomes zero. GT.M
* at the end of each transaction, will try to detach itself from this shared memory segment and will
* eventually lead to number of attached processes becoming zero.*/
CLEANUP_SHARED_MEMORY(ss_shmid);
/* intentional fall through */
case AFTER_SHADOW_FIL_CREAT:
CLEANUP_SHADOW_FILE(preserve_snapshot, shadow_file);
/* intentional fall through */
case BEFORE_SHADOW_FIL_CREAT:
if (NULL != *ss_ctx)
{
free(*ss_ctx);
*ss_ctx = NULL;
}
break;
default:
assert(FALSE);
GTMASSERT;
}
return;
}