1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 *  Copyright 1994-2003 Sun Microsytems, Inc.  All rights reserved.
24 *  Use is subject to license terms.
25 */
26
27#pragma ident	"%Z%%M%	%I%	%E% SMI"
28
29#include <sys/types.h>
30#include <sys/param.h>
31#ifdef _KERNEL
32#include <sys/systm.h>		/* for bzero */
33#include <sys/spl.h>
34#include <sys/cmn_err.h>
35#else  /* _KERNEL */
36#include <string.h>		/* for memset */
37#endif /* _KERNEL */
38
39#include "tnf_buf.h"
40
41#ifdef TNFWB_DEBUG
42#ifdef _KERNEL
43#error TNFWB_DEBUG
44#else  /* _KERNEL */
45#include <stdio.h>
46#include <thread.h>
47#endif /* _KERNEL */
48#endif /* TNFW_DEBUG */
49
50/*
51 * Defines
52 */
53
54#define	TNFW_B_FW_INVALID 		0xffffffff
55#define	TNFW_B_ALLOC_LO_SELECTOR 	0x1
56#define	TNFW_B_MAXALLOCTRY 		200
57
58#ifdef TNF_BLOCK_STATS
59static struct {
60	int tnf_block_allocs;
61	int tnf_block_tries;
62	int tnf_max_block_tries;
63	int tnf_tag_blocks;
64	int tnf_generation_laps;
65	int tnf_a_locks;
66	int tnf_b_locks;
67} tnf_block_stats;
68#endif
69
70/*
71 * Regular record tag pointer - CAUTION - has to be in sync with tnf_tag
72 * macro in writer.h
73 */
74#define	TNFW_B_TAG_DIFF(item, ref)				\
75	((TNF_REF32_MAKE_PERMANENT((tnf_ref32_t)		\
76	    ((char *)(item) - (char *)(ref)))) | TNF_REF32_T_TAG)
77
78/*
79 * Exported interface by buffering layer to indicate where fowarding ptrs
80 * for file header and block header are.
81 */
82static tnf_buf_header_t forwarding_ptrs = {NULL, NULL, NULL};
83tnf_buf_header_t *_tnf_buf_headers_p = &forwarding_ptrs;
84
85#ifdef _KERNEL
86extern volatile caddr_t tnf_buf;
87
88static kmutex_t hintlock;
89#endif
90
91/*
92 * (Private) Allocate a new block.  Return NULL on failure.  'istag'
93 * is true if the block is to be non-reclaimable.
94 */
95static tnf_block_header_t *
96tnfw_b_alloc_block(TNFW_B_WCB *wcb, enum tnf_alloc_mode istag)
97{
98	tnf_block_header_t 	*block;
99	uint_t 			hint_hi, hint_lo;
100	uint_t			new_hint_hi, new_hint_lo;
101	uint_t 			generation;
102	uint_t			blocknum;
103	uint_t 			prev_gen = 0;
104	uint_t			prev_block = 0;
105	uint_t			i, b;
106	boolean_t 		gotit = B_FALSE;
107	volatile tnf_buf_file_header_t 	*fh;
108#ifdef TNF_BLOCK_STATS
109	register int tag_blocks = 0, generation_laps = 0, a_locks = 0,
110		b_locks = 0;
111#endif
112
113#ifdef _TNF_VERBOSE
114	fprintf(stderr, "tnfw_b_alloc_block: \n");
115#endif
116
117	if (_tnfw_b_control->tnf_state != TNFW_B_RUNNING) {
118#ifndef _KERNEL
119		if (_tnfw_b_control->tnf_state == TNFW_B_NOBUFFER)
120			if (_tnfw_b_control->tnf_init_callback() == 0)
121				return (NULL);
122#endif /* _KERNEL */
123		if (TNFW_B_IS_STOPPED(_tnfw_b_control->tnf_state))
124			return (NULL);
125		if (_tnfw_b_control->tnf_state == TNFW_B_BROKEN)
126			return (NULL);
127	}
128
129	/* LINTED pointer cast may result in improper alignment */
130	fh = (volatile tnf_buf_file_header_t *)_tnfw_b_control->tnf_buffer;
131	if (!wcb->tnfw_w_initialized) {
132		/* Get the block shift and generation shift values. */
133		b = 1;
134		wcb->tnfw_w_block_shift = wcb->tnfw_w_gen_shift = 0;
135		while (b != fh->com.block_size) {
136			b <<= 1;
137			++wcb->tnfw_w_block_shift;
138		}
139		b = 1;
140		while (b < fh->com.block_count) {
141			b <<= 1;
142			++wcb->tnfw_w_gen_shift;
143		}
144		wcb->tnfw_w_pid = _tnfw_b_control->tnf_pid;
145		wcb->tnfw_w_initialized = B_TRUE;
146	}
147
148	/*
149	 * If we need a tag block, check the reserved tag block space
150	 * first.  fh->next_tag_alloc is only a hint; it is updated
151	 * without concurrency control.
152	 */
153	if (istag && fh->next_tag_alloc < TNFW_B_DATA_BLOCK_BEGIN) {
154		i = fh->next_tag_alloc;
155		do {
156			/* LINTED pointer cast */
157			block = (tnf_block_header_t *) ((char *) fh + i);
158			if (!tnfw_b_get_lock(&block->A_lock) &&
159			    block->generation == 0)
160				break;
161			i += fh->com.block_size;
162		} while (i < TNFW_B_DATA_BLOCK_BEGIN);
163		if (i < TNFW_B_DATA_BLOCK_BEGIN) {
164			if (i > fh->next_tag_alloc)
165				fh->next_tag_alloc = i;
166			blocknum = i >> wcb->tnfw_w_block_shift;
167			if (blocknum > fh->com.blocks_valid)
168				fh->com.blocks_valid = blocknum;
169			/* LINTED pointer subtraction casted to 32 bits */
170			block->tag = TNFW_B_TAG_DIFF(
171			    forwarding_ptrs.fw_block_header, fh);
172			/* LINTED constant truncated by assignment */
173			block->generation = TNF_TAG_GENERATION_NUM;
174			block->bytes_valid = sizeof (tnf_block_header_t);
175			block->next_block = NULL;
176			tnfw_b_clear_lock(&block->A_lock);
177			return (block);
178		}
179	}
180
181	for (i = 0; !gotit && i != TNFW_B_MAXALLOCTRY; ++i) {
182		hint_hi = fh->next_alloc.hi;
183		hint_lo = (hint_hi & TNFW_B_ALLOC_LO_SELECTOR)
184			? fh->next_alloc.lo[1] : fh->next_alloc.lo[0];
185		generation = (hint_hi << (32 - wcb->tnfw_w_gen_shift)) |
186			(hint_lo >> wcb->tnfw_w_gen_shift);
187		blocknum = hint_lo & ((1 << wcb->tnfw_w_gen_shift) - 1);
188#ifdef TNFWB_DEBUG
189		fprintf(stderr, "alloc_block (%d): read hint (%d, %d)\n",
190		    thr_self(), generation, blocknum);
191#endif
192		if ((prev_gen == generation && prev_block > blocknum) ||
193		    prev_gen > generation) {
194			generation = prev_gen;
195			blocknum = prev_block;
196		}
197#ifdef TNFWB_DEBUG
198		fprintf(stderr,
199		    "alloc_block (%d): trying blocknum = %d, gen %d\n",
200		    thr_self(), blocknum, generation);
201#endif
202		block = (tnf_block_header_t *)
203		/* LINTED pointer cast may result in improper alignment */
204			((char *)fh + blocknum * fh->com.block_size);
205#ifdef TNF_BLOCK_STATS
206		if (block->generation == TNF_TAG_GENERATION_NUM)
207			++tag_blocks;
208		else if (block->generation >= generation)
209			++generation_laps;
210		else if (tnfw_b_get_lock(&block->A_lock))
211			++a_locks;
212		else if (block->generation == TNF_TAG_GENERATION_NUM)
213			++tag_blocks;
214		else if (block->generation >= generation)
215			++generation_laps;
216		else if (tnfw_b_get_lock(&block->B_lock)) {
217			tnfw_b_clear_lock(&block->A_lock);
218			++b_locks;
219		} else
220			gotit = B_TRUE;
221
222#else
223		if (block->generation < generation &&
224		    !tnfw_b_get_lock(&block->A_lock)) {
225			if (block->generation < generation &&
226			    !tnfw_b_get_lock(&block->B_lock)) {
227				gotit = B_TRUE;
228			} else {
229				tnfw_b_clear_lock(&block->A_lock);
230			}
231		}
232#endif
233		prev_block = blocknum + 1;
234		prev_gen = generation;
235		if (prev_block == fh->com.block_count) {
236			prev_block =
237			    TNFW_B_DATA_BLOCK_BEGIN >> wcb->tnfw_w_block_shift;
238			++prev_gen;
239		}
240		if (blocknum > fh->com.blocks_valid) {
241			fh->com.blocks_valid = blocknum;
242		}
243	}
244
245	if (i == TNFW_B_MAXALLOCTRY) {
246		_tnfw_b_control->tnf_state = TNFW_B_BROKEN;
247		return (NULL);
248	}
249#ifdef TNFWB_DEBUG
250	fprintf(stderr,
251	    "alloc_block (%d): got blocknum = %d, gen %d, block at 0x%x\n",
252	    thr_self(), blocknum, generation, block);
253#endif
254	/* LINTED pointer subtraction casted to 32 bits */
255	block->tag = TNFW_B_TAG_DIFF(forwarding_ptrs.fw_block_header, fh);
256	block->generation = (istag) ? TNF_TAG_GENERATION_NUM : generation;
257	block->bytes_valid = sizeof (tnf_block_header_t);
258	block->next_block = NULL;
259	if (istag) {
260		tnfw_b_clear_lock(&block->A_lock);
261	}
262	tnfw_b_clear_lock(&block->B_lock);
263
264	/*
265	 * Read the hint one more time, only update it if we'll be increasing
266	 * it
267	 */
268	new_hint_hi = prev_gen >> (32 - wcb->tnfw_w_gen_shift);
269	new_hint_lo = prev_block | (prev_gen << wcb->tnfw_w_gen_shift);
270#ifdef _KERNEL
271	mutex_enter(&hintlock);
272#endif
273	hint_hi = fh->next_alloc.hi;
274	hint_lo = (hint_hi & TNFW_B_ALLOC_LO_SELECTOR) ?
275		fh->next_alloc.lo[1] : fh->next_alloc.lo[0];
276
277	if ((new_hint_hi == hint_hi && new_hint_lo > hint_lo) ||
278	    new_hint_hi > hint_hi) {
279		/*
280		 * Order is important here!  It is the write to next_alloc.hi
281		 * that atomically records the new value.
282		 */
283		if (new_hint_hi & TNFW_B_ALLOC_LO_SELECTOR)
284			fh->next_alloc.lo[1] = new_hint_lo;
285		else
286			fh->next_alloc.lo[0] = new_hint_lo;
287		fh->next_alloc.hi = new_hint_hi;
288#ifdef TNFWB_DEBUG
289		fprintf(stderr, "alloc_block (%d): wrote hint (%d, %d)\n",
290		    thr_self(), prev_gen, prev_block);
291#endif
292	}
293#ifdef _KERNEL
294	mutex_exit(&hintlock);
295#endif
296#ifdef TNF_BLOCK_STATS
297	++tnf_block_stats.tnf_block_allocs;
298	tnf_block_stats.tnf_block_tries += i;
299	if (i > tnf_block_stats.tnf_max_block_tries) {
300		tnf_block_stats.tnf_max_block_tries = i;
301		tnf_block_stats.tnf_tag_blocks = tag_blocks;
302		tnf_block_stats.tnf_generation_laps = generation_laps;
303		tnf_block_stats.tnf_a_locks = a_locks;
304		tnf_block_stats.tnf_b_locks = b_locks;
305	}
306#endif
307	return (block);
308}
309
310static void release_block_from_pos(TNFW_B_POS * pos)
311{
312	if (pos->tnfw_w_block == NULL)
313		return;
314	if (pos->tnfw_w_uncommitted != NULL)
315		return;
316	tnfw_b_clear_lock(&pos->tnfw_w_block->A_lock);
317	pos->tnfw_w_block = NULL;
318}
319
320void
321tnfw_b_release_block(TNFW_B_WCB * wcb)
322{
323	if (wcb == NULL)
324		return;
325	release_block_from_pos(&wcb->tnfw_w_tag_pos);
326	release_block_from_pos(&wcb->tnfw_w_pos);
327}
328
329/*
330 * Initialize a buffer.  NOT RE-ENTRANT!  Block sizes other than 512
331 * are currently rejected.  The code "ought to work" with any block
332 * size that is an integral power of 2.  'zfod' states whether we
333 * can assume that the buffer is zero-filled (or paged-in zero-fill-on-demand).
334 */
335TNFW_B_STATUS
336tnfw_b_init_buffer(char *buf, int blocks, int block_size, boolean_t zfod)
337
338{
339	int 	block_shift, gen_shift;
340	int 	i;
341	int	file_size;
342	unsigned b;
343	tnf_block_header_t *block;
344	/* LINTED pointer cast may result in improper alignment */
345	tnf_buf_file_header_t *fh = (tnf_buf_file_header_t *)buf;
346
347#ifdef _TNF_VERBOSE
348	fprintf(stderr, "tnfw_b_init_buffer: \n");
349#endif
350
351	/* Check for 512 could go away. */
352	if (block_size != 512 || block_size < sizeof (tnf_buf_file_header_t))
353		return (TNFW_B_BAD_BLOCK_SIZE);
354	/*
355	 * Check to see if block size is a power of 2, and get
356	 * log2(block size).
357	 */
358	for (b = (unsigned)block_size, block_shift = 0; (b & 1) == 0; b >>= 1)
359		++block_shift;
360	if (b != 1)
361		return (TNFW_B_BAD_BLOCK_SIZE);
362	gen_shift = 0;
363	while (b < blocks) {
364		b <<= 1;
365		++gen_shift;
366	}
367	/* reserve first two words for file header tag and block header tag */
368	forwarding_ptrs.fw_file_header  = (char *)fh + block_size;
369	forwarding_ptrs.fw_block_header = (char *)fh + block_size +
370		sizeof (tnf_ref32_t);
371	forwarding_ptrs.fw_root = (char *)fh + block_size +
372		(2 * sizeof (tnf_ref32_t));
373	/* LINTED size of tnf_ref_32_t known to be 32 */
374	fh->next_fw_alloc = block_size + (3 * sizeof (tnf_ref32_t));
375	/* fill in rest of file header */
376	fh->magic = TNF_MAGIC;
377	/* Self relative pointer to tag */
378	/* LINTED pointer subtraction casted to 32 bits */
379	fh->com.tag = TNFW_B_TAG_DIFF(forwarding_ptrs.fw_file_header, fh);
380	fh->com.file_version = TNF_FILE_VERSION;
381	fh->com.file_header_size = sizeof (tnf_file_header_t);
382	/* fill in fh->com.file_log_size */
383	b = 1;
384	file_size = blocks * block_size;
385	fh->com.file_log_size = 0;
386	while (b < file_size) {
387		b <<= 1;
388		++fh->com.file_log_size;
389	}
390
391	fh->com.block_header_size = sizeof (tnf_block_header_t);
392	fh->com.block_size = block_size;
393	fh->com.directory_size = TNFW_B_FW_ZONE;
394	fh->com.block_count = blocks;
395	fh->com.blocks_valid = TNFW_B_FW_ZONE >> block_shift;
396	if (fh->com.blocks_valid == 0)
397		fh->com.blocks_valid = 1;
398	fh->next_tag_alloc = TNFW_B_FW_ZONE;
399	fh->next_alloc.hi = 0;
400	fh->next_alloc.lo[0] =
401	    (1 << gen_shift) | (TNFW_B_DATA_BLOCK_BEGIN >> block_shift);
402#ifdef TNFWB_DEBUG
403	fprintf(stderr, "gen_shift = %d, blocks_valid = %d\n",
404	    gen_shift, fh->com.blocks_valid);
405	fprintf(stderr, "alloc hint initialized to (%d, %d, %d)\n",
406	    fh->next_alloc.hi, fh->next_alloc.lo[0], fh->next_alloc.lo[1]);
407#endif
408	if (!zfod) {
409		for (i = 1; i < (TNFW_B_FW_ZONE >> block_shift); ++i) {
410#ifdef _KERNEL
411			bzero(buf + (i << block_shift), block_size);
412#else
413			(void) memset(buf + (i << block_shift), 0, block_size);
414#endif
415		}
416		for (; i != blocks; ++i) {
417			block =	(tnf_block_header_t *)
418				/* LINTED pointer cast */
419				(buf + (i << block_shift));
420			block->tag = 0;
421			block->generation = 0;
422			tnfw_b_clear_lock(&block->A_lock);
423			tnfw_b_clear_lock(&block->B_lock);
424		}
425	}
426#ifdef _KERNEL
427	mutex_init(&hintlock, "tnf buffer hint lock", MUTEX_SPIN_DEFAULT,
428	    (void *) ipltospl(LOCK_LEVEL));
429#endif
430	return (TNFW_B_OK);
431}
432
433/*
434 *
435 */
436void *
437tnfw_b_alloc(TNFW_B_WCB *wcb, size_t size, enum tnf_alloc_mode istag)
438{
439	TNFW_B_POS 	*pos;
440	int 		offset;
441	void 		*destp;
442	volatile tnf_buf_file_header_t *fh;
443	tnf_block_header_t *block, *new_block;
444
445#ifdef _TNF_VERBOSE
446	fprintf(stderr, "tnfw_b_alloc: \n");
447#endif
448
449	if (_tnfw_b_control->tnf_state != TNFW_B_RUNNING) {
450		if (TNFW_B_IS_STOPPED(_tnfw_b_control->tnf_state))
451			return (NULL);
452		if (_tnfw_b_control->tnf_state == TNFW_B_FORKED &&
453		    _tnfw_b_control->tnf_pid != wcb->tnfw_w_pid) {
454			wcb->tnfw_w_pos.tnfw_w_block =
455				wcb->tnfw_w_pos.tnfw_w_uncommitted =
456				wcb->tnfw_w_tag_pos.tnfw_w_block =
457				wcb->tnfw_w_tag_pos.tnfw_w_uncommitted = NULL;
458			wcb->tnfw_w_pid = _tnfw_b_control->tnf_pid;
459			_tnfw_b_control->tnf_fork_callback();
460		}
461	}
462
463	/* Round size up to a multiple of 8. */
464	size = (size + 7) & ~7;
465
466	/* LINTED pointer cast may result in improper alignment */
467	fh = (volatile tnf_buf_file_header_t *)_tnfw_b_control->tnf_buffer;
468	pos = (istag) ? &wcb->tnfw_w_tag_pos : &wcb->tnfw_w_pos;
469	block = pos->tnfw_w_block;
470	/* Check size within range. */
471#ifdef TNFWB_SAFER
472	if (size > fh->com.block_size - sizeof (tnf_block_header_t))
473		/* TNFW_B_RECORD_TOO_BIG */
474		return (NULL);
475#endif
476	offset = pos->tnfw_w_write_off;
477#ifdef TNFWB_MAY_RELEASE_A_LOCK
478	if (block != NULL && wcb->tnfw_w_a_lock_released) {
479		/* re-acquire the A-lock for the current block */
480		if (!tnfw_b_get_lock(&block->A_lock)) {
481			wcb->tnfw_w_a_lock_released = B_FALSE;
482			if (wcb->tnfw_w_generation != block->generation) {
483				tnfw_b_clear_lock(&block->A_lock);
484				wcb->tnfw_w_pos.tnfw_w_block = NULL;
485			}
486		} else {
487			wcb->tnfw_w_pos.tnfw_w_block = NULL;
488		}
489	}
490#endif
491	if (block == NULL || offset + size > fh->com.block_size) {
492		new_block = tnfw_b_alloc_block(wcb, istag);
493		if (new_block == NULL) {
494			/* TNFW_B_ACKPHT */
495			return (NULL);
496		}
497#ifdef TNFWB_DEBUG
498		fprintf(stderr,
499		    "wcb 0x%x: new block at 0x%x, old block is 0x%x, "
500		    "uncommitted is 0x%x\n",
501		    wcb, new_block, block, pos->tnfw_w_uncommitted);
502#endif
503		if (block != NULL) {
504			/* XXXX is this what we want for padding? */
505#ifdef _KERNEL
506			(void) bzero((char *)block + offset,
507			    fh->com.block_size - offset);
508#else
509			(void) memset((char *)block + offset, 0,
510			    fh->com.block_size - offset);
511#endif
512			if (pos->tnfw_w_uncommitted == NULL) {
513#ifdef TNFWB_MAY_RELEASE_A_LOCK
514				/* Could still be holding the A-lock on block */
515				if (!wcb->tnfw_w_a_lock_released)
516					tnfw_b_clear_lock(&block->A_lock);
517#else
518				/* Definitely still holding the A-lock */
519				tnfw_b_clear_lock(&block->A_lock);
520#endif	/* TNFWB_MAY_RELEASE_A_LOCK */
521			}
522		}
523		/* Add new_block to the list of uncommitted blocks. */
524		if (pos->tnfw_w_uncommitted == NULL) {
525			pos->tnfw_w_uncommitted = new_block;
526		} else {
527			/* Assert(block != NULL); */
528			block->next_block = new_block;
529		}
530		pos->tnfw_w_block = new_block;
531		pos->tnfw_w_write_off = new_block->bytes_valid;
532	} else if (pos->tnfw_w_uncommitted == NULL) {
533		pos->tnfw_w_uncommitted = block;
534	}
535	destp = (char *)pos->tnfw_w_block + pos->tnfw_w_write_off;
536	pos->tnfw_w_write_off += size;
537	/*
538	 * Unconditionally write a 0 into the last word allocated,
539	 * in case we left an alignment gap.  (Assume that doing an
540	 * unconditional write is cheaper than testing and branching
541	 * around the write half the time.)
542	 */
543	/* LINTED pointer cast may result in improper alignment */
544	*((int *)((char *) destp + size - sizeof (int))) = 0;
545
546#ifdef _TNF_VERBOSE
547	fprintf(stderr, "tnfw_b_alloc returning %p\n", destp);
548#endif
549	return (destp);
550}
551
552/*
553 *
554 */
555TNFW_B_STATUS
556tnfw_b_xcommit(TNFW_B_WCB *wcb)
557{
558	TNFW_B_POS *pos;
559	tnf_block_header_t *block;
560	volatile tnf_buf_file_header_t *fh =
561		/* LINTED pointer cast may result in improper alignment */
562		(volatile tnf_buf_file_header_t *)_tnfw_b_control->tnf_buffer;
563
564#ifdef TNFWB_DEBUG
565	fprintf(stderr, "tnfw_b_xcommit \n");
566#endif
567
568	/*
569	 * cope with the normal record block(s) first
570	 */
571
572	pos = &wcb->tnfw_w_pos;
573	block = pos->tnfw_w_uncommitted;
574	while (block && (block != pos->tnfw_w_block)) {
575#ifdef TNFWB_DEBUG
576		fprintf(stderr, "commit %d: block = 0x%x, last = 0x%x\n",
577		    block->generation, block, pos->tnfw_w_block);
578#endif
579		block->bytes_valid = fh->com.block_size;
580		pos->tnfw_w_uncommitted = block->next_block;
581		tnfw_b_clear_lock(&block->A_lock);
582		block = pos->tnfw_w_uncommitted;
583	}
584	if (block != NULL) {
585#ifdef TNFWB_DEBUG
586		fprintf(stderr, "commit last %d: block = 0x%x, offset = 0x%x\n",
587		    block->generation, block, pos->tnfw_w_write_off);
588#endif
589		block->bytes_valid = pos->tnfw_w_write_off;
590	}
591	pos->tnfw_w_uncommitted = NULL;
592#ifdef TNFWB_MAY_RELEASE_A_LOCK
593	if (0) {	/* XXXX Do we or don't we clear this lock? */
594		wcb->tnfw_w_generation = block->generation;
595		tnfw_b_clear_lock(&block->A_lock);
596		wcb->tnfw_w_a_lock_released = B_TRUE;
597	}
598#endif
599
600	/*
601	 * cope with the tag block(s)
602	 */
603
604	pos = &wcb->tnfw_w_tag_pos;
605	block = pos->tnfw_w_uncommitted;
606	while (block && (block != pos->tnfw_w_block)) {
607#ifdef TNFWB_DEBUG
608		fprintf(stderr, "commit %d: block = 0x%x, last = 0x%x\n",
609		    thr_self(), block, pos->tnfw_w_block);
610#endif
611		block->bytes_valid = fh->com.block_size;
612		pos->tnfw_w_uncommitted = block->next_block;
613		block = pos->tnfw_w_uncommitted;
614	}
615	if (block != NULL)
616		block->bytes_valid = pos->tnfw_w_write_off;
617	pos->tnfw_w_uncommitted = NULL;
618	return (TNFW_B_OK);
619}
620
621/*
622 *
623 */
624TNFW_B_STATUS
625tnfw_b_xabort(TNFW_B_WCB *wcb)
626{
627	TNFW_B_POS *pos = &wcb->tnfw_w_pos;
628	tnf_block_header_t *block, *next;
629	volatile tnf_buf_file_header_t *fh =
630		/* LINTED pointer cast may result in improper alignment */
631		(volatile tnf_buf_file_header_t *)_tnfw_b_control->tnf_buffer;
632
633	block = pos->tnfw_w_block = pos->tnfw_w_uncommitted;
634	if (block != NULL) {
635		pos->tnfw_w_write_off = block->bytes_valid;
636#ifdef TNFWB_MAY_RELEASE_A_LOCK
637		if (0) {		/* XXXX */
638			tnfw_b_clear_lock(&block->A_lock);
639			wcb->tnfw_w_generation = block->generation;
640			wcb->tnfw_w_a_lock_released = B_TRUE;
641		}
642#endif
643		block = block->next_block;
644	}
645	while (block != NULL) {
646		next = block->next_block;
647		tnfw_b_clear_lock(&block->A_lock);
648		block = next;
649	}
650	pos->tnfw_w_uncommitted = NULL;
651	pos = &wcb->tnfw_w_tag_pos;
652	block = pos->tnfw_w_uncommitted;
653	while (block && (block != pos->tnfw_w_block)) {
654		block->bytes_valid = fh->com.block_size;
655		pos->tnfw_w_uncommitted = block->next_block;
656		block = pos->tnfw_w_uncommitted;
657	}
658	if (block != NULL)
659		block->bytes_valid = pos->tnfw_w_write_off;
660	pos->tnfw_w_uncommitted = NULL;
661	return (TNFW_B_OK);
662}
663
664/*
665 * The kernel version is different because we can use a spin mutex
666 * in the kernel, and not all SPARC systems support the SWAP instruction.
667 */
668#ifdef _KERNEL
669/*ARGSUSED0*/
670tnf_uint32_t *
671tnfw_b_fw_alloc(TNFW_B_WCB *wcb)
672{
673	tnf_uint32_t *ret_val;
674	volatile tnf_buf_file_header_t *fh =
675		/* LINTED pointer cast may result in improper alignment */
676		(volatile tnf_buf_file_header_t *)_tnfw_b_control->tnf_buffer;
677	tnf_uint32_t *zone_end = (tnf_uint32_t *)((char *)fh + TNFW_B_FW_ZONE);
678	mutex_enter(&hintlock);
679	ret_val = (tnf_uint32_t *)((char *)fh + fh->next_fw_alloc);
680	if (ret_val != zone_end)
681		fh->next_fw_alloc += sizeof (tnf_uint32_t);
682	mutex_exit(&hintlock);
683	return ((ret_val != zone_end) ? ret_val : NULL);
684}
685
686#else
687
688/*ARGSUSED0*/
689tnf_uint32_t *
690tnfw_b_fw_alloc(TNFW_B_WCB *wcb)
691{
692	volatile tnf_buf_file_header_t *fh =
693		/* LINTED pointer cast may result in improper alignment */
694		(volatile tnf_buf_file_header_t *)_tnfw_b_control->tnf_buffer;
695	/* LINTED pointer cast may result in improper alignment */
696	uint_t *hint = (uint_t *)((uintptr_t)fh + fh->next_fw_alloc);
697	/* LINTED pointer cast may result in improper alignment */
698	ulong_t *zone_end = (ulong_t *)((uintptr_t)fh + TNFW_B_FW_ZONE);
699	u_long swapin;
700	char tmp_buf[512];
701	tnf_uint32_t *retval;
702
703#ifdef VERYVERBOSE
704	    sprintf(tmp_buf, "tnfw_b_vw_alloc: begin\n");
705	    (void) write(2, tmp_buf, strlen(tmp_buf));
706#endif
707
708#ifdef VERYVERBOSE
709	    sprintf(tmp_buf, "tnfw_b_vw_alloc: (1)hint=%p\n", hint);
710	    (void) write(2, tmp_buf, strlen(tmp_buf));
711#endif
712
713	while ((uintptr_t)hint != (uintptr_t)zone_end) {
714#ifdef VERYVERBOSE
715	    sprintf(tmp_buf, "tnfw_b_vw_alloc: (2)hint=%p,zone_end=%p\n",
716		    hint, zone_end);
717	    (void) write(2, tmp_buf, strlen(tmp_buf));
718#endif
719
720#ifdef VERYVERBOSE
721	sprintf(tmp_buf, "tnfw_b_fw_alloc: fh = %p, next->alloc = %d\n",
722		fh, fh->next_fw_alloc);
723	(void) write(2, tmp_buf, strlen(tmp_buf));
724
725	    sprintf(tmp_buf, "tnfw_b_vw_alloc: about to deref hint\n");
726	    (void) write(2, tmp_buf, strlen(tmp_buf));
727
728	    sprintf(tmp_buf, "tnfw_b_vw_alloc: *hint=%ld\n", *hint);
729	    (void) write(2, tmp_buf, strlen(tmp_buf));
730#endif
731		if (*hint == 0) {
732			swapin = tnfw_b_atomic_swap(hint, TNFW_B_FW_INVALID);
733			if (swapin != 0) {
734				if (swapin != (unsigned)TNFW_B_FW_INVALID) {
735					/* restore */
736					*hint = swapin;
737				}
738			} else {
739				break;
740			}
741		}
742		++hint;
743#ifdef VERYVERBOSE
744	    sprintf(tmp_buf, "tnfw_b_vw_alloc: (3)hint=%p\n", hint);
745	    (void) write(2, tmp_buf, strlen(tmp_buf));
746#endif
747
748	}
749	/* LINTED pointer subtraction casted to 32 bits */
750	fh->next_fw_alloc = (uint_t) ((char *)hint - (char *)fh);
751	retval = (((uintptr_t)hint != (uintptr_t)zone_end) ?
752		(tnf_uint32_t *)hint : NULL);
753
754#ifdef VERYVERBOSE
755	sprintf(tmp_buf, "tnfw_b_vw_alloc: returning %p", retval);
756	(void) write(2, tmp_buf, strlen(tmp_buf));
757#endif
758
759	return (retval);
760}
761
762#endif	/* _KERNEL */
763