/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 1994-2003 Sun Microsytems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% 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;
uint_t hint_hi, hint_lo;
uint_t new_hint_hi, new_hint_lo;
uint_t generation;
uint_t blocknum;
uint_t prev_gen = 0;
uint_t prev_block = 0;
uint_t 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
#ifdef _TNF_VERBOSE
fprintf(stderr, "tnfw_b_alloc_block: \n");
#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;
/* LINTED pointer subtraction casted to 32 bits */
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
/* LINTED pointer subtraction casted to 32 bits */
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_gen >> (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);
}
static void release_block_from_pos(TNFW_B_POS * pos)
{
if (pos->tnfw_w_block == NULL)
return;
if (pos->tnfw_w_uncommitted != NULL)
return;
tnfw_b_clear_lock(&pos->tnfw_w_block->A_lock);
pos->tnfw_w_block = NULL;
}
void
tnfw_b_release_block(TNFW_B_WCB * wcb)
{
if (wcb == NULL)
return;
release_block_from_pos(&wcb->tnfw_w_tag_pos);
release_block_from_pos(&wcb->tnfw_w_pos);
}
/*
* 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;
#ifdef _TNF_VERBOSE
fprintf(stderr, "tnfw_b_init_buffer: \n");
#endif
/* 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));
/* LINTED size of tnf_ref_32_t known to be 32 */
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 */
/* LINTED pointer subtraction casted to 32 bits */
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, size_t 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;
#ifdef _TNF_VERBOSE
fprintf(stderr, "tnfw_b_alloc: \n");
#endif
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;
#ifdef _TNF_VERBOSE
fprintf(stderr, "tnfw_b_alloc returning %p\n", destp);
#endif
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
/*
* cope with the normal record block(s) first
*/
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
/*
* cope with the tag block(s)
*/
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 */
uint_t *hint = (uint_t *)((uintptr_t)fh + fh->next_fw_alloc);
/* LINTED pointer cast may result in improper alignment */
ulong_t *zone_end = (ulong_t *)((uintptr_t)fh + TNFW_B_FW_ZONE);
u_long swapin;
char tmp_buf[512];
tnf_uint32_t *retval;
#ifdef VERYVERBOSE
sprintf(tmp_buf, "tnfw_b_vw_alloc: begin\n");
(void) write(2, tmp_buf, strlen(tmp_buf));
#endif
#ifdef VERYVERBOSE
sprintf(tmp_buf, "tnfw_b_vw_alloc: (1)hint=%p\n", hint);
(void) write(2, tmp_buf, strlen(tmp_buf));
#endif
while ((uintptr_t)hint != (uintptr_t)zone_end) {
#ifdef VERYVERBOSE
sprintf(tmp_buf, "tnfw_b_vw_alloc: (2)hint=%p,zone_end=%p\n",
hint, zone_end);
(void) write(2, tmp_buf, strlen(tmp_buf));
#endif
#ifdef VERYVERBOSE
sprintf(tmp_buf, "tnfw_b_fw_alloc: fh = %p, next->alloc = %d\n",
fh, fh->next_fw_alloc);
(void) write(2, tmp_buf, strlen(tmp_buf));
sprintf(tmp_buf, "tnfw_b_vw_alloc: about to deref hint\n");
(void) write(2, tmp_buf, strlen(tmp_buf));
sprintf(tmp_buf, "tnfw_b_vw_alloc: *hint=%ld\n", *hint);
(void) write(2, tmp_buf, strlen(tmp_buf));
#endif
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;
#ifdef VERYVERBOSE
sprintf(tmp_buf, "tnfw_b_vw_alloc: (3)hint=%p\n", hint);
(void) write(2, tmp_buf, strlen(tmp_buf));
#endif
}
/* LINTED pointer subtraction casted to 32 bits */
fh->next_fw_alloc = (uint_t) ((char *)hint - (char *)fh);
retval = (((uintptr_t)hint != (uintptr_t)zone_end) ?
(tnf_uint32_t *)hint : NULL);
#ifdef VERYVERBOSE
sprintf(tmp_buf, "tnfw_b_vw_alloc: returning %p", retval);
(void) write(2, tmp_buf, strlen(tmp_buf));
#endif
return (retval);
}
#endif /* _KERNEL */