fis-gtm/sr_unix/rc_cpt_ops.c

607 lines
14 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. *
* *
****************************************************************/
#include "mdef.h"
#include "gtm_string.h"
#include "gtm_stdio.h"
#include "gtm_ipc.h"
#include <sys/sem.h>
#include <sys/shm.h>
#include <errno.h>
#include "iosp.h"
#include "rc_cpt.h"
#include "gdsroot.h"
#include "gtm_facility.h"
#include "fileinfo.h"
#include "gdsbt.h"
#include "gdsfhead.h"
#include "filestruct.h"
#include "io.h"
#include "copy.h"
#include "rc.h"
#include "gtm_c_stack_trace.h"
#include "eintr_wrappers.h"
#include "eintr_wrapper_semop.h"
#include "rc_cpt_ops.h"
#include "do_shmat.h"
#include "trans_log_name.h"
GBLDEF trans_num rc_read_stamp;
GBLREF sgmnt_data_ptr_t cs_data;
GBLREF sgmnt_addrs *cs_addrs;
GBLREF gd_region *gv_cur_region;
GBLREF bool rc_locked;
error_def(ERR_DBFILERR);
error_def(ERR_TEXT);
static int rc_shmid = INVALID_SHMID, rc_sem = INVALID_SEMID;
static rc_cp_table *rc_cpt = 0;
static int rc_init_ipc(void);
static void rc_cpt_unlock(void);
static void rc_cpt_lock(void);
#define HIGH_WORD_SHIFT 0x00010000
#define HIGH_4BIT_SHIFT 0x10000000
#define LOW_WORD_MASK 0x0000FFFF
#define LOW_BYTE_MASK 0x000000FF
#define HIGH_BYTE_MASK 0x0000FF00
int rc_cpt_entry(int blk)
{
key_t rc_key;
int4 entry;
int i;
mstr fpath1, fpath2;
struct sembuf sop[2];
bool found;
# ifdef DEBUG_CPT
FPRINTF(stderr,"\trc_cpt_entry(%d)",blk);
# endif
/* test any existing RC semaphore first */
if (rc_sem)
{
errno = 0;
i = semctl(rc_sem,0,GETVAL);
if (errno) /* invalid semaphore */
{
rc_sem = 0;
/* detach shared memory segment as well */
if (rc_cpt)
{
(void) shmdt((char *)rc_cpt);
rc_cpt = NULL;
}
}
}
if (!rc_cpt)
{
if (i = rc_init_ipc())
return i; /* return error code (errno) */
}
/* cpvfy is the value of cpsync when the CPT table was last sent to a client.
* If this block has already been entered since then, do not reenter.
*/
/* entry = (((cs_data->dsid / 256) * 9) % LOW_BYTE_MASK) + (blk % LOW_WORD_MASK) + (cs_data->rc_node * HIGH_WORD_SHIFT); */
entry = ((cs_data->dsid / 256 * 9 + blk) & LOW_BYTE_MASK) + (blk & HIGH_BYTE_MASK) + (cs_data->rc_node * HIGH_WORD_SHIFT);
found = FALSE;
rc_cpt_lock();
i = rc_cpt->index - (rc_cpt->cpsync - rc_cpt->cpvfy);
while (i < 0)
i += RC_CPT_TABSIZE;
for (; i < RC_CPT_TABSIZE; i++)
{
if (i == rc_cpt->index)
break;
if (rc_cpt->ring_buff[i] == entry)
{
found = TRUE;
break;
}
}
if (i == RC_CPT_TABSIZE)
{
for (i = 0; i < rc_cpt->index; i++)
{
if (rc_cpt->ring_buff[i] == entry)
{
found = TRUE;
break;
}
}
}
# ifdef DEBUG_CPT
if (found)
FPRINTF(stderr," exists");
else
FPRINTF(stderr," add");
# endif
if (!found)
{
rc_cpt->ring_buff[rc_cpt->index++] = entry;
rc_cpt->cpsync++;
}
if (rc_cpt->index == RC_CPT_TABSIZE)
rc_cpt->index = 0;
rc_cpt_unlock();
# ifdef DEBUG_CPT
FPRINTF(stderr,"\n");
# endif
return 0;
}
/* set up shared memory and semaphore segments, clearing away any existing RC IPC */
static int rc_init_ipc(void)
{
key_t rc_key;
mstr fpath1, fpath2;
int old_errno;
char buff[1024];
/* RC semaphores are automatically cleared by shmclean, but it
* is necessary to clear any previously existing shared memory.
*/
if (rc_cpt)
{
(void) shmdt((char *)rc_cpt);
rc_cpt = NULL;
}
fpath1.addr = RC_CPT_PATH;
fpath1.len = SIZEOF(RC_CPT_PATH);
if (SS_NORMAL != TRANS_LOG_NAME(&fpath1, &fpath2, buff, SIZEOF(buff), do_sendmsg_on_log2long))
{
PERROR("Error translating rc path");
return errno;
}
if ((rc_key = FTOK(fpath2.addr,GTM_ID)) == -1)
{
PERROR("Error with rc ftok");
return errno;
}
if ((rc_shmid = shmget(rc_key, SIZEOF(rc_cp_table) , RWDALL)) == -1)
{
rc_shmid = INVALID_SHMID;
PERROR("Error with rc shmget");
return errno;
}
if ((INTPTR_T)(rc_cpt = (rc_cp_table*)do_shmat(rc_shmid, 0, 0)) == -1L)
{
PERROR("Error with rc shmat");
return errno;
}
if ((rc_sem = semget(rc_key, 4, RWDALL)) == -1)
{
old_errno = errno;
PERROR("Error with rc semget");
(void) shmdt((char *)rc_cpt);
rc_cpt = 0;
rc_sem = INVALID_SEMID;
return old_errno;
}
return 0;
}
static void rc_cpt_unlock(void)
{ struct sembuf sop[2];
int rv;
int semop_rv;
sop[0].sem_num = 0;
sop[0].sem_op = -1;
sop[0].sem_flg = SEM_UNDO;
rc_locked = FALSE;
SEMOP(rc_sem, sop, 1, semop_rv, NO_WAIT);
if (-1 == semop_rv)
{
if (errno == EINVAL)
{ /* try reinitializing semaphore... */
if (!(rv=rc_init_ipc()))
{
SEMOP(rc_sem, sop, 1, semop_rv, NO_WAIT);
if (-1 != semop_rv)
{
rc_locked = FALSE;
return;
}
}
}
if (gv_cur_region)
rts_error(VARLSTCNT(9) ERR_DBFILERR,2, DB_LEN_STR(gv_cur_region),
ERR_TEXT, 2, LEN_AND_LIT("Error with rc semaphore unlock"), errno);
else
rts_error(VARLSTCNT(5) ERR_TEXT, 2, LEN_AND_LIT("Error with rc semaphore unlock"), errno);
}
return;
}
static void rc_cpt_lock(void)
{
struct sembuf sop[2];
int rv;
int semop_rv;
/* WARNING: To prevent deadlocks, never attempt to acquire a database critical section while holding this semaphore */
sop[0].sem_num = sop[1].sem_num = 0;
sop[0].sem_op = 0;
sop[1].sem_op = 1;
sop[0].sem_flg = sop[1].sem_flg = SEM_UNDO;
SEMOP(rc_sem, sop, 2, semop_rv, FORCED_WAIT)
if (-1 == semop_rv)
{
if (errno == EINVAL)
{ /* try reinitializing semaphore... */
if (!(rv=rc_init_ipc()))
{
SEMOP(rc_sem, sop, 2, semop_rv, FORCED_WAIT);
if (-1 != semop_rv)
{
rc_locked = TRUE;
return;
}
}
}
if (gv_cur_region)
rts_error(VARLSTCNT(9) ERR_DBFILERR,2, DB_LEN_STR(gv_cur_region),
ERR_TEXT, 2, LEN_AND_LIT("Error with rc semaphore lock"), errno);
else
rts_error(VARLSTCNT(5) ERR_TEXT, 2, LEN_AND_LIT("Error with rc semaphore lock"), errno);
}
rc_locked = TRUE;
return;
}
int rc_cpt_inval(void)
{
key_t rc_key;
mstr fpath1, fpath2;
int4 entry;
struct sembuf sop[2];
int i, old_errno;
char buff[1024];
bool found;
# ifdef DEBUG_CPT
FPRINTF(stderr,"\trc_cpt_inval()\n");
# endif
if (!rc_cpt)
{ fpath1.addr = RC_CPT_PATH;
fpath1.len = SIZEOF(RC_CPT_PATH);
if (SS_NORMAL != TRANS_LOG_NAME(&fpath1, &fpath2, buff, SIZEOF(buff), do_sendmsg_on_log2long))
{
PERROR("Error translating rc path");
return errno;
}
if ((rc_key = FTOK(fpath2.addr,GTM_ID)) == -1)
{
PERROR("Error with rc ftok");
return errno;
}
if ((rc_shmid = shmget(rc_key, SIZEOF(rc_cp_table) , RWDALL)) == -1)
{
rc_shmid = INVALID_SHMID;
PERROR("Error with rc shmget");
return errno;
}
if ((INTPTR_T)(rc_cpt = (rc_cp_table*)do_shmat(rc_shmid, 0, 0)) == -1L)
{
PERROR("Error with rc shmat");
return errno;
}
if ((rc_sem = semget(rc_key, 4, RWDALL)) == -1)
{
old_errno = errno;
PERROR("Error with rc semget");
(void) shmdt((char *)rc_cpt);
rc_cpt = 0;
rc_sem = INVALID_SEMID;
return old_errno;
}
}
rc_cpt_lock();
entry = cs_data->dsid + (cs_data->rc_node * HIGH_WORD_SHIFT) + (RC_CPT_INVAL * HIGH_4BIT_SHIFT);
rc_cpt->ring_buff[rc_cpt->index++] = entry;
rc_cpt->cpsync++;
if (rc_cpt->index == RC_CPT_TABSIZE)
rc_cpt->index = 0;
rc_cpt_unlock();
return 0;
}
void rc_close_section(void)
{
# ifndef GTCM_RC
if (rc_cpt)
{ (void) shmdt((char *)rc_cpt);
rc_cpt = 0;
}
# endif
return;
}
/* mupip_rundown_cpt()
*
* Called from mupip rundown to force the deletion of the RC cpt, if
* present
*
* returns:
* zero GT.CM installed and CPT deleted or not present or
* GT.CM not installed
*
* non-zero CPT present, but could not be deleted.
*
* The CPT is not deleted if there are other processes attached to it.
*/
int mupip_rundown_cpt()
{
char buff[1024];
key_t rc_key;
mstr fpath1, fpath2;
struct shmid_ds shm_buf;
/* detach from CPT if we happen to be connected */
if (rc_cpt)
(void) shmdt((char *)rc_cpt);
fpath1.addr = RC_CPT_PATH;
fpath1.len = SIZEOF(RC_CPT_PATH);
if (SS_NORMAL != TRANS_LOG_NAME(&fpath1, &fpath2, buff, SIZEOF(buff), do_sendmsg_on_log2long))
{ /* invalid environment variable setup....error */
return -1;
}
if ((rc_key = FTOK(fpath2.addr,GTM_ID)) == -1)
{ /* no GT.CM server installed on system - okay to reset RC values */
return 0;
}
if ((rc_shmid = shmget(rc_key, SIZEOF(rc_cp_table), RWDALL)) == -1)
{ /* no RC CPT - okay to reset RC values */
rc_shmid = INVALID_SHMID;
return 0;
}
if (shmctl(rc_shmid, IPC_STAT, &shm_buf) == -1)
{
PERROR("Warning- can't access RC CPT");
return -1;
}
if (shm_buf.shm_nattch == 0) /* no GT.CM servers out there */
{ /* delete CPT shared memory */
if (shmctl(rc_shmid, IPC_RMID, 0) == -1)
{
PERROR("Warning- can't delete RC CPT");
return -1;
}
/* attach to and delete CPT semaphore */
if ((rc_sem = semget(rc_key, 4, RWDALL)) == -1)
{
PERROR("Warning- can't access RC CPT semaphore");
rc_cpt = 0;
rc_sem = INVALID_SEMID;
return 0;
}
if (semctl(rc_sem, 0, IPC_RMID, 0) == -1)
{
PERROR("Error cleaning up RC CPT semaphore");
}
return 0; /* we successfully deleted CPT shared memory */
}
return -1;
}
void rc_delete_cpt(void)
{
struct sembuf sop[2];
struct shmid_ds shm_buf;
int semop_rv;
if (!rc_cpt)
return;
sop[0].sem_num = sop[1].sem_num = 0;
sop[0].sem_op = 0;
sop[1].sem_op = 1;
sop[0].sem_flg = sop[1].sem_flg = SEM_UNDO;
SEMOP(rc_sem, sop, 2, semop_rv, FORCED_WAIT);
if (-1 == semop_rv)
{
PERROR("Error with RC semaphore lock");
(void) shmdt((char *)rc_cpt);
rc_cpt = 0;
rc_sem = 0;
return;
}
if (rc_cpt->server_count == 1) /* only daemon, delete it */
{
if (shmctl(rc_shmid,IPC_RMID,&shm_buf) == -1)
{
PERROR("Error cleaning up rc shared memory segment");
}
if (semctl(rc_sem, 0, IPC_RMID, 0) == -1)
{
PERROR("Error cleaning up rc semaphores");
}
} else
{
rc_cpt->server_count--;
sop[0].sem_num = 0;
sop[0].sem_op = -1;
sop[0].sem_flg = SEM_UNDO;
SEMOP(rc_sem, sop, 1, semop_rv, NO_WAIT);
if (-1 == semop_rv)
{
PERROR("Error with RC semaphore unlock");
(void) shmdt((char *)rc_cpt);
rc_cpt = 0;
rc_sem = 0;
if (semctl(rc_sem, 0, IPC_RMID, 0) == -1)
{
PERROR("Error cleaning up rc semaphores");
}
}
}
return;
}
/****************************** Routines used only by the server **********************************/
int rc_create_cpt(void)
{
char buff[1024];
int old_errno;
int semop_rv;
key_t rc_key;
mstr fpath1, fpath2;
struct sembuf sop[2];
struct shmid_ds shm_buf;
if (rc_cpt)
return 0;
fpath1.addr = RC_CPT_PATH;
fpath1.len = SIZEOF(RC_CPT_PATH);
if (SS_NORMAL != TRANS_LOG_NAME(&fpath1, &fpath2, buff, SIZEOF(buff), do_sendmsg_on_log2long))
{
PERROR("Error translating rc path");
return errno;
}
if ((rc_key = FTOK(fpath2.addr,GTM_ID)) == -1)
{
PERROR("Error with rc ftok");
return errno;
}
if ((rc_shmid = shmget(rc_key, SIZEOF(rc_cp_table) ,IPC_CREAT | RWDALL)) == -1)
{
rc_shmid = INVALID_SHMID;
PERROR("Error with rc shmget");
return errno;
}
if ((INTPTR_T)(rc_cpt = (rc_cp_table*)do_shmat(rc_shmid, 0, 0)) == -1L)
{
PERROR("Error with rc shmat");
return errno;
}
if ((rc_sem = semget(rc_key, 4, IPC_CREAT | RWDALL)) == -1)
{
old_errno = errno;
PERROR("Error with rc semget");
(void) shmdt((char *)rc_cpt);
rc_cpt = 0;
rc_sem = INVALID_SEMID;
return old_errno;
}
sop[0].sem_num = sop[1].sem_num = 0;
sop[0].sem_op = 0;
sop[1].sem_op = 1;
sop[0].sem_flg = sop[1].sem_flg = SEM_UNDO;
SEMOP(rc_sem, sop, 2, semop_rv, FORCED_WAIT);
if (-1 == semop_rv)
{
old_errno = errno;
PERROR("Error with RC semaphore lock");
(void) shmdt((char *)rc_cpt);
rc_cpt = 0;
rc_sem = 0;
return old_errno;
}
rc_cpt->server_count++;
sop[0].sem_num = 0;
sop[0].sem_op = -1;
sop[0].sem_flg = SEM_UNDO;
SEMOP(rc_sem, sop, 1, semop_rv, NO_WAIT);
if (-1 == semop_rv)
{
old_errno = errno;
PERROR("Error with RC semaphore unlock");
rc_cpt->server_count++;
(void) shmdt((char *)rc_cpt);
rc_cpt = 0;
rc_sem = 0;
if (semctl(rc_sem, 0, IPC_RMID, 0) == -1)
{
PERROR("Error cleaning up rc semaphores");
}
return old_errno;
}
return 0;
}
short rc_get_cpsync(void)
{
return rc_cpt->cpsync;
}
/* Copy CPT into XBLK, check for overflow, verify that if there is a getrecord operation, the page is still valid */
void rc_send_cpt(rc_xblk_hdr *head, rc_rsp_page *last_aq) /* Zero if no read op in this XBLK */
{ char *ptr;
int cpt_size, copy_size;
bt_rec_ptr_t b;
if (!rc_cpt)
{
head->free.value = head->cpt_tab.value;
head->cpt_siz.value = 0;
return;
}
if (last_aq && (last_aq->hdr.r.typ.value == RC_GET_PAGE || last_aq->hdr.r.typ.value == RC_GET_RECORD))
{
int4 blknum;
assert(!cs_addrs->hold_onto_crit); /* this ensures we can safely do unconditional grab_crit and rel_crit */
grab_crit(gv_cur_region); /* DBP Need to verify that block hasn't changed since copied. look in BT? */
rc_cpt_lock();
GET_LONG(blknum, last_aq->pageaddr);
if ((b = bt_get(blknum)) ? rc_read_stamp <= b->tn :
(rc_read_stamp <= OLDEST_HIST_TN(cs_addrs)))
{
last_aq->hdr.a.erc.value = RC_NETERRRETRY;
}
rel_crit(gv_cur_region);
} else
rc_cpt_lock();
cpt_size = rc_cpt->cpsync - head->sync.value;
if (cpt_size < 0) /* handle wraps */
cpt_size += RC_MAX_CPT_SYNC;
cpt_size *= (int)(RC_CPT_ENTRY_SIZE);
if ((cpt_size > (int)head->cpt_siz.value) || (cpt_size > (RC_CPT_ENTRY_SIZE * RC_CPT_TABSIZE)))
{
head->cpt_siz.value = RC_CPT_OVERFLOW;
rc_cpt->cpvfy = rc_cpt->cpsync;
head->sync.value = rc_cpt->cpsync;
head->free.value = head->cpt_tab.value;
rc_cpt_unlock();
return;
} else
{
head->cpt_siz.value = cpt_size;
ptr = (char *)((char *)&rc_cpt->ring_buff[rc_cpt->index] - cpt_size);
if (ptr < (char *)rc_cpt->ring_buff)
{
ptr = (char *)rc_cpt->ring_buff;
copy_size = (int4)((char *)&rc_cpt->ring_buff[rc_cpt->index] - (char *)rc_cpt->ring_buff);
} else
copy_size = cpt_size;
memcpy((char *)head + head->cpt_tab.value, ptr, copy_size);
if (copy_size != cpt_size)
{
memcpy((char *)head + head->cpt_tab.value + copy_size,
(char *)ARRAYTOP(rc_cpt->ring_buff) - (cpt_size - copy_size), cpt_size - copy_size);
}
}
rc_cpt->cpvfy = rc_cpt->cpsync;
head->sync.value = rc_cpt->cpsync;
head->free.value = head->cpt_tab.value + head->cpt_siz.value;
rc_cpt_unlock();
return;
}