Home | History | Annotate | Download | only in tnf
      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 (c) 1994-2000 by Sun Microsystems, Inc.
     24  * All rights reserved.
     25  */
     26 
     27 #pragma ident	"@(#)tnf_buf.c	1.36	05/06/08 SMI"
     28 
     29 #include <sys/types.h>
     30 #include <sys/param.h>
     31 #include <sys/systm.h>		/* for bzero */
     32 #include <sys/machlock.h>
     33 #include <sys/spl.h>
     34 #include <sys/promif.h>
     35 #include <sys/debug.h>
     36 
     37 #include "tnf_buf.h"
     38 
     39 /*
     40  * Defines
     41  */
     42 
     43 #define	TNFW_B_ALLOC_LO		0x1
     44 #define	TNFW_B_MAXALLOCTRY 	32
     45 
     46 #define	TNF_MAXALLOC		(TNF_BLOCK_SIZE - sizeof (tnf_block_header_t))
     47 
     48 /*
     49  * Globals
     50  */
     51 
     52 TNFW_B_STATE tnfw_b_state = TNFW_B_NOBUFFER | TNFW_B_STOPPED;
     53 
     54 /*
     55  * Locals
     56  */
     57 
     58 static int	spinlock_spl;
     59 
     60 /*
     61  * Declarations
     62  */
     63 
     64 static tnf_block_header_t *tnfw_b_alloc_block(tnf_buf_file_header_t *,
     65     enum tnf_alloc_mode);
     66 
     67 /*
     68  * (Private) Allocate a new block.  Return NULL on failure and mark
     69  * tracing as broken.  'istag' is non-zero if the block is to be
     70  * non-reclaimable.  All blocks are returned A-locked.
     71  */
     72 
     73 static tnf_block_header_t *
     74 tnfw_b_alloc_block(tnf_buf_file_header_t *fh, enum tnf_alloc_mode istag)
     75 {
     76 	tnf_block_header_t 	*block;
     77 	ulong_t			bcount;
     78 	ulong_t			tmp_bn, bn, new_bn;
     79 	ulong_t			tmp_gen, gen, new_gen;
     80 	ulong_t			next;
     81 	int			i;
     82 	lock_t			*lp;
     83 	ushort_t		spl;
     84 
     85 	if (tnfw_b_state != TNFW_B_RUNNING)
     86 		return (NULL);
     87 
     88 	lp = &fh->lock;
     89 
     90 	/*
     91 	 * Check reserved area first for tag block allocations
     92 	 * Tag allocations are rare, so we move the code out of line
     93 	 */
     94 	if (istag)
     95 		goto try_reserved;
     96 
     97 try_loop:
     98 	/*
     99 	 * Search for a block, using hint as starting point.
    100 	 */
    101 
    102 	bcount = fh->com.block_count;	/* total block count */
    103 
    104 	gen = fh->next_alloc.gen;
    105 	bn = fh->next_alloc.block[gen & TNFW_B_ALLOC_LO];
    106 
    107 	for (i = 0; i < TNFW_B_MAXALLOCTRY; i++) {
    108 
    109 		/*
    110 		 * Calculate next (not this) block to look for.
    111 		 * Needed for updating the hint.
    112 		 */
    113 		if ((new_bn = bn + 1) >= bcount) {
    114 			new_bn = TNFW_B_DATA_BLOCK_BEGIN >> TNF_BLOCK_SHIFT;
    115 			new_gen = gen + 1;
    116 		} else
    117 			new_gen = gen;
    118 
    119 		/*
    120 		 * Try to reserve candidate block
    121 		 */
    122 		/* LINTED pointer cast may result in improper alignment */
    123 		block = (tnf_block_header_t *)
    124 			((char *)fh + (bn << TNF_BLOCK_SHIFT));
    125 
    126 		if (lock_try(&block->A_lock))
    127 			if (block->generation < gen &&
    128 			    lock_try(&block->B_lock))
    129 				goto update_hint;
    130 			else
    131 				lock_clear(&block->A_lock);
    132 
    133 		/* Reload hint values */
    134 		gen = fh->next_alloc.gen;
    135 		bn = fh->next_alloc.block[gen & TNFW_B_ALLOC_LO];
    136 
    137 		/* adjust if we know a little better than the hint */
    138 		if ((new_bn > bn && new_gen == gen) || new_gen > gen) {
    139 			gen = new_gen;
    140 			bn = new_bn;
    141 		}
    142 	}
    143 
    144 	goto loop_fail;
    145 
    146 update_hint:
    147 	/*
    148 	 * Re-read the hint and update it only if we'll be increasing it.
    149 	 */
    150 	lock_set_spl(lp, spinlock_spl, &spl);
    151 	tmp_gen = fh->next_alloc.gen;
    152 	tmp_bn = fh->next_alloc.block[tmp_gen & TNFW_B_ALLOC_LO];
    153 
    154 	if ((new_gen == tmp_gen && new_bn > tmp_bn) || new_gen > tmp_gen) {
    155 		/*
    156 		 * Order is important here!  It is the write to
    157 		 * next_alloc.gen that atomically records the new
    158 		 * value.
    159 		 */
    160 		fh->next_alloc.block[new_gen & TNFW_B_ALLOC_LO] = new_bn;
    161 		fh->next_alloc.gen = new_gen;
    162 	}
    163 	lock_clear_splx(lp, spl);
    164 
    165 got_block:
    166 	/*
    167 	 * Initialize and return the block
    168 	 */
    169 	/* ASSERT(block->tag == TNF_BLOCK_HEADER_TAG); */
    170 	block->bytes_valid = sizeof (tnf_block_header_t);
    171 	block->next_block = NULL;
    172 	/* LINTED assignment of 64-bit integer to 32-bit integer */
    173 	block->generation = istag ? TNF_TAG_GENERATION_NUM : gen;
    174 	/* ASSERT(LOCK_HELD(&block->A_lock); */
    175 	lock_clear(&block->B_lock);
    176 	return (block);
    177 
    178 try_reserved:
    179 	/*
    180 	 * Look for a free tag block in reserved area
    181 	 */
    182 	next = fh->next_tag_alloc;
    183 	while (next < (TNFW_B_DATA_BLOCK_BEGIN >> TNF_BLOCK_SHIFT)) {
    184 		/* LINTED pointer cast may result in improper alignment */
    185 		block = (tnf_block_header_t *)
    186 			((char *)fh + (next << TNF_BLOCK_SHIFT));
    187 		next++;
    188 		/*
    189 		 * See if block is unclaimed.
    190 		 * Don't bother clearing the A-lock if the
    191 		 * block was claimed and released, since it
    192 		 * will never be reallocated anyway.
    193 		 */
    194 		if (lock_try(&block->A_lock) &&
    195 		    block->generation == 0) {
    196 			lock_set_spl(lp, spinlock_spl, &spl);
    197 			if (next > fh->next_tag_alloc)
    198 				fh->next_tag_alloc = next;
    199 			lock_clear_splx(lp, spl);
    200 			goto got_block;
    201 		}
    202 	}
    203 	goto try_loop;
    204 
    205 loop_fail:
    206 	/*
    207 	 * Only get here if we failed the for loop
    208 	 */
    209 	ASSERT(i == TNFW_B_MAXALLOCTRY);
    210 	tnfw_b_state = TNFW_B_BROKEN;
    211 #ifdef DEBUG
    212 	prom_printf("kernel probes: alloc_block failed\n");
    213 #endif
    214 	return (NULL);
    215 
    216 }
    217 
    218 /*
    219  * Allocate size bytes from the trace buffer.  Return NULL on failure,
    220  * and mark tracing as broken.  We're guaranteed that the buffer will
    221  * not be deallocated while we're in this routine.
    222  * Allocation requests must be word-sized and are word-aligned.
    223  */
    224 
    225 void *
    226 tnfw_b_alloc(TNFW_B_WCB *wcb, size_t size, enum tnf_alloc_mode istag)
    227 {
    228 	TNFW_B_POS 		*pos;
    229 	ushort_t		offset;
    230 	void 			*destp;
    231 	tnf_block_header_t	*block, *new_block;
    232 
    233 	pos = &wcb->tnfw_w_pos;	/* common case */
    234 	if (istag)
    235 		pos = &wcb->tnfw_w_tag_pos;
    236 	block = pos->tnfw_w_block;
    237 	offset = pos->tnfw_w_write_off;
    238 	/* Round size up to a multiple of 8. */
    239 	size = (size + 7) & ~7;
    240 
    241 	if (block == NULL || offset + size > TNF_BLOCK_SIZE) {
    242 
    243 		/* Get a new block */
    244 		/* LINTED pointer cast may result in improper alignment */
    245 		new_block = tnfw_b_alloc_block(TNF_FILE_HEADER(), istag);
    246 		if (new_block == NULL)
    247 			/* tracing has been marked as broken at this point */
    248 			return (NULL);
    249 
    250 		/* ASSERT(size <= TNF_MAXALLOC); */
    251 
    252 		/*
    253 		 * If the old block is clean (i.e., we're in a new
    254 		 * transaction), just release it.  Else, pad it out
    255 		 * and attach it to the list of uncommitted blocks.
    256 		 */
    257 		if (block != NULL) {
    258 			if (block->bytes_valid == offset &&
    259 			    !pos->tnfw_w_dirty) {
    260 				/* block is clean: release it */
    261 				lock_clear(&block->A_lock);
    262 			} else {
    263 				/* block is dirty */
    264 				ulong_t *p, *q;
    265 
    266 				/* LINTED pointer cast */
    267 				p = (ulong_t *)((char *)block + offset);
    268 				/* LINTED pointer cast */
    269 				q = (ulong_t *)((char *)block + TNF_BLOCK_SIZE);
    270 				while (p < q)
    271 					*p++ = TNF_NULL;
    272 
    273 				/* append block to release list */
    274 				new_block->next_block = block;
    275 
    276 				/* we have at least one dirty block */
    277 				pos->tnfw_w_dirty = 1;
    278 			}
    279 		}
    280 
    281 		/* make new_block the current block */
    282 		pos->tnfw_w_block = block = new_block;
    283 		/* write_off is updated below */
    284 		offset = sizeof (tnf_block_header_t);
    285 		/* ASSERT(new_block->bytes_valid == offset); */
    286 	}
    287 
    288 	destp = (char *)block + offset;
    289 	/* update write_off */
    290 	pos->tnfw_w_write_off = offset + size;
    291 	/*
    292 	 * Unconditionally write a 0 into the last word allocated,
    293 	 * in case we left an alignment gap.  (Assume that doing an
    294 	 * unconditional write is cheaper than testing and branching
    295 	 * around the write half the time.)
    296 	 */
    297 	/* LINTED pointer cast may result in improper alignment */
    298 	*((int *)((char *)destp + size - sizeof (int))) = 0;
    299 	return (destp);
    300 }
    301 
    302 /*
    303  * Allocate a directory entry.
    304  */
    305 
    306 /*ARGSUSED0*/
    307 void *
    308 tnfw_b_fw_alloc(TNFW_B_WCB *wcb)
    309 {
    310 	tnf_buf_file_header_t	*fh;
    311 	lock_t			*lp;
    312 	ushort_t		spl;
    313 	caddr_t			cell;
    314 	ulong_t			next;
    315 
    316 	/* LINTED pointer cast may result in improper alignment */
    317 	fh = TNF_FILE_HEADER();
    318 	lp = &fh->lock;
    319 
    320 	lock_set_spl(lp, spinlock_spl, &spl);
    321 	next = fh->next_fw_alloc;
    322 	if (next < TNFW_B_FW_ZONE) {
    323 		cell = (caddr_t)fh + next;
    324 		fh->next_fw_alloc = next + sizeof (tnf_ref32_t);
    325 	} else
    326 		cell = NULL;
    327 	lock_clear_splx(lp, spl);
    328 
    329 	return (cell);
    330 }
    331 
    332 /*
    333  * Initialize a buffer.
    334  */
    335 
    336 void
    337 tnfw_b_init_buffer(caddr_t buf, size_t size)
    338 {
    339 	int 	gen_shift;
    340 	int 	i;
    341 	ulong_t	b;
    342 	ulong_t	blocks;
    343 	tnf_block_header_t *block;
    344 	tnf_buf_file_header_t *fh;
    345 
    346 	/* Compute platform-specific spinlock_spl */
    347 	spinlock_spl = __ipltospl(LOCK_LEVEL + 1);
    348 
    349 	/* LINTED pointer cast may result in improper alignment */
    350 	fh = (tnf_buf_file_header_t *)buf;
    351 
    352 	/* LINTED logical expression always true: op "||" */
    353 	ASSERT(TNF_DIRECTORY_SIZE > TNF_BLOCK_SIZE);
    354 
    355 	/*
    356 	 * This assertion is needed because we cannot change
    357 	 * sys/tnf_com.h this late in the release cycle, but we need the
    358 	 * interface in sys/machlock.h for locking operations.
    359 	 */
    360 	/* LINTED logical expression always true: op "||" */
    361 	ASSERT(sizeof (tnf_byte_lock_t) == sizeof (lock_t));
    362 
    363 	/* Calculate number of blocks */
    364 	blocks = size >> TNF_BLOCK_SHIFT;
    365 
    366 	/* Calculate generation shift */
    367 	gen_shift = 0;
    368 	b = 1;
    369 	while (b < blocks) {
    370 		b <<= 1;
    371 		++gen_shift;
    372 	}
    373 	ASSERT(gen_shift < 32);
    374 
    375 	/* fill in file header */
    376 	/* magic number comes last */
    377 	/* LINTED constant truncated by assignment */
    378 	fh->com.tag = TNF_FILE_HEADER_TAG;
    379 	fh->com.file_version = TNF_FILE_VERSION;
    380 	fh->com.file_header_size = sizeof (tnf_file_header_t);
    381 	fh->com.file_log_size = gen_shift + TNF_BLOCK_SHIFT;
    382 	fh->com.block_header_size = sizeof (tnf_block_header_t);
    383 	fh->com.block_size = TNF_BLOCK_SIZE;
    384 	fh->com.directory_size = TNF_DIRECTORY_SIZE;
    385 	/* LINTED assignment of 64-bit integer to 32-bit integer */
    386 	fh->com.block_count = blocks;
    387 	/* com.blocks_valid is unused */
    388 	fh->next_alloc.gen = 1;
    389 	fh->next_alloc.block[0] = 0;
    390 	fh->next_alloc.block[1] = TNFW_B_DATA_BLOCK_BEGIN >> TNF_BLOCK_SHIFT;
    391 	fh->next_tag_alloc = TNF_DIRECTORY_SIZE >> TNF_BLOCK_SHIFT;
    392 	fh->next_fw_alloc = TNF_DIRENT_LAST + 4;
    393 	LOCK_INIT_CLEAR(&fh->lock);
    394 
    395 	(void) bzero(buf + sizeof (*fh), TNF_DIRECTORY_SIZE - sizeof (*fh));
    396 	i = TNF_DIRECTORY_SIZE >> TNF_BLOCK_SHIFT;
    397 	for (; i < blocks; ++i) {
    398 		/* LINTED pointer cast may result in improper alignment */
    399 		block =	(tnf_block_header_t *)(buf + (i << TNF_BLOCK_SHIFT));
    400 		block->tag = (tnf_ref32_t)TNF_BLOCK_HEADER_TAG;
    401 		block->generation = 0;
    402 		block->bytes_valid = sizeof (tnf_block_header_t);
    403 		LOCK_INIT_CLEAR(&block->A_lock);
    404 		LOCK_INIT_CLEAR(&block->B_lock);
    405 	}
    406 
    407 	/* snap in magic number */
    408 	fh->magic = TNF_MAGIC;
    409 }
    410