xref: /titanic_41/usr/src/lib/libtnfprobe/tnf_buf.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
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
59 static 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  */
82 static tnf_buf_header_t forwarding_ptrs = {NULL, NULL, NULL};
83 tnf_buf_header_t *_tnf_buf_headers_p = &forwarding_ptrs;
84 
85 #ifdef _KERNEL
86 extern volatile caddr_t tnf_buf;
87 
88 static 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  */
95 static tnf_block_header_t *
tnfw_b_alloc_block(TNFW_B_WCB * wcb,enum tnf_alloc_mode istag)96 tnfw_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 
release_block_from_pos(TNFW_B_POS * pos)310 static 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 
320 void
tnfw_b_release_block(TNFW_B_WCB * wcb)321 tnfw_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  */
335 TNFW_B_STATUS
tnfw_b_init_buffer(char * buf,int blocks,int block_size,boolean_t zfod)336 tnfw_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  */
436 void *
tnfw_b_alloc(TNFW_B_WCB * wcb,size_t size,enum tnf_alloc_mode istag)437 tnfw_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  */
555 TNFW_B_STATUS
tnfw_b_xcommit(TNFW_B_WCB * wcb)556 tnfw_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  */
624 TNFW_B_STATUS
tnfw_b_xabort(TNFW_B_WCB * wcb)625 tnfw_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*/
670 tnf_uint32_t *
tnfw_b_fw_alloc(TNFW_B_WCB * wcb)671 tnfw_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*/
689 tnf_uint32_t *
tnfw_b_fw_alloc(TNFW_B_WCB * wcb)690 tnfw_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