/**************************************************************** * * * 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_ipc.h" #include "gtm_unistd.h" #include "gtm_string.h" #include "gtm_inet.h" #include "gtm_fcntl.h" #include #include #include #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "iosp.h" #include "gtmrecv.h" #include "repl_msg.h" #include "gtmsource.h" #include "gtm_logicals.h" #include "jnl.h" #include "repl_sem.h" #include "repl_shutdcode.h" #include "io.h" #include "trans_log_name.h" #include "repl_instance.h" #include "mu_gv_cur_reg_init.h" #include "gtmmsg.h" #include "gtm_sem.h" #include "mu_rndwn_replpool.h" #include "ftok_sems.h" #include "util.h" #include "anticipatory_freeze.h" #define REMOVE_SEM_SET(SEM_CREATED, POOL_TYPE) /* Assumes 'sem_created_ptr' is is already declared */ \ { \ if (SEM_CREATED) \ remove_sem_set(JNLPOOL_SEGMENT == POOL_TYPE ? SOURCE : RECV); \ *sem_created_ptr = SEM_CREATED = FALSE; \ } #define DO_CLNUP_AND_RETURN(SAVE_ERRNO, SEM_CREATED, POOL_TYPE, INSTFILENAME, INSTFILELEN, SEM_ID, FAILED_OP) \ { \ REMOVE_SEM_SET(SEM_CREATED, REPLPOOL_ID); \ gtm_putmsg(VARLSTCNT(5) ERR_REPLACCSEM, 3, SEM_ID, INSTFILELEN, INSTFILENAME); \ gtm_putmsg(VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT(FAILED_OP), CALLFROM, SAVE_ERRNO); \ return -1; \ } #define RELEASE_ALREADY_HELD_SEMAPHORE(SEM_SET, SEM_NUM) \ { \ int status, lcl_save_errno; \ \ assert(holds_sem[SEM_SET][SEM_NUM]); \ status = rel_sem_immediate(SEM_SET, SEM_NUM); \ if (-1 == status) \ { \ lcl_save_errno = errno; \ gtm_putmsg(VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("semop()"), CALLFROM, lcl_save_errno); \ } \ } #define DECR_ALREADY_INCREMENTED_SEMAPHORE(SEM_SET, SEM_NUM) \ { \ int status, lcl_save_errno; \ \ assert(holds_sem[SEM_SET][SEM_NUM]); \ status = decr_sem(SEM_SET, SEM_NUM); \ if (-1 == status) \ { \ lcl_save_errno = errno; \ gtm_putmsg(VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("semop()"), CALLFROM, lcl_save_errno); \ } \ } GBLREF jnlpool_addrs jnlpool; GBLREF recvpool_addrs recvpool; GBLREF gd_region *gv_cur_region; GBLREF jnl_gbls_t jgbl; GBLREF boolean_t holds_sem[NUM_SEM_SETS][NUM_SRC_SEMS]; GBLREF boolean_t argumentless_rundown; error_def(ERR_JNLPOOLSETUP); error_def(ERR_RECVPOOLSETUP); error_def(ERR_REPLACCSEM); error_def(ERR_REPLFTOKSEM); error_def(ERR_SYSCALL); error_def(ERR_TEXT); /* * Description: * Grab ftok semaphore on replication instance file * Grab all replication semaphores for the instance (both jnlpool and recvpool) * Release ftok semaphore * Parameters: * Return Value: SS_NORMAL, if succsessful * -1, if fails. */ int mu_replpool_grab_sem(repl_inst_hdr_ptr_t repl_inst_filehdr, char pool_type, boolean_t *sem_created_ptr, boolean_t immediate) { int status, save_errno, sem_id, semval, semnum, instfilelen; time_t sem_ctime; boolean_t sem_created, force_increment; char *instfilename; union semun semarg; struct semid_ds semstat; gd_region *replreg; DEBUG_ONLY(unix_db_info *udi;) DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; *sem_created_ptr = sem_created = FALSE; /* assume semaphore not created by default */ force_increment = (jgbl.onlnrlbk || (!jgbl.mur_rollback && !argumentless_rundown && ANTICIPATORY_FREEZE_AVAILABLE)); /* First ensure that the caller has grabbed the ftok semaphore on the replication instance file */ assert((NULL != jnlpool.jnlpool_dummy_reg) && (jnlpool.jnlpool_dummy_reg == recvpool.recvpool_dummy_reg)); replreg = jnlpool.jnlpool_dummy_reg; DEBUG_ONLY(udi = FILE_INFO(jnlpool.jnlpool_dummy_reg)); assert(udi->grabbed_ftok_sem); /* the caller should have grabbed ftok semaphore */ instfilename = (char *)replreg->dyn.addr->fname; instfilelen = replreg->dyn.addr->fname_len; assert((NULL != instfilename) && (0 != instfilelen) && ('\0' == instfilename[instfilelen])); assert((JNLPOOL_SEGMENT == pool_type) || (RECVPOOL_SEGMENT == pool_type)); if (JNLPOOL_SEGMENT == pool_type) { sem_id = repl_inst_filehdr->jnlpool_semid; sem_ctime = repl_inst_filehdr->jnlpool_semid_ctime; } else { sem_id = repl_inst_filehdr->recvpool_semid; sem_ctime = repl_inst_filehdr->recvpool_semid_ctime; } semarg.buf = &semstat; if ((INVALID_SEMID == sem_id) || (-1 == semctl(sem_id, 0, IPC_STAT, semarg)) || (sem_ctime != semarg.buf->sem_ctime)) { /* Semaphore doesn't exist. Create new ones */ if (JNLPOOL_SEGMENT == pool_type) { repl_inst_filehdr->jnlpool_semid = INVALID_SEMID; /* Invalidate previous semid in file header */ repl_inst_filehdr->jnlpool_semid_ctime = 0; sem_id = init_sem_set_source(IPC_PRIVATE, NUM_SRC_SEMS, RWDALL | IPC_CREAT); } else { repl_inst_filehdr->recvpool_semid = INVALID_SEMID; /* Invalidate previous semid in file header */ repl_inst_filehdr->recvpool_semid_ctime = 0; sem_id = init_sem_set_recvr(IPC_PRIVATE, NUM_RECV_SEMS, RWDALL | IPC_CREAT); } if (INVALID_SEMID == sem_id) { save_errno = errno; DO_CLNUP_AND_RETURN(save_errno, sem_created, pool_type, instfilename, instfilelen, sem_id, "semget()"); } *sem_created_ptr = sem_created = TRUE; semarg.val = GTM_ID; if (-1 == semctl(sem_id, SOURCE_ID_SEM, SETVAL, semarg)) { save_errno = errno; DO_CLNUP_AND_RETURN(save_errno, sem_created, pool_type, instfilename, instfilelen, sem_id, "semctl()"); } semarg.buf = &semstat; if (-1 == semctl(sem_id, DB_CONTROL_SEM, IPC_STAT, semarg)) { save_errno = errno; DO_CLNUP_AND_RETURN(save_errno, sem_created, pool_type, instfilename, instfilelen, sem_id, "semctl()"); } sem_ctime = semarg.buf->sem_ctime; } else if (JNLPOOL_SEGMENT == pool_type) set_sem_set_src(sem_id); else set_sem_set_recvr(sem_id); /* Semaphores are setup. Grab them */ if (JNLPOOL_SEGMENT == pool_type) { assert(!jgbl.onlnrlbk || !immediate); if (!immediate) status = grab_sem(SOURCE, JNL_POOL_ACCESS_SEM); /* want to wait in case of online rollback */ else status = grab_sem_immediate(SOURCE, JNL_POOL_ACCESS_SEM); if (SS_NORMAL != status) { save_errno = errno; DO_CLNUP_AND_RETURN(save_errno, sem_created, pool_type, instfilename, instfilelen, sem_id, "semop()"); } semval = semctl(sem_id, SRC_SERV_COUNT_SEM, GETVAL); if (-1 == semval) { save_errno = errno; RELEASE_ALREADY_HELD_SEMAPHORE(SOURCE, JNL_POOL_ACCESS_SEM); DO_CLNUP_AND_RETURN(save_errno, sem_created, pool_type, instfilename, instfilelen, sem_id, "semctl()"); } if (0 < semval && !force_increment) { RELEASE_ALREADY_HELD_SEMAPHORE(SOURCE, JNL_POOL_ACCESS_SEM); util_out_print("Replpool semaphore (id = !UL) for replication instance !AD is in use by another process.", TRUE, sem_id, instfilelen, instfilename); REMOVE_SEM_SET(sem_created, pool_type); return -1; } status = incr_sem(SOURCE, SRC_SERV_COUNT_SEM); if (SS_NORMAL != status) { save_errno = errno; RELEASE_ALREADY_HELD_SEMAPHORE(SOURCE, JNL_POOL_ACCESS_SEM); assert(FALSE); /* we hold it, so there is no reason why we cannot release it */ DO_CLNUP_AND_RETURN(save_errno, sem_created, pool_type, instfilename, instfilelen, sem_id, "semop()"); } holds_sem[SOURCE][SRC_SERV_COUNT_SEM] = TRUE; repl_inst_filehdr->jnlpool_semid = sem_id; repl_inst_filehdr->jnlpool_semid_ctime = sem_ctime; } else { if (!immediate) status = grab_sem(RECV, RECV_POOL_ACCESS_SEM); else status = grab_sem_immediate(RECV, RECV_POOL_ACCESS_SEM); if (SS_NORMAL != status) { save_errno = errno; DO_CLNUP_AND_RETURN(save_errno, sem_created, pool_type, instfilename, instfilelen, sem_id, "semop()"); } if (!immediate) status = grab_sem(RECV, RECV_SERV_OPTIONS_SEM); else status = grab_sem_immediate(RECV, RECV_SERV_OPTIONS_SEM); if (SS_NORMAL != status) { save_errno = errno; RELEASE_ALREADY_HELD_SEMAPHORE(RECV, RECV_POOL_ACCESS_SEM); DO_CLNUP_AND_RETURN(save_errno, sem_created, pool_type, instfilename, instfilelen, sem_id, "semop()"); } assert(RECV_SERV_COUNT_SEM + 1 == UPD_PROC_COUNT_SEM); for (semnum = RECV_SERV_COUNT_SEM; semnum <= UPD_PROC_COUNT_SEM; semnum++) { semval = semctl(sem_id, semnum, GETVAL); if (-1 == semval) { save_errno = errno; RELEASE_ALREADY_HELD_SEMAPHORE(RECV, RECV_POOL_ACCESS_SEM); RELEASE_ALREADY_HELD_SEMAPHORE(RECV, RECV_SERV_OPTIONS_SEM); DO_CLNUP_AND_RETURN(save_errno, sem_created, pool_type, instfilename, instfilelen, sem_id, "semctl()"); } if ((0 < semval) && !jgbl.onlnrlbk) { RELEASE_ALREADY_HELD_SEMAPHORE(RECV, RECV_POOL_ACCESS_SEM); RELEASE_ALREADY_HELD_SEMAPHORE(RECV, RECV_SERV_OPTIONS_SEM); if (UPD_PROC_COUNT_SEM == semnum) { /* Need to decrement RECV_SERV_COUNT_SEM before returning to the caller since we have * incremented it before */ DECR_ALREADY_INCREMENTED_SEMAPHORE(RECV, RECV_SERV_COUNT_SEM); } util_out_print("Replpool semaphore (id = !UL) for replication instance !AD is in use by " "another process.", TRUE, sem_id, instfilelen, instfilename); REMOVE_SEM_SET(sem_created, pool_type); return -1; } status = incr_sem(RECV, semnum); if (SS_NORMAL != status) { save_errno = errno; if (UPD_PROC_COUNT_SEM == semnum) { /* Need to decrement RECV_SERV_COUNT_SEM before returning to the caller since we have * incremented it before */ DECR_ALREADY_INCREMENTED_SEMAPHORE(RECV, RECV_SERV_COUNT_SEM); } DO_CLNUP_AND_RETURN(save_errno, sem_created, pool_type, instfilename, instfilelen, sem_id, "semop()"); } holds_sem[RECV][semnum] = TRUE; } repl_inst_filehdr->recvpool_semid = sem_id; repl_inst_filehdr->recvpool_semid_ctime = sem_ctime; } return SS_NORMAL; }