649 lines
18 KiB
C
Executable File
649 lines
18 KiB
C
Executable File
/*
|
|
* Copyright (c) 1994, by Sun Microsytems, Inc.
|
|
*/
|
|
|
|
#pragma ident "@(#)tnf_buf.c 1.35 95/07/19 SMI"
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/param.h>
|
|
#ifdef _KERNEL
|
|
#include <sys/systm.h> /* for bzero */
|
|
#include <sys/spl.h>
|
|
#include <sys/cmn_err.h>
|
|
#else /* _KERNEL */
|
|
#include <string.h> /* for memset */
|
|
#endif /* _KERNEL */
|
|
|
|
#include "tnf_buf.h"
|
|
|
|
#ifdef TNFWB_DEBUG
|
|
#ifdef _KERNEL
|
|
#error TNFWB_DEBUG
|
|
#else /* _KERNEL */
|
|
#include <stdio.h>
|
|
#include <thread.h>
|
|
#endif /* _KERNEL */
|
|
#endif /* TNFW_DEBUG */
|
|
|
|
/*
|
|
* Defines
|
|
*/
|
|
|
|
#define TNFW_B_FW_INVALID 0xffffffff
|
|
#define TNFW_B_ALLOC_LO_SELECTOR 0x1
|
|
#define TNFW_B_MAXALLOCTRY 200
|
|
|
|
#ifdef TNF_BLOCK_STATS
|
|
static struct {
|
|
int tnf_block_allocs;
|
|
int tnf_block_tries;
|
|
int tnf_max_block_tries;
|
|
int tnf_tag_blocks;
|
|
int tnf_generation_laps;
|
|
int tnf_a_locks;
|
|
int tnf_b_locks;
|
|
} tnf_block_stats;
|
|
#endif
|
|
|
|
/*
|
|
* Regular record tag pointer - CAUTION - has to be in sync with tnf_tag
|
|
* macro in writer.h
|
|
*/
|
|
#define TNFW_B_TAG_DIFF(item, ref) \
|
|
((TNF_REF32_MAKE_PERMANENT((tnf_ref32_t) \
|
|
((char *)(item) - (char *)(ref)))) | TNF_REF32_T_TAG)
|
|
|
|
/*
|
|
* Exported interface by buffering layer to indicate where fowarding ptrs
|
|
* for file header and block header are.
|
|
*/
|
|
static tnf_buf_header_t forwarding_ptrs = {NULL, NULL, NULL};
|
|
tnf_buf_header_t *_tnf_buf_headers_p = &forwarding_ptrs;
|
|
|
|
#ifdef _KERNEL
|
|
extern volatile caddr_t tnf_buf;
|
|
|
|
static kmutex_t hintlock;
|
|
#endif
|
|
|
|
/*
|
|
* (Private) Allocate a new block. Return NULL on failure. 'istag'
|
|
* is true if the block is to be non-reclaimable.
|
|
*/
|
|
static tnf_block_header_t *
|
|
tnfw_b_alloc_block(TNFW_B_WCB *wcb, enum tnf_alloc_mode istag)
|
|
{
|
|
tnf_block_header_t *block;
|
|
u_long hint_hi, hint_lo;
|
|
u_long new_hint_hi, new_hint_lo;
|
|
u_long generation, blocknum;
|
|
u_long prev_gen = 0;
|
|
u_long prev_block = 0;
|
|
int i, b;
|
|
boolean_t gotit = B_FALSE;
|
|
volatile tnf_buf_file_header_t *fh;
|
|
#ifdef TNF_BLOCK_STATS
|
|
register int tag_blocks = 0, generation_laps = 0, a_locks = 0,
|
|
b_locks = 0;
|
|
#endif
|
|
|
|
if (_tnfw_b_control->tnf_state != TNFW_B_RUNNING) {
|
|
#ifndef _KERNEL
|
|
if (_tnfw_b_control->tnf_state == TNFW_B_NOBUFFER)
|
|
if (_tnfw_b_control->tnf_init_callback() == 0)
|
|
return (NULL);
|
|
#endif /* _KERNEL */
|
|
if (TNFW_B_IS_STOPPED(_tnfw_b_control->tnf_state))
|
|
return (NULL);
|
|
if (_tnfw_b_control->tnf_state == TNFW_B_BROKEN)
|
|
return (NULL);
|
|
}
|
|
|
|
/* LINTED pointer cast may result in improper alignment */
|
|
fh = (volatile tnf_buf_file_header_t *)_tnfw_b_control->tnf_buffer;
|
|
if (!wcb->tnfw_w_initialized) {
|
|
/* Get the block shift and generation shift values. */
|
|
b = 1;
|
|
wcb->tnfw_w_block_shift = wcb->tnfw_w_gen_shift = 0;
|
|
while (b != fh->com.block_size) {
|
|
b <<= 1;
|
|
++wcb->tnfw_w_block_shift;
|
|
}
|
|
b = 1;
|
|
while (b < fh->com.block_count) {
|
|
b <<= 1;
|
|
++wcb->tnfw_w_gen_shift;
|
|
}
|
|
wcb->tnfw_w_pid = _tnfw_b_control->tnf_pid;
|
|
wcb->tnfw_w_initialized = B_TRUE;
|
|
}
|
|
|
|
/*
|
|
* If we need a tag block, check the reserved tag block space
|
|
* first. fh->next_tag_alloc is only a hint; it is updated
|
|
* without concurrency control.
|
|
*/
|
|
if (istag && fh->next_tag_alloc < TNFW_B_DATA_BLOCK_BEGIN) {
|
|
i = fh->next_tag_alloc;
|
|
do {
|
|
/* LINTED pointer cast */
|
|
block = (tnf_block_header_t *) ((char *) fh + i);
|
|
if (!tnfw_b_get_lock(&block->A_lock) &&
|
|
block->generation == 0)
|
|
break;
|
|
i += fh->com.block_size;
|
|
} while (i < TNFW_B_DATA_BLOCK_BEGIN);
|
|
if (i < TNFW_B_DATA_BLOCK_BEGIN) {
|
|
if (i > fh->next_tag_alloc)
|
|
fh->next_tag_alloc = i;
|
|
blocknum = i >> wcb->tnfw_w_block_shift;
|
|
if (blocknum > fh->com.blocks_valid)
|
|
fh->com.blocks_valid = blocknum;
|
|
block->tag = TNFW_B_TAG_DIFF(
|
|
forwarding_ptrs.fw_block_header, fh);
|
|
/* LINTED constant truncated by assignment */
|
|
block->generation = TNF_TAG_GENERATION_NUM;
|
|
block->bytes_valid = sizeof (tnf_block_header_t);
|
|
block->next_block = NULL;
|
|
tnfw_b_clear_lock(&block->A_lock);
|
|
return (block);
|
|
}
|
|
}
|
|
|
|
for (i = 0; !gotit && i != TNFW_B_MAXALLOCTRY; ++i) {
|
|
hint_hi = fh->next_alloc.hi;
|
|
hint_lo = (hint_hi & TNFW_B_ALLOC_LO_SELECTOR)
|
|
? fh->next_alloc.lo[1] : fh->next_alloc.lo[0];
|
|
generation = (hint_hi << (32 - wcb->tnfw_w_gen_shift)) |
|
|
(hint_lo >> wcb->tnfw_w_gen_shift);
|
|
blocknum = hint_lo & ((1 << wcb->tnfw_w_gen_shift) - 1);
|
|
#ifdef TNFWB_DEBUG
|
|
fprintf(stderr, "alloc_block (%d): read hint (%d, %d)\n",
|
|
thr_self(), generation, blocknum);
|
|
#endif
|
|
if ((prev_gen == generation && prev_block > blocknum) ||
|
|
prev_gen > generation) {
|
|
generation = prev_gen;
|
|
blocknum = prev_block;
|
|
}
|
|
#ifdef TNFWB_DEBUG
|
|
fprintf(stderr,
|
|
"alloc_block (%d): trying blocknum = %d, gen %d\n",
|
|
thr_self(), blocknum, generation);
|
|
#endif
|
|
block = (tnf_block_header_t *)
|
|
/* LINTED pointer cast may result in improper alignment */
|
|
((char *)fh + blocknum * fh->com.block_size);
|
|
#ifdef TNF_BLOCK_STATS
|
|
if (block->generation == TNF_TAG_GENERATION_NUM)
|
|
++tag_blocks;
|
|
else if (block->generation >= generation)
|
|
++generation_laps;
|
|
else if (tnfw_b_get_lock(&block->A_lock))
|
|
++a_locks;
|
|
else if (block->generation == TNF_TAG_GENERATION_NUM)
|
|
++tag_blocks;
|
|
else if (block->generation >= generation)
|
|
++generation_laps;
|
|
else if (tnfw_b_get_lock(&block->B_lock)) {
|
|
tnfw_b_clear_lock(&block->A_lock);
|
|
++b_locks;
|
|
} else
|
|
gotit = B_TRUE;
|
|
|
|
#else
|
|
if (block->generation < generation &&
|
|
!tnfw_b_get_lock(&block->A_lock)) {
|
|
if (block->generation < generation &&
|
|
!tnfw_b_get_lock(&block->B_lock)) {
|
|
gotit = B_TRUE;
|
|
} else {
|
|
tnfw_b_clear_lock(&block->A_lock);
|
|
}
|
|
}
|
|
#endif
|
|
prev_block = blocknum + 1;
|
|
prev_gen = generation;
|
|
if (prev_block == fh->com.block_count) {
|
|
prev_block =
|
|
TNFW_B_DATA_BLOCK_BEGIN >> wcb->tnfw_w_block_shift;
|
|
++prev_gen;
|
|
}
|
|
if (blocknum > fh->com.blocks_valid) {
|
|
fh->com.blocks_valid = blocknum;
|
|
}
|
|
}
|
|
|
|
if (i == TNFW_B_MAXALLOCTRY) {
|
|
_tnfw_b_control->tnf_state = TNFW_B_BROKEN;
|
|
return (NULL);
|
|
}
|
|
#ifdef TNFWB_DEBUG
|
|
fprintf(stderr,
|
|
"alloc_block (%d): got blocknum = %d, gen %d, block at 0x%x\n",
|
|
thr_self(), blocknum, generation, block);
|
|
#endif
|
|
block->tag = TNFW_B_TAG_DIFF(forwarding_ptrs.fw_block_header, fh);
|
|
block->generation = (istag) ? TNF_TAG_GENERATION_NUM : generation;
|
|
block->bytes_valid = sizeof (tnf_block_header_t);
|
|
block->next_block = NULL;
|
|
if (istag) {
|
|
tnfw_b_clear_lock(&block->A_lock);
|
|
}
|
|
tnfw_b_clear_lock(&block->B_lock);
|
|
|
|
/*
|
|
* Read the hint one more time, only update it if we'll be increasing
|
|
* it
|
|
*/
|
|
new_hint_hi = prev_block >> (32 - wcb->tnfw_w_gen_shift);
|
|
new_hint_lo = prev_block | (prev_gen << wcb->tnfw_w_gen_shift);
|
|
#ifdef _KERNEL
|
|
mutex_enter(&hintlock);
|
|
#endif
|
|
hint_hi = fh->next_alloc.hi;
|
|
hint_lo = (hint_hi & TNFW_B_ALLOC_LO_SELECTOR) ?
|
|
fh->next_alloc.lo[1] : fh->next_alloc.lo[0];
|
|
|
|
if ((new_hint_hi == hint_hi && new_hint_lo > hint_lo) ||
|
|
new_hint_hi > hint_hi) {
|
|
/*
|
|
* Order is important here! It is the write to next_alloc.hi
|
|
* that atomically records the new value.
|
|
*/
|
|
if (new_hint_hi & TNFW_B_ALLOC_LO_SELECTOR)
|
|
fh->next_alloc.lo[1] = new_hint_lo;
|
|
else
|
|
fh->next_alloc.lo[0] = new_hint_lo;
|
|
fh->next_alloc.hi = new_hint_hi;
|
|
#ifdef TNFWB_DEBUG
|
|
fprintf(stderr, "alloc_block (%d): wrote hint (%d, %d)\n",
|
|
thr_self(), prev_gen, prev_block);
|
|
#endif
|
|
}
|
|
#ifdef _KERNEL
|
|
mutex_exit(&hintlock);
|
|
#endif
|
|
#ifdef TNF_BLOCK_STATS
|
|
++tnf_block_stats.tnf_block_allocs;
|
|
tnf_block_stats.tnf_block_tries += i;
|
|
if (i > tnf_block_stats.tnf_max_block_tries) {
|
|
tnf_block_stats.tnf_max_block_tries = i;
|
|
tnf_block_stats.tnf_tag_blocks = tag_blocks;
|
|
tnf_block_stats.tnf_generation_laps = generation_laps;
|
|
tnf_block_stats.tnf_a_locks = a_locks;
|
|
tnf_block_stats.tnf_b_locks = b_locks;
|
|
}
|
|
#endif
|
|
return (block);
|
|
}
|
|
|
|
/*
|
|
* Initialize a buffer. NOT RE-ENTRANT! Block sizes other than 512
|
|
* are currently rejected. The code "ought to work" with any block
|
|
* size that is an integral power of 2. 'zfod' states whether we
|
|
* can assume that the buffer is zero-filled (or paged-in zero-fill-on-demand).
|
|
*/
|
|
TNFW_B_STATUS
|
|
tnfw_b_init_buffer(char *buf, int blocks, int block_size, boolean_t zfod)
|
|
|
|
{
|
|
int block_shift, gen_shift;
|
|
int i;
|
|
int file_size;
|
|
unsigned b;
|
|
tnf_block_header_t *block;
|
|
/* LINTED pointer cast may result in improper alignment */
|
|
tnf_buf_file_header_t *fh = (tnf_buf_file_header_t *)buf;
|
|
|
|
/* Check for 512 could go away. */
|
|
if (block_size != 512 || block_size < sizeof (tnf_buf_file_header_t))
|
|
return (TNFW_B_BAD_BLOCK_SIZE);
|
|
/*
|
|
* Check to see if block size is a power of 2, and get
|
|
* log2(block size).
|
|
*/
|
|
for (b = (unsigned)block_size, block_shift = 0; (b & 1) == 0; b >>= 1)
|
|
++block_shift;
|
|
if (b != 1)
|
|
return (TNFW_B_BAD_BLOCK_SIZE);
|
|
gen_shift = 0;
|
|
while (b < blocks) {
|
|
b <<= 1;
|
|
++gen_shift;
|
|
}
|
|
/* reserve first two words for file header tag and block header tag */
|
|
forwarding_ptrs.fw_file_header = (char *)fh + block_size;
|
|
forwarding_ptrs.fw_block_header = (char *)fh + block_size +
|
|
sizeof (tnf_ref32_t);
|
|
forwarding_ptrs.fw_root = (char *)fh + block_size +
|
|
(2 * sizeof (tnf_ref32_t));
|
|
fh->next_fw_alloc = block_size + (3 * sizeof (tnf_ref32_t));
|
|
/* fill in rest of file header */
|
|
fh->magic = TNF_MAGIC;
|
|
/* Self relative pointer to tag */
|
|
fh->com.tag = TNFW_B_TAG_DIFF(forwarding_ptrs.fw_file_header, fh);
|
|
fh->com.file_version = TNF_FILE_VERSION;
|
|
fh->com.file_header_size = sizeof (tnf_file_header_t);
|
|
/* fill in fh->com.file_log_size */
|
|
b = 1;
|
|
file_size = blocks * block_size;
|
|
fh->com.file_log_size = 0;
|
|
while (b < file_size) {
|
|
b <<= 1;
|
|
++fh->com.file_log_size;
|
|
}
|
|
|
|
fh->com.block_header_size = sizeof (tnf_block_header_t);
|
|
fh->com.block_size = block_size;
|
|
fh->com.directory_size = TNFW_B_FW_ZONE;
|
|
fh->com.block_count = blocks;
|
|
fh->com.blocks_valid = TNFW_B_FW_ZONE >> block_shift;
|
|
if (fh->com.blocks_valid == 0)
|
|
fh->com.blocks_valid = 1;
|
|
fh->next_tag_alloc = TNFW_B_FW_ZONE;
|
|
fh->next_alloc.hi = 0;
|
|
fh->next_alloc.lo[0] =
|
|
(1 << gen_shift) | (TNFW_B_DATA_BLOCK_BEGIN >> block_shift);
|
|
#ifdef TNFWB_DEBUG
|
|
fprintf(stderr, "gen_shift = %d, blocks_valid = %d\n",
|
|
gen_shift, fh->com.blocks_valid);
|
|
fprintf(stderr, "alloc hint initialized to (%d, %d, %d)\n",
|
|
fh->next_alloc.hi, fh->next_alloc.lo[0], fh->next_alloc.lo[1]);
|
|
#endif
|
|
if (!zfod) {
|
|
for (i = 1; i < (TNFW_B_FW_ZONE >> block_shift); ++i) {
|
|
#ifdef _KERNEL
|
|
bzero(buf + (i << block_shift), block_size);
|
|
#else
|
|
(void) memset(buf + (i << block_shift), 0, block_size);
|
|
#endif
|
|
}
|
|
for (; i != blocks; ++i) {
|
|
block = (tnf_block_header_t *)
|
|
/* LINTED pointer cast */
|
|
(buf + (i << block_shift));
|
|
block->tag = 0;
|
|
block->generation = 0;
|
|
tnfw_b_clear_lock(&block->A_lock);
|
|
tnfw_b_clear_lock(&block->B_lock);
|
|
}
|
|
}
|
|
#ifdef _KERNEL
|
|
mutex_init(&hintlock, "tnf buffer hint lock", MUTEX_SPIN_DEFAULT,
|
|
(void *) ipltospl(LOCK_LEVEL));
|
|
#endif
|
|
return (TNFW_B_OK);
|
|
}
|
|
|
|
/*
|
|
*
|
|
*/
|
|
void *
|
|
tnfw_b_alloc(TNFW_B_WCB *wcb, int size, enum tnf_alloc_mode istag)
|
|
{
|
|
TNFW_B_POS *pos;
|
|
int offset;
|
|
void *destp;
|
|
volatile tnf_buf_file_header_t *fh;
|
|
tnf_block_header_t *block, *new_block;
|
|
|
|
if (_tnfw_b_control->tnf_state != TNFW_B_RUNNING) {
|
|
if (TNFW_B_IS_STOPPED(_tnfw_b_control->tnf_state))
|
|
return (NULL);
|
|
if (_tnfw_b_control->tnf_state == TNFW_B_FORKED &&
|
|
_tnfw_b_control->tnf_pid != wcb->tnfw_w_pid) {
|
|
wcb->tnfw_w_pos.tnfw_w_block =
|
|
wcb->tnfw_w_pos.tnfw_w_uncommitted =
|
|
wcb->tnfw_w_tag_pos.tnfw_w_block =
|
|
wcb->tnfw_w_tag_pos.tnfw_w_uncommitted = NULL;
|
|
wcb->tnfw_w_pid = _tnfw_b_control->tnf_pid;
|
|
_tnfw_b_control->tnf_fork_callback();
|
|
}
|
|
}
|
|
|
|
/* Round size up to a multiple of 8. */
|
|
size = (size + 7) & ~7;
|
|
|
|
/* LINTED pointer cast may result in improper alignment */
|
|
fh = (volatile tnf_buf_file_header_t *)_tnfw_b_control->tnf_buffer;
|
|
pos = (istag) ? &wcb->tnfw_w_tag_pos : &wcb->tnfw_w_pos;
|
|
block = pos->tnfw_w_block;
|
|
/* Check size within range. */
|
|
#ifdef TNFWB_SAFER
|
|
if (size > fh->com.block_size - sizeof (tnf_block_header_t))
|
|
/* TNFW_B_RECORD_TOO_BIG */
|
|
return (NULL);
|
|
#endif
|
|
offset = pos->tnfw_w_write_off;
|
|
#ifdef TNFWB_MAY_RELEASE_A_LOCK
|
|
if (block != NULL && wcb->tnfw_w_a_lock_released) {
|
|
/* re-acquire the A-lock for the current block */
|
|
if (!tnfw_b_get_lock(&block->A_lock)) {
|
|
wcb->tnfw_w_a_lock_released = B_FALSE;
|
|
if (wcb->tnfw_w_generation != block->generation) {
|
|
tnfw_b_clear_lock(&block->A_lock);
|
|
wcb->tnfw_w_pos.tnfw_w_block = NULL;
|
|
}
|
|
} else {
|
|
wcb->tnfw_w_pos.tnfw_w_block = NULL;
|
|
}
|
|
}
|
|
#endif
|
|
if (block == NULL || offset + size > fh->com.block_size) {
|
|
new_block = tnfw_b_alloc_block(wcb, istag);
|
|
if (new_block == NULL) {
|
|
/* TNFW_B_ACKPHT */
|
|
return (NULL);
|
|
}
|
|
#ifdef TNFWB_DEBUG
|
|
fprintf(stderr,
|
|
"wcb 0x%x: new block at 0x%x, old block is 0x%x, "
|
|
"uncommitted is 0x%x\n",
|
|
wcb, new_block, block, pos->tnfw_w_uncommitted);
|
|
#endif
|
|
if (block != NULL) {
|
|
/* XXXX is this what we want for padding? */
|
|
#ifdef _KERNEL
|
|
(void) bzero((char *)block + offset,
|
|
fh->com.block_size - offset);
|
|
#else
|
|
(void) memset((char *)block + offset, 0,
|
|
fh->com.block_size - offset);
|
|
#endif
|
|
if (pos->tnfw_w_uncommitted == NULL) {
|
|
#ifdef TNFWB_MAY_RELEASE_A_LOCK
|
|
/* Could still be holding the A-lock on block */
|
|
if (!wcb->tnfw_w_a_lock_released)
|
|
tnfw_b_clear_lock(&block->A_lock);
|
|
#else
|
|
/* Definitely still holding the A-lock */
|
|
tnfw_b_clear_lock(&block->A_lock);
|
|
#endif /* TNFWB_MAY_RELEASE_A_LOCK */
|
|
}
|
|
}
|
|
/* Add new_block to the list of uncommitted blocks. */
|
|
if (pos->tnfw_w_uncommitted == NULL) {
|
|
pos->tnfw_w_uncommitted = new_block;
|
|
} else {
|
|
/* Assert(block != NULL); */
|
|
block->next_block = new_block;
|
|
}
|
|
pos->tnfw_w_block = new_block;
|
|
pos->tnfw_w_write_off = new_block->bytes_valid;
|
|
} else if (pos->tnfw_w_uncommitted == NULL) {
|
|
pos->tnfw_w_uncommitted = block;
|
|
}
|
|
destp = (char *)pos->tnfw_w_block + pos->tnfw_w_write_off;
|
|
pos->tnfw_w_write_off += size;
|
|
/*
|
|
* Unconditionally write a 0 into the last word allocated,
|
|
* in case we left an alignment gap. (Assume that doing an
|
|
* unconditional write is cheaper than testing and branching
|
|
* around the write half the time.)
|
|
*/
|
|
/* LINTED pointer cast may result in improper alignment */
|
|
*((int *)((char *) destp + size - sizeof (int))) = 0;
|
|
|
|
return (destp);
|
|
}
|
|
|
|
/*
|
|
*
|
|
*/
|
|
TNFW_B_STATUS
|
|
tnfw_b_xcommit(TNFW_B_WCB *wcb)
|
|
{
|
|
TNFW_B_POS *pos;
|
|
tnf_block_header_t *block;
|
|
volatile tnf_buf_file_header_t *fh =
|
|
/* LINTED pointer cast may result in improper alignment */
|
|
(volatile tnf_buf_file_header_t *)_tnfw_b_control->tnf_buffer;
|
|
|
|
#ifdef TNFWB_DEBUG
|
|
fprintf(stderr, "tnfw_b_xcommit \n");
|
|
#endif
|
|
pos = &wcb->tnfw_w_pos;
|
|
block = pos->tnfw_w_uncommitted;
|
|
while (block && (block != pos->tnfw_w_block)) {
|
|
#ifdef TNFWB_DEBUG
|
|
fprintf(stderr, "commit %d: block = 0x%x, last = 0x%x\n",
|
|
block->generation, block, pos->tnfw_w_block);
|
|
#endif
|
|
block->bytes_valid = fh->com.block_size;
|
|
pos->tnfw_w_uncommitted = block->next_block;
|
|
tnfw_b_clear_lock(&block->A_lock);
|
|
block = pos->tnfw_w_uncommitted;
|
|
}
|
|
if (block != NULL) {
|
|
#ifdef TNFWB_DEBUG
|
|
fprintf(stderr, "commit last %d: block = 0x%x, offset = 0x%x\n",
|
|
block->generation, block, pos->tnfw_w_write_off);
|
|
#endif
|
|
block->bytes_valid = pos->tnfw_w_write_off;
|
|
}
|
|
pos->tnfw_w_uncommitted = NULL;
|
|
#ifdef TNFWB_MAY_RELEASE_A_LOCK
|
|
if (0) { /* XXXX Do we or don't we clear this lock? */
|
|
wcb->tnfw_w_generation = block->generation;
|
|
tnfw_b_clear_lock(&block->A_lock);
|
|
wcb->tnfw_w_a_lock_released = B_TRUE;
|
|
}
|
|
#endif
|
|
pos = &wcb->tnfw_w_tag_pos;
|
|
block = pos->tnfw_w_uncommitted;
|
|
while (block && (block != pos->tnfw_w_block)) {
|
|
#ifdef TNFWB_DEBUG
|
|
fprintf(stderr, "commit %d: block = 0x%x, last = 0x%x\n",
|
|
thr_self(), block, pos->tnfw_w_block);
|
|
#endif
|
|
block->bytes_valid = fh->com.block_size;
|
|
pos->tnfw_w_uncommitted = block->next_block;
|
|
block = pos->tnfw_w_uncommitted;
|
|
}
|
|
if (block != NULL)
|
|
block->bytes_valid = pos->tnfw_w_write_off;
|
|
pos->tnfw_w_uncommitted = NULL;
|
|
return (TNFW_B_OK);
|
|
}
|
|
|
|
/*
|
|
*
|
|
*/
|
|
TNFW_B_STATUS
|
|
tnfw_b_xabort(TNFW_B_WCB *wcb)
|
|
{
|
|
TNFW_B_POS *pos = &wcb->tnfw_w_pos;
|
|
tnf_block_header_t *block, *next;
|
|
volatile tnf_buf_file_header_t *fh =
|
|
/* LINTED pointer cast may result in improper alignment */
|
|
(volatile tnf_buf_file_header_t *)_tnfw_b_control->tnf_buffer;
|
|
|
|
block = pos->tnfw_w_block = pos->tnfw_w_uncommitted;
|
|
if (block != NULL) {
|
|
pos->tnfw_w_write_off = block->bytes_valid;
|
|
#ifdef TNFWB_MAY_RELEASE_A_LOCK
|
|
if (0) { /* XXXX */
|
|
tnfw_b_clear_lock(&block->A_lock);
|
|
wcb->tnfw_w_generation = block->generation;
|
|
wcb->tnfw_w_a_lock_released = B_TRUE;
|
|
}
|
|
#endif
|
|
block = block->next_block;
|
|
}
|
|
while (block != NULL) {
|
|
next = block->next_block;
|
|
tnfw_b_clear_lock(&block->A_lock);
|
|
block = next;
|
|
}
|
|
pos->tnfw_w_uncommitted = NULL;
|
|
pos = &wcb->tnfw_w_tag_pos;
|
|
block = pos->tnfw_w_uncommitted;
|
|
while (block && (block != pos->tnfw_w_block)) {
|
|
block->bytes_valid = fh->com.block_size;
|
|
pos->tnfw_w_uncommitted = block->next_block;
|
|
block = pos->tnfw_w_uncommitted;
|
|
}
|
|
if (block != NULL)
|
|
block->bytes_valid = pos->tnfw_w_write_off;
|
|
pos->tnfw_w_uncommitted = NULL;
|
|
return (TNFW_B_OK);
|
|
}
|
|
|
|
/*
|
|
* The kernel version is different because we can use a spin mutex
|
|
* in the kernel, and not all SPARC systems support the SWAP instruction.
|
|
*/
|
|
#ifdef _KERNEL
|
|
/*ARGSUSED0*/
|
|
tnf_uint32_t *
|
|
tnfw_b_fw_alloc(TNFW_B_WCB *wcb)
|
|
{
|
|
tnf_uint32_t *ret_val;
|
|
volatile tnf_buf_file_header_t *fh =
|
|
/* LINTED pointer cast may result in improper alignment */
|
|
(volatile tnf_buf_file_header_t *)_tnfw_b_control->tnf_buffer;
|
|
tnf_uint32_t *zone_end = (tnf_uint32_t *)((char *)fh + TNFW_B_FW_ZONE);
|
|
mutex_enter(&hintlock);
|
|
ret_val = (tnf_uint32_t *)((char *)fh + fh->next_fw_alloc);
|
|
if (ret_val != zone_end)
|
|
fh->next_fw_alloc += sizeof (tnf_uint32_t);
|
|
mutex_exit(&hintlock);
|
|
return ((ret_val != zone_end) ? ret_val : NULL);
|
|
}
|
|
|
|
#else
|
|
|
|
/*ARGSUSED0*/
|
|
tnf_uint32_t *
|
|
tnfw_b_fw_alloc(TNFW_B_WCB *wcb)
|
|
{
|
|
volatile tnf_buf_file_header_t *fh =
|
|
/* LINTED pointer cast may result in improper alignment */
|
|
(volatile tnf_buf_file_header_t *)_tnfw_b_control->tnf_buffer;
|
|
/* LINTED pointer cast may result in improper alignment */
|
|
u_long *hint = (u_long *)((char *)fh + fh->next_fw_alloc);
|
|
/* LINTED pointer cast may result in improper alignment */
|
|
u_long *zone_end = (u_long *)((char *)fh + TNFW_B_FW_ZONE);
|
|
u_long swapin;
|
|
|
|
while (hint != zone_end) {
|
|
if (*hint == 0) {
|
|
swapin = tnfw_b_atomic_swap(hint, TNFW_B_FW_INVALID);
|
|
if (swapin != 0) {
|
|
if (swapin != (unsigned)TNFW_B_FW_INVALID) {
|
|
/* restore */
|
|
*hint = swapin;
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
++hint;
|
|
}
|
|
fh->next_fw_alloc = (char *)hint - (char *)fh;
|
|
return ((hint != zone_end) ? hint : NULL);
|
|
}
|
|
|
|
#endif /* _KERNEL */
|