fis-gtm/sr_port/shmpool.c

622 lines
23 KiB
C

/****************************************************************
* *
* Copyright 2005, 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. *
* *
****************************************************************/
/*
shmpool_buff.c is a collection of routines that collectively manage the 1MB shared buffer pool
that is resident in all database shared memory segments. This pool of database blocksized buffers
is used (currently) for a couple of purposes:
1) When the DB output version is V4 on VMS, there needs to be a place where the reformatted buffer
can live during the async I/O that VMS does. Those blocks are obtained from this pool.
2) When backup needs to send blocks to the intermediate backup file, they are first written to these
buffers and flushed at appropriate times.
Rules for usage:
- On VMS, if both backup and downgrade are in effect, neither service can use more than 50% of the
available blocks. If a service is *not* being used, the other service may use all but one of the
buffers.
*/
#include "mdef.h"
#include "gtm_string.h"
#include "gdsroot.h"
#include "gdskill.h"
#include "gdsblk.h"
#include "gtm_facility.h"
#include "fileinfo.h"
#include "gdsbt.h"
#include "gdsfhead.h"
#include "filestruct.h"
#include "iosp.h"
#include "relqop.h"
#include "copy.h"
#include "wcs_sleep.h"
#include "caller_id.h"
#include "rel_quant.h"
#include "sleep_cnt.h"
#include "interlock.h"
#include "is_proc_alive.h"
#include "mupipbckup.h"
#include "send_msg.h"
#include "performcaslatchcheck.h"
#include "gdsbgtr.h"
#include "lockconst.h"
#include "memcoherency.h"
#include "shmpool.h"
#include"gtm_c_stack_trace.h"
GBLREF volatile int4 fast_lock_count;
GBLREF pid_t process_id;
GBLREF uint4 image_count;
GBLREF int num_additional_processors;
GBLREF node_local_ptr_t locknl;
GBLREF gd_region *gv_cur_region;
error_def(ERR_BCKUPBUFLUSH);
error_def(ERR_DBCCERR);
error_def(ERR_ERRCALL);
error_def(ERR_SHMPLRECOV);
/* Initialize the shared memory buffer pool area */
void shmpool_buff_init(gd_region *reg)
{
int len, elem_len;
shmpool_buff_hdr_ptr_t sbufh_p;
shmpool_blk_hdr_ptr_t sblkh_p;
sgmnt_addrs *csa;
csa = &FILE_INFO(reg)->s_addrs;
sbufh_p = csa->shmpool_buffer;
assert(0 == ((INTPTR_T)sbufh_p & (OS_PAGE_SIZE - 1))); /* Locks and such optimally aligned */
memset(sbufh_p, 0, SIZEOF(shmpool_buff_hdr));
SET_LATCH_GLOBAL(&sbufh_p->shmpool_crit_latch, LOCK_AVAILABLE);
len = SHMPOOL_BUFFER_SIZE - SIZEOF(shmpool_buff_hdr); /* Length to build buffers with */
elem_len = csa->hdr->blk_size + SIZEOF(shmpool_blk_hdr);
assert((len & ~7) == len); /* Verify 8 byte alignment */
assert((elem_len & ~7) == elem_len);
assert(len >= 2 * elem_len); /* Need at least 1 rfmt and 1 bkup block */
/* Place all buffers on free queue initially */
for (sblkh_p = (shmpool_blk_hdr_ptr_t)(sbufh_p + 1);
len >= elem_len;
sblkh_p = (shmpool_blk_hdr_ptr_t)((char_ptr_t)sblkh_p + elem_len), len -= elem_len)
{ /* For each buffer in the pool (however many there are) */
sblkh_p->blktype = SHMBLK_FREE;
sblkh_p->valid_data = FALSE;
VMS_ONLY(sblkh_p->image_count = -1);
insqt(&sblkh_p->sm_que, (que_ent_ptr_t)&sbufh_p->que_free);
++sbufh_p->free_cnt;
}
sbufh_p->total_blks = sbufh_p->free_cnt;
sbufh_p->blk_size = csa->hdr->blk_size;
VERIFY_QUEUE((que_head_ptr_t)&sbufh_p->que_free);
}
/* Allocate a block from the pool for our caller.
Return: addr-of-block on success
-1 no block available
*/
shmpool_blk_hdr_ptr_t shmpool_blk_alloc(gd_region *reg, enum shmblk_type blktype)
{
shmpool_buff_hdr_ptr_t sbufh_p;
shmpool_blk_hdr_ptr_t sblkh_p;
sgmnt_addrs *csa;
int attempts;
boolean_t limit_hit;
csa = &FILE_INFO(reg)->s_addrs;
sbufh_p = csa->shmpool_buffer;
for (attempts = 1; ; ++attempts) /* Start at 1 so we don't wcs_sleep() at bottom of loop on 1st iteration */
{
/* Try a bunch of times but release lock each time to give access to queues to processes that
are trying to free blocks they no longer need.
*/
if (FALSE == shmpool_lock_hdr(reg))
{
assert(FALSE);
rts_error(VARLSTCNT(9) ERR_DBCCERR, 2, REG_LEN_STR(reg), ERR_ERRCALL, 3, CALLFROM);
}
/* Can only verify queue *AFTER* get the lock */
VERIFY_QUEUE((que_head_ptr_t)&sbufh_p->que_free);
/* We have the lock and fast_lock_count is incremented to prevent deadlock on interrupt */
#ifdef VMS
/* Checks to make sure one mode or the other isn't over using its resources. Only important
on VMS as reformat blocks do not exist on UNIX ports.
*/
if (SHMBLK_BACKUP == blktype)
{
/* See if backup has terminated itself for whatever reason */
if (sbufh_p->backup_errno)
{
shmpool_unlock_hdr(reg);
return (shmpool_blk_hdr_ptr_t)-1;
}
/* If we are in compatibility mode, backup is restricted to 50% of the buffers and must leave 1
reformat buffer available */
if ((GDSV4 == csa->hdr->desired_db_format && (sbufh_p->total_blks / 2) < sbufh_p->backup_cnt)
|| (sbufh_p->total_blks - 1) <= sbufh_p->backup_cnt)
{ /* Too many backup blocks already in use for this type */
if (MAX_BACKUP_FLUSH_TRY < attempts)
{ /* We have tried too long .. backup errors out */
sbufh_p->failed = process_id;
sbufh_p->backup_errno = ERR_BCKUPBUFLUSH;
csa->nl->nbb = BACKUP_NOT_IN_PROGRESS;
shmpool_unlock_hdr(reg);
return (shmpool_blk_hdr_ptr_t)-1;
}
limit_hit = TRUE;
} else
limit_hit = FALSE;
} else if (SHMBLK_REFORMAT == blktype)
{ /* If we are in compatibility mode and backup is active, reformat is restricted to 50% of the
buffers. Else must always leave 1 backup buffer available */
if (((GDSV4 == csa->hdr->desired_db_format && (sbufh_p->total_blks / 2) < sbufh_p->reformat_cnt)
&& csa->backup_in_prog) || (sbufh_p->total_blks - 1) <= sbufh_p->reformat_cnt)
{ /* Too many reformat blocks already in use */
if (MAX_BACKUP_FLUSH_TRY < attempts)
{ /* We have tried too long .. return non-buffer to signal do sync IO */
shmpool_unlock_hdr(reg);
return (shmpool_blk_hdr_ptr_t)-1;
}
limit_hit = TRUE;
} else
limit_hit = FALSE;
} else
GTMASSERT;
if (!limit_hit)
#elif defined UNIX
assert(SHMBLK_BACKUP == blktype);
if (sbufh_p->backup_errno)
{
shmpool_unlock_hdr(reg);
return (shmpool_blk_hdr_ptr_t)-1L;
}
DEBUG_ONLY (
if ((MAX_BACKUP_FLUSH_TRY / 2) == attempts)
GET_C_STACK_FROM_SCRIPT("BCKUPBUFLUSH", process_id, sbufh_p->shmpool_crit_latch.u.parts.latch_pid, ONCE);
)
if (MAX_BACKUP_FLUSH_TRY < attempts)
{ /* We have tried too long .. backup errors out */
#ifdef DEBUG
GET_C_STACK_FROM_SCRIPT("BCKUPBUFLUSH", process_id, sbufh_p->shmpool_crit_latch.u.parts.latch_pid, TWICE);
#else
GET_C_STACK_FROM_SCRIPT("BCKUPBUFLUSH", process_id, sbufh_p->shmpool_crit_latch.u.parts.latch_pid, ONCE);
#endif
sbufh_p->failed = process_id;
sbufh_p->backup_errno = ERR_BCKUPBUFLUSH;
csa->nl->nbb = BACKUP_NOT_IN_PROGRESS;
shmpool_unlock_hdr(reg);
return (shmpool_blk_hdr_ptr_t)-1L;
}
#endif
{ /* We didn't hit the limit for this */
/* Get the element (if available) */
sblkh_p = (shmpool_blk_hdr_ptr_t)remqh(&sbufh_p->que_free);
DEBUG_ONLY(if (sblkh_p) {sblkh_p->sm_que.fl = 0; sblkh_p->sm_que.bl = 0;} /* Mark elements as deq'd */);
VERIFY_QUEUE((que_head_ptr_t)&sbufh_p->que_free);
if (NULL != sblkh_p)
{
--sbufh_p->free_cnt;
assert(SHMBLK_FREE == sblkh_p->blktype);
/* Finish initializing block for return and queue to local queue to keep track of it */
sblkh_p->blktype = blktype;
sblkh_p->holder_pid = process_id;
VMS_ONLY(sblkh_p->image_count = image_count);
assert(FALSE == sblkh_p->valid_data); /* cleared when blocok was freed .. should still be free */
VMS_ONLY(if (SHMBLK_BACKUP == blktype))
{
insqt(&sblkh_p->sm_que, &sbufh_p->que_backup);
++sbufh_p->backup_cnt;
VERIFY_QUEUE((que_head_ptr_t)&sbufh_p->que_backup);
#ifdef VMS
} else
{
insqt(&sblkh_p->sm_que, &sbufh_p->que_reformat);
++sbufh_p->reformat_cnt;
VERIFY_QUEUE((que_head_ptr_t)&sbufh_p->que_reformat);
#endif
}
++sbufh_p->allocs_since_chk;
shmpool_unlock_hdr(reg);
return sblkh_p;
}
}
/* Block buffer is not available -- call requisite reclaimation routine depending on blk type needed.
The availability of backup blocks is largely under our control (we can flush to get some) so sleeping
at this level won't free these up. However for reformat buffers, a short sleep to allow IO to (1) be
completed and (2) recognized by wcs_wtfini can take some time so some rel_quant()s and micro sleeps
are warranted.
*/
shmpool_unlock_hdr(reg);
VMS_ONLY(if (SHMBLK_BACKUP == blktype))
{ /* Too many backup blocks already in use */
BG_TRACE_PRO_ANY(csa, shmpool_alloc_bbflush);
if (!backup_buffer_flush(reg))
{ /* The lock was held by someone else, just do a micro sleep before cycling back
around to try again.
*/
wcs_sleep(LOCK_SLEEP);
}
#ifdef VMS
if ((sbufh_p->total_blks / 2) < sbufh_p->reformat_cnt)
/* There are too many reformat blocks in use, make sure we recover
unused ones in a timely fashion.
*/
shmpool_harvest_reformatq(reg);
} else
{ /* Too many reformat blocks already in use */
shmpool_harvest_reformatq(reg);
if (attempts & 0x3)
/* On all but every 4th pass, do a simple rel_quant */
rel_quant(); /* Release processor to holder of lock (hopefully) */
else
/* On every 4th pass, we bide for awhile */
wcs_sleep(LOCK_SLEEP);
if ((sbufh_p->total_blks / 2) < sbufh_p->backup_cnt)
{ /* There are too many backup blocks in use, make sure we recover
unused ones in a timely fashion.
*/
BG_TRACE_PRO_ANY(csa, shmpool_alloc_bbflush);
backup_buffer_flush(reg);
}
#endif
}
}
}
/* Release previously allocated block */
void shmpool_blk_free(gd_region *reg, shmpool_blk_hdr_ptr_t sblkh_p)
{
shmpool_buff_hdr_ptr_t sbufh_p;
shmpool_blk_hdr_ptr_t sblkh_p2;
sgmnt_addrs *csa;
csa = &FILE_INFO(reg)->s_addrs;
sbufh_p = csa->shmpool_buffer;
/* Make sure we have lock coming in */
assert(sbufh_p->shmpool_crit_latch.u.parts.latch_pid == process_id
VMS_ONLY(&& sbufh_p->shmpool_crit_latch.u.parts.latch_image_count == image_count));
/* Verify queue only *AFTER* have the lock */
VERIFY_QUEUE((que_head_ptr_t)&sbufh_p->que_free);
assert(VMS_ONLY(SHMBLK_REFORMAT == sblkh_p->blktype ||) SHMBLK_BACKUP == sblkh_p->blktype);
sblkh_p->holder_pid = 0;
sblkh_p->valid_data = FALSE;
VMS_ONLY(sblkh_p->image_count = -1);
sblkh_p2 = (shmpool_blk_hdr_ptr_t)remqt((que_ent_ptr_t)((char_ptr_t)sblkh_p + sblkh_p->sm_que.fl));
assert(sblkh_p2 == sblkh_p); /* Check we dequ'd the element we want */
DEBUG_ONLY(sblkh_p->sm_que.fl = 0; sblkh_p->sm_que.bl = 0);
if (SHMBLK_BACKUP == sblkh_p->blktype)
{
--sbufh_p->backup_cnt;
assert(0 <= sbufh_p->backup_cnt);
VERIFY_QUEUE((que_head_ptr_t)&sbufh_p->que_backup);
}
#ifdef VMS
else if (SHMBLK_REFORMAT == sblkh_p->blktype)
{
--sbufh_p->reformat_cnt;
sblkh_p->use.rfrmt.cr_off = 0; /* No longer queued to given CR */
assert(0 <= sbufh_p->reformat_cnt);
VERIFY_QUEUE(&(que_head_ptr_t)sbufh_p->que_reformat);
}
#endif
else
GTMASSERT;
sblkh_p->blktype = SHMBLK_FREE;
++sbufh_p->free_cnt;
insqt(&sblkh_p->sm_que, &sbufh_p->que_free);
VERIFY_QUEUE((que_head_ptr_t)&sbufh_p->que_free);
return;
}
#ifdef VMS
/* Routine to "harvest" reformat blocks that have complete IOs (VMS only). */
void shmpool_harvest_reformatq(gd_region *reg)
{
shmpool_buff_hdr_ptr_t sbufh_p;
shmpool_blk_hdr_ptr_t sblkh_p, next_sblkh_p;
sgmnt_addrs *csa;
cache_rec_ptr_t cr;
unsigned int iosb_cond;
csa = &FILE_INFO(reg)->s_addrs;
BG_TRACE_PRO_ANY(csa, shmpool_refmt_harvests);
sbufh_p = csa->shmpool_buffer;
if (FALSE == shmpool_lock_hdr(reg))
{
assert(FALSE);
rts_error(VARLSTCNT(9) ERR_DBCCERR, 2, REG_LEN_STR(reg), ERR_ERRCALL, 3, CALLFROM);
}
if (0 < sbufh_p->reformat_cnt)
{ /* Only if there are some entries */
assert(0 != sbufh_p->que_reformat.fl);
for (sblkh_p = SBLKP_REL2ABS(&sbufh_p->que_reformat, fl);
sblkh_p != (shmpool_blk_hdr_ptr_t)&sbufh_p->que_reformat;
sblkh_p = next_sblkh_p)
{ /* Loop through the queued reformat blocks */
next_sblkh_p = SBLKP_REL2ABS(sblkh_p, fl); /* Get next offset now in case remove entry */
/* Check cache entry to see if it is for the same block and if the pending
IO is complete. */
if (!sblkh_p->valid_data)
{ /* Block is not in use yet */
BG_TRACE_ANY(csa, refmt_hvst_blk_ignored);
continue;
}
assert(SHMBLK_REFORMAT == sblkh_p->blktype);
cr = (cache_rec_ptr_t)GDS_ANY_REL2ABS(csa, sblkh_p->use.rfrmt.cr_off);
/* Check that:
1) This block and the cr it points to are linked.
2) That the cycle is the same.
If we fail either of these, the link can be broken and this block released.
*/
if (0 == sblkh_p->use.rfrmt.cr_off
|| (shmpool_blk_hdr_ptr_t)GDS_ANY_REL2ABS(csa, cr->shmpool_blk_off) != sblkh_p
|| cr->cycle != sblkh_p->use.rfrmt.cycle)
{ /* Block no longer in (same) use .. release it */
shmpool_blk_free(reg, sblkh_p);
BG_TRACE_ANY(csa, refmt_hvst_blk_released_replaced);
} else
{ /* The link is intact. See if IO is done and recheck the cycle since we do not
have the region critical section here to prevent the cache record from being
modified while we are looking at it.
*/
SHM_READ_MEMORY_BARRIER; /* Attempt to sync cr->* references */
iosb_cond = cr->iosb.cond;
if ((0 != iosb_cond && (WRT_STRT_PNDNG != iosb_cond))
|| cr->cycle != sblkh_p->use.rfrmt.cycle)
{ /* IO is complete or block otherwise reused. This means the IO ATTEMPT is
complete. This is not entirely the same check in wcs_wtfini() which cares
if the write was successful. We do not. If the write is retried, it will
be with a newly reformatted buffer, not this iteration of this one. Note
also that the missing part of this condition from wcs_wtfini() where an
is_proc_alive check is done is not done because whether the process is
alive or dead is wcs_wtfini's problem, not ours. When it clears up the
cache entry, we will clear up the associated reformat buffer, if
necessary.
*/
shmpool_blk_free(reg, sblkh_p);
BG_TRACE_ANY(csa, refmt_hvst_blk_released_io_complete);
} else
{
BG_TRACE_ANY(csa, refmt_hvst_blk_kept);
}
}
}
} else
/* Shouldn't be any queue elements either */
assert(0 == sbufh_p->que_reformat.fl);
shmpool_unlock_hdr(reg);
}
#endif
/* Check if we have any "lost" blocks by failed (and now defunct) processes.
There are 3 reasons this routine can be called:
(1) Called by shmpool_unlock_hdr() if the sum of the counts of the elements on the 3 queues differs from
the known total number of blocks. There is a small variance allowed since a couple of missing blocks
need not trigger a recovery but more than a handful triggers this code to reclaim the lost blocks and
make the ocunters correct again.
(2) If secshr_db_clnup() sets shmpool_blocked by releasing the lock of a shot/killed process during
cleanup.
(3) If after 15 (0xF) attempts to allocate a backup buffer after a buffer flush we still have no buffer
to use then trigger this module in case the reason is lost blocks. It is possible for blocks to be
lost and not trigger either (1) or (2) if, for example, a process is shot or errors out after allocating
a backup block but before filling it in or allocating but before filling a reformat block.
*/
void shmpool_abandoned_blk_chk(gd_region *reg, boolean_t force)
{
int blks;
shmpool_buff_hdr_ptr_t sbufh_p;
shmpool_blk_hdr_ptr_t sblkh_p;
sgmnt_addrs *csa;
csa = &FILE_INFO(reg)->s_addrs;
sbufh_p = csa->shmpool_buffer;
/* Note we must hold the shmpool latch *before* calling this routine */
assert(process_id == sbufh_p->shmpool_crit_latch.u.parts.latch_pid
VMS_ONLY(&& sbufh_p->shmpool_crit_latch.u.parts.latch_image_count == image_count));
/* This check only runs a maximum of once every N block allocations where N is the
number of blocks in this buffer pool.
*/
if (!force && sbufh_p->allocs_since_chk < sbufh_p->total_blks)
return; /* Too soon to run this check */
BG_TRACE_PRO_ANY(csa, shmpool_recovery);
sbufh_p->que_free.fl = sbufh_p->que_free.bl = 0;
sbufh_p->que_backup.fl = sbufh_p->que_backup.bl = 0;
VMS_ONLY(sbufh_p->que_reformat.fl = sbufh_p->que_reformat.bl = 0);
sbufh_p->free_cnt = sbufh_p->backup_cnt = VMS_ONLY(sbufh_p->reformat_cnt = ) 0;
sbufh_p->allocs_since_chk = 0; /* Restart the counter */
/* Rebuild the queues and counts according to:
1) What the block thinks it is (free, backup or reformat.
2) Verify that the block is in a valid state (needs pid set, process should exist)
3) Invalid state blocks are considered abandoned and put on the free queue.
*/
for (sblkh_p = (shmpool_blk_hdr_ptr_t)(sbufh_p + 1), blks = sbufh_p->total_blks;
blks > 0;
sblkh_p = (shmpool_blk_hdr_ptr_t)((char_ptr_t)sblkh_p + SIZEOF(shmpool_blk_hdr) + sbufh_p->blk_size), --blks)
{ /* For each block not free, check if it is assigned to a process and if so if that process exists.
If not or if the block is already free, put on the free queue after a thorough cleaning.
*/
if (SHMBLK_FREE == sblkh_p->blktype
|| 0 == sblkh_p->holder_pid || !is_proc_alive(sblkh_p->holder_pid, sblkh_p->image_count))
{ /* Make sure block is clean (orphaned blocks might not be) and queue on free. */
sblkh_p->holder_pid = 0;
sblkh_p->valid_data = FALSE;
sblkh_p->blktype = SHMBLK_FREE;
VMS_ONLY(sblkh_p->image_count = -1);
insqt(&sblkh_p->sm_que, &sbufh_p->que_free);
++sbufh_p->free_cnt;
} else if (SHMBLK_BACKUP == sblkh_p->blktype)
{
insqt(&sblkh_p->sm_que, &sbufh_p->que_backup);
++sbufh_p->backup_cnt;
} else
{
#ifdef VMS
if (SHMBLK_REFORMAT == sblkh_p->blktype)
{
insqt(&sblkh_p->sm_que, &sbufh_p->que_reformat);
++sbufh_p->reformat_cnt;
} else
#endif
GTMASSERT;
}
}
VERIFY_QUEUE((que_head_ptr_t)&sbufh_p->que_free);
VERIFY_QUEUE((que_head_ptr_t)&sbufh_p->que_backup);
VMS_ONLY(VERIFY_QUEUE((que_head_ptr_t)&sbufh_p->que_reformat));
assert((sbufh_p->free_cnt + sbufh_p->backup_cnt VMS_ONLY(+ sbufh_p->reformat_cnt)) == sbufh_p->total_blks);
sbufh_p->shmpool_blocked = FALSE;
send_msg(VARLSTCNT(4) ERR_SHMPLRECOV, 2, REG_LEN_STR(reg));
}
/* Lock the shared memory buffer pool header. If cannot get the lock, return FALSE, else TRUE. */
boolean_t shmpool_lock_hdr(gd_region *reg)
{
int retries, spins, maxspins;
sm_global_latch_ptr_t latch;
shmpool_buff_hdr_ptr_t sbufh_p;
sgmnt_addrs *csa;
csa = &FILE_INFO(reg)->s_addrs;
sbufh_p = csa->shmpool_buffer;
latch = &sbufh_p->shmpool_crit_latch;
++fast_lock_count; /* Disable wcs_stale for duration */
maxspins = num_additional_processors ? MAX_LOCK_SPINS(LOCK_SPINS, num_additional_processors) : 1;
/* Since LOCK_TRIES is approx 50 seconds, give us 4X that long since IO is involved */
for (retries = (LOCK_TRIES * 4) - 1; 0 < retries; retries--) /* - 1 so do rel_quant 3 times first */
{
for (spins = maxspins; 0 < spins; spins--)
{ /* We better not hold it if trying to get it */
assert(latch->u.parts.latch_pid != process_id
VMS_ONLY(|| latch->u.parts.latch_image_count != image_count));
if (GET_SWAPLOCK(latch))
{
DEBUG_ONLY(locknl = csa->nl);
LOCK_HIST("OBTN", latch, process_id, retries);
DEBUG_ONLY(locknl = NULL);
/* Note that fast_lock_count is kept incremented for the duration that we hold the lock
to prevent our dispatching an interrupt that could deadlock getting this lock
*/
/* If the buffer is marked as "blocked", first run a forced recovery before returning */
if (sbufh_p->shmpool_blocked)
shmpool_abandoned_blk_chk(reg, TRUE);
return TRUE;
}
}
if (retries & 0x3)
/* On all but every 4th pass, do a simple rel_quant */
rel_quant(); /* Release processor to holder of lock (hopefully) */
else
{
/* On every 4th pass, we bide for awhile */
wcs_sleep(LOCK_SLEEP);
/* assert(0 == (LOCK_TRIES % 4)); assures there are 3 rel_quants prior to first wcs_sleep()
but not needed as LOCK_TRIES is multiplied by 4 above.
*/
/* If near end of loop segment (LOCK_TRIES iters), see if target is dead and/or wake it up */
if (RETRY_CASLATCH_CUTOFF == (retries % LOCK_TRIES))
performCASLatchCheck(latch, TRUE);
}
}
DUMP_LOCKHIST();
--fast_lock_count;
assert(0 <= fast_lock_count);
assert(FALSE);
return FALSE;
}
/* Lock the buffer header area like shmpool_lock_hdr() but if cannot get lock, do not wait for it */
boolean_t shmpool_lock_hdr_nowait(gd_region *reg)
{
sm_global_latch_ptr_t latch;
shmpool_buff_hdr_ptr_t sbufh_p;
sgmnt_addrs *csa;
csa = &FILE_INFO(reg)->s_addrs;
sbufh_p = csa->shmpool_buffer;
latch = &sbufh_p->shmpool_crit_latch;
++fast_lock_count; /* Disable wcs_stale for duration */
/* We better not hold it if trying to get it */
assert(latch->u.parts.latch_pid != process_id VMS_ONLY(|| latch->u.parts.latch_image_count != image_count));
if (GET_SWAPLOCK(latch))
{
DEBUG_ONLY(locknl = csa->nl);
LOCK_HIST("OBTN", latch, process_id, -1);
DEBUG_ONLY(locknl = NULL);
/* Note that fast_lock_count is kept incremented for the duration that we hold the lock
to prevent our dispatching an interrupt that could deadlock getting this lock
*/
/* If the buffer is marked as "blocked", first run a forced recovery before returning */
if (sbufh_p->shmpool_blocked)
shmpool_abandoned_blk_chk(reg, TRUE);
return TRUE;
}
--fast_lock_count;
assert(0 <= fast_lock_count);
return FALSE;
}
/* Unlock the shared memory buffer pool header */
void shmpool_unlock_hdr(gd_region *reg)
{
sm_global_latch_ptr_t latch;
shmpool_buff_hdr_ptr_t sbufh_p;
sgmnt_addrs *csa;
int cntr_delta;
csa = &FILE_INFO(reg)->s_addrs;
sbufh_p = csa->shmpool_buffer;
latch = &sbufh_p->shmpool_crit_latch;
assert(process_id == latch->u.parts.latch_pid VMS_ONLY(&& image_count == latch->u.parts.latch_image_count));
/* Quickly check if our counters are as we expect them to be. If not see if we need to run
our recovery procedure (shmpool_blk_abandoned_chk()).
*/
cntr_delta = sbufh_p->free_cnt + sbufh_p->backup_cnt VMS_ONLY(+ sbufh_p->reformat_cnt) - sbufh_p->total_blks;
if (0 != cntr_delta)
shmpool_abandoned_blk_chk(reg, FALSE);
DEBUG_ONLY(locknl = csa->nl);
LOCK_HIST("RLSE", latch, process_id, 0);
RELEASE_SWAPLOCK(latch);
DEBUG_ONLY(locknl = NULL);
--fast_lock_count;
assert(0 <= fast_lock_count);
}
/* If the shmpool lock is held by this process return true .. else false. */
boolean_t shmpool_lock_held_by_us(gd_region *reg)
{
sgmnt_addrs *csa;
csa = &FILE_INFO(reg)->s_addrs;
return GLOBAL_LATCH_HELD_BY_US(&csa->shmpool_buffer->shmpool_crit_latch);
}