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