/**************************************************************** * * * Copyright 2001, 2011 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 #include #include #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 <= ((th_rec *)((uchar_ptr_t)cs_addrs->th_base + cs_addrs->th_base->tnque.fl))->tn)) { 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; }