xref: /freebsd/lib/libc/db/mpool/mpool.libtp (revision b1f9167f94059fd55c630891d359bcff987bd7eb)
1/******************************************************************************
2
3VERSION $FreeBSD$
4PACKAGE: 	User Level Shared Memory Manager
5
6DESCRIPTION:
7	This package provides a buffer pool interface implemented as
8	a collection of file pages mapped into shared memory.
9
10	Based on Mark's buffer manager
11
12ROUTINES:
13    External
14	buf_alloc
15	buf_flags
16	buf_get
17	buf_init
18	buf_last
19	buf_open
20	buf_pin
21	buf_sync
22	buf_unpin
23    Internal
24	bf_assign_buf
25	bf_fid_to_fd
26	bf_newbuf
27	bf_put_page
28
29
30******************************************************************************/
31#include	<sys/types.h>
32#include	<assert.h>
33#include	<sys/file.h>
34#include	<sys/stat.h>
35#include	<stdio.h>
36#include	<errno.h>
37#include	"list.h"
38#include	"user.h"
39#include	"txn_sys.h"
40#include	"buf.h"
41#include	"semkeys.h"
42#include	"error.h"
43
44/*
45    we need to translate between some type of file id that the user
46    process passes and a file descriptor.  For now, it's a nop.
47*/
48#define GET_MASTER      get_sem ( buf_spinlock )
49#define RELEASE_MASTER  release_sem ( buf_spinlock )
50
51#define	LRUID	*buf_lru
52#define	LRUP	(bufhdr_table+*buf_lru)
53#define	MRU	bufhdr_table[*buf_lru].lru.prev
54
55/* Global indicator that you have started reusing buffers */
56int	do_statistics = 0;
57/*
58    Process Statics (pointers into shared memory)
59*/
60static	BUF_T	*buf_table = 0;
61static	BUFHDR_T	*bufhdr_table;
62static	int	*buf_hash_table;
63static	int	*buf_lru;		/* LRU is the free list */
64static	int	buf_spinlock;
65static	FINFO_T	*buf_fids;
66static	int	*buf_sp;		/* Pointer to string free space */
67static	char	*buf_strings;
68
69/* Process Local FID->FD table */
70static	int	fds[NUM_FILE_ENTRIES];
71
72/* Static routines */
73static	BUFHDR_T	*bf_assign_buf();
74static	int		bf_fid_to_fd();
75static	BUFHDR_T	*bf_newbuf();
76static	int		bf_put_page();
77
78/*
79    Return  0 on success
80	    1 on failure
81*/
82extern int
83buf_init ( )
84{
85    ADDR_T	buf_region;
86    BUFHDR_T	*bhp;
87    int		i;
88    int		ref_count;
89    int		*spinlockp;
90
91    /*
92	Initialize Process local structures
93    */
94    for ( i = 0; i < NUM_FILE_ENTRIES; i++ ) {
95	fds[i] = -1;
96    }
97
98    buf_region = attach_region ( BUF_REGION_NAME, BUF_REGION_NUM,
99				 BUF_REGION_SIZE, &ref_count );
100    if ( !buf_region ) {
101	return (1);
102    }
103    error_log3 ( "Buf Region: ADDR: %d ID: %d SIZE: %d\n", buf_region,
104		    BUF_REGION_NUM, BUF_REGION_SIZE );
105
106    buf_table = (BUF_T *)buf_region;
107    bufhdr_table = (BUFHDR_T *)(buf_table + NUM_BUFS);
108    buf_hash_table = (int *)(bufhdr_table + NUM_BUFS);
109    buf_lru = buf_hash_table + NUMTABLE_ENTRIES;
110    spinlockp = buf_lru + 1;
111    buf_fids = (FINFO_T *)(spinlockp+1);
112    buf_sp = (int *)(buf_fids + NUM_FILE_ENTRIES);
113    buf_strings = (char *)(buf_sp + 1);
114
115    /* Create locking spinlock (gets creating holding the lock) */
116    buf_spinlock = create_sem ( BUF_SPIN_NAME, BUF_SPIN_NUM, ref_count <= 1 );
117    if ( buf_spinlock < 0 )  {
118	return(1);
119    }
120    if ( ref_count <= 1 ) {
121	*spinlockp = buf_spinlock;
122
123	/* Now initialize the buffer manager */
124
125	/* 1. Free list */
126	*buf_lru = 0;
127
128	/* 2. Buffer headers */
129	for ( i = 0, bhp = bufhdr_table; i < NUM_BUFS; bhp++, i++ ) {
130		bhp->lru.next = i+1;
131		bhp->lru.prev = i-1;
132		bhp->flags = 0;				/* All Flags off */
133		bhp->refcount = 0;
134		bhp->wait_proc = -1;			/* No sleepers */
135		LISTPE_INIT ( hash, bhp, i );		/* Hash chains */
136	}
137	bufhdr_table[0].lru.prev = NUM_BUFS-1;
138	bufhdr_table[NUM_BUFS-1].lru.next = 0;
139
140	/* 3. Hash Table */
141	for ( i = 0; i < NUMTABLE_ENTRIES; i++ ) {
142		buf_hash_table[i] = NUM_BUFS;
143	}
144
145	/* 4. File ID Table */
146	for ( i = 0; i < NUM_FILE_ENTRIES; i++ ) {
147		buf_fids[i].offset = -1;
148		buf_fids[i].npages = -1;
149		buf_fids[i].refcount = 0;
150	}
151
152	/* 5. Free String Pointer */
153	*buf_sp = (FILE_NAME_LEN*NUM_FILE_ENTRIES);
154	if (RELEASE_MASTER) {
155		return(1);
156	}
157	error_log0 ( "Initialized buffer region\n" );
158    }
159    return (0);
160}
161
162extern	void
163buf_exit()
164{
165    int	ref;
166    int	i;
167
168    /* Flush Buffer Pool on Exit */
169    for ( i = 0; i < NUM_FILE_ENTRIES; i++ ) {
170	if ( fds[i] != -1 ) {
171		close ( fds[i] );
172	}
173    }
174    if ( buf_table ) {
175	detach_region ( buf_table, BUF_REGION_NUM, BUF_REGION_SIZE, &ref );
176    }
177    return;
178}
179
180/*
181	We need an empty buffer.  Find the LRU unpinned NON-Dirty page.
182*/
183static BUFHDR_T	*
184bf_newbuf()
185{
186    int		fd;
187    int		lruid;
188    int		nbytes;
189    int		ndx;
190    BUFHDR_T	*bhp;
191
192    lruid = LRUID;
193    for ( bhp = LRUP;
194	  bhp->flags & (BUF_PINNED|BUF_IO_IN_PROGRESS);
195	  bhp = LISTP_NEXTP (bufhdr_table, lru, bhp ) ) {
196
197	if ( bhp->lru.next == lruid ) {
198		/* OUT OF BUFFERS */
199		error_log1 ( "All buffers are pinned.  %s\n",
200				"Unable to grant buffer request" );
201		return(NULL);
202	}
203    }
204    /* BHP can be used */
205    if ( bhp->flags & BUF_DIRTY ) {
206	do_statistics = 1;
207	/*
208	    MIS  Check for log flushed appropriately
209	*/
210	fd = bf_fid_to_fd(bhp->id.file_id);
211	if ( fd == -1 ) {
212	    error_log1 ("Invalid fid %d\n", bhp->id.file_id);
213	    return(NULL);
214	}
215	if ( bf_put_page(fd, bhp) < 0 ) {
216	    return(NULL);
217	}
218    }
219    /* Update Hash Pointers */
220    ndx = BUF_HASH ( bhp->id.file_id, bhp->id.obj_id );
221    LISTP_REMOVE(bufhdr_table, hash, bhp);
222    if ( buf_hash_table[ndx] == (bhp-bufhdr_table) ) {
223	if ( bhp->hash.next != (bhp-bufhdr_table) ) {
224		buf_hash_table[ndx] = bhp->hash.next;
225	} else {
226		buf_hash_table[ndx] = NUM_BUFS;
227	}
228    }
229    INIT_BUF(bhp);
230
231    return(bhp);
232}
233/*
234    buf_alloc
235
236    Add a page to a file and return a buffer for it.
237
238*/
239ADDR_T
240buf_alloc ( fid, new_pageno )
241int	fid;
242int	*new_pageno;
243{
244	BUFHDR_T	*bhp;
245	int	fd;
246	int	len;
247	int	ndx;
248	OBJ_T	fobj;
249
250	if (GET_MASTER) {
251		return(NULL);
252	}
253	if ( buf_fids[fid].npages == -1 ) {
254	    /* initialize npages field */
255	    fd = bf_fid_to_fd ( fid );
256	}
257	assert (fid < NUM_FILE_ENTRIES);
258
259	*new_pageno = buf_fids[fid].npages;
260	if ( *new_pageno == -1 ) {
261	    RELEASE_MASTER;
262	    return ( NULL );
263	}
264	buf_fids[fid].npages++;
265	ndx = BUF_HASH ( fid, *new_pageno );
266	fobj.file_id = fid;
267	fobj.obj_id  = *new_pageno;
268	bhp = bf_assign_buf ( ndx, &fobj, BF_PIN|BF_DIRTY|BF_EMPTY, &len );
269	if ( RELEASE_MASTER ) {
270		/* Memory leak */
271		return(NULL);
272	}
273	if ( bhp ) {
274	    return ((ADDR_T)(buf_table+(bhp-bufhdr_table)));
275	} else {
276	    return ( NULL );
277	}
278}
279
280
281/*
282	Buffer Flags
283	BF_DIRTY		Mark page as dirty
284	BF_EMPTY		Don't initialize page, just get buffer
285	BF_PIN			Retrieve with pin
286
287MIS
288Might want to add a flag that sets an LSN for this buffer is the
289DIRTY flag is set
290
291Eventually, you may want a flag that indicates the I/O and lock
292request should be shipped off together, but not for now.
293*/
294extern ADDR_T
295buf_get ( file_id, page_id, flags, len )
296int	file_id;
297int	page_id;
298u_long	flags;
299int	*len;		/* Number of bytes read into buffer */
300{
301	BUFHDR_T	*bhp;
302	int		bufid;
303	int		fd;
304	int		ndx;
305	int		next_bufid;
306	int		stat;
307	OBJ_T		fobj;
308
309	ndx = BUF_HASH ( file_id, page_id );
310	fobj.file_id = (long) file_id;
311	fobj.obj_id = (long) page_id;
312	if ( GET_MASTER ) {
313		return(NULL);
314	}
315	/*
316		This could be a for loop, but we lose speed
317		by making all the cases general purpose so we
318		optimize for the no-collision case.
319	*/
320	bufid = buf_hash_table[ndx];
321	if ( bufid < NUM_BUFS ) {
322	    for ( bhp = bufhdr_table+bufid;
323		  !OBJ_EQ (bhp->id, fobj) || !(bhp->flags & BUF_VALID);
324		  bhp = LISTP_NEXTP ( bufhdr_table, hash, bhp ) ) {
325
326		if ( bhp->hash.next == bufid ) {
327		    goto not_found;
328		}
329	    }
330/* found */
331	    if ( flags & BF_PIN ) {
332		    bhp->flags |= BUF_PINNED;
333		    bhp->refcount++;
334#ifdef PIN_DEBUG
335	fprintf(stderr, "buf_get: %X PINNED (%d)\n",
336			buf_table + (bhp-bufhdr_table), bhp->refcount);
337#endif
338	    }
339	    if ( flags & BF_DIRTY ) {
340		    bhp->flags |= BUF_DIRTY;
341	    }
342
343	    while ( bhp->flags & BUF_IO_IN_PROGRESS ) {
344		/* MIS -- eventually err check here */
345#ifdef DEBUG
346		printf("About to sleep on %d (me: %d\n)\n", bhp->wait_proc,
347			my_txnp - txn_table);
348#endif
349#ifdef WAIT_STATS
350		buf_waits++;
351#endif
352		stat = proc_sleep_on ( &(bhp->wait_proc), buf_spinlock );
353		if ( stat ) {
354			/* Memory leak */
355			return(NULL);
356		}
357		if (!( bhp->flags & BUF_IO_IN_PROGRESS) &&
358		    (!OBJ_EQ (bhp->id, fobj) || !(bhp->flags & BUF_VALID))) {
359			if (RELEASE_MASTER)
360				return(NULL);
361			return(buf_get ( file_id, page_id, flags, len ));
362		}
363	    }
364	    MAKE_MRU( bhp );
365	    *len = BUFSIZE;
366	} else {
367not_found:
368	    /* If you get here, the page isn't in the hash table */
369	    bhp = bf_assign_buf ( ndx, &fobj, flags, len );
370	}
371	/* Common code between found and not found */
372
373	if ( bhp && bhp->flags & BUF_NEWPAGE ) {
374	    *len = 0;
375	}
376	if (RELEASE_MASTER){
377		/* Memory leak */
378		return(NULL);
379	}
380	if ( bhp ) {
381	    return ((ADDR_T)(buf_table+(bhp-bufhdr_table)));
382	} else {
383	    return ( NULL );
384	}
385}
386
387/*
388	MIS - do I want to add file links to buffer pool?
389*/
390extern int
391buf_sync ( fid, close )
392int	fid;
393int	close;		/* should we dec refcount and possibly
394			   invalidate all the buffers */
395{
396    int	i;
397    int	fd;
398    int	invalidate;
399    BUFHDR_T	*bhp;
400
401    if ( (fd = bf_fid_to_fd ( fid )) < 0 ) {
402	return(1);
403    }
404    if (GET_MASTER) {
405	return(1);
406    }
407    invalidate = (buf_fids[fid].refcount == 1 && close);
408    if ( invalidate )
409	for ( bhp = bufhdr_table, i = 0; i < NUM_BUFS; bhp++, i++ ) {
410	    if (bhp->id.file_id == fid) {
411		if ((bhp->flags & BF_DIRTY) && (bf_put_page( fd, bhp ) < 0)) {
412			return(1);
413		}
414		bhp->id.file_id = -1;
415	    }
416	}
417    if (invalidate || close)
418	buf_fids[fid].refcount--;
419
420    if (RELEASE_MASTER) {
421	return(1);
422    }
423    return(0);
424
425
426}
427
428extern int
429buf_flags ( addr, set_flags, unset_flags )
430ADDR_T	addr;
431u_long	set_flags;
432u_long	unset_flags;
433{
434    int		bufid;
435    BUFHDR_T	*bhp;
436
437#ifdef PIN_DEBUG
438	fprintf(stderr, "buf_flags: %X setting %s%s%s%s%s releasing %s%s%s%s%s\n",
439	    addr,
440	    set_flags&BUF_DIRTY ? "DIRTY " : "",
441	    set_flags&BUF_VALID ? "VALID " : "",
442	    set_flags&BUF_PINNED ? "PINNED " : "",
443	    set_flags&BUF_IO_ERROR ? "IO_ERROR " : "",
444	    set_flags&BUF_IO_IN_PROGRESS ? "IO_IN_PROG " : "",
445	    set_flags&BUF_NEWPAGE ? "NEWPAGE " : "",
446	    unset_flags&BUF_DIRTY ? "DIRTY " : "",
447	    unset_flags&BUF_VALID ? "VALID " : "",
448	    unset_flags&BUF_PINNED ? "PINNED " : "",
449	    unset_flags&BUF_IO_ERROR ? "IO_ERROR " : "",
450	    unset_flags&BUF_IO_IN_PROGRESS ? "IO_IN_PROG " : "",
451	    unset_flags&BUF_NEWPAGE ? "NEWPAGE " : "" );
452#endif
453    if (!ADDR_OK(addr)) {
454	error_log1 ( "buf_pin: Invalid Buffer Address %x\n", addr );
455	return(1);
456    }
457    bufid = ((BUF_T *)addr) - buf_table;
458    assert ( bufid < NUM_BUFS);
459    bhp = &bufhdr_table[bufid];
460    if (GET_MASTER) {
461	return(1);
462    }
463    bhp->flags |= set_flags;
464    if ( set_flags & BUF_PINNED ) {
465	bhp->refcount++;
466    }
467    if ( set_flags & BUF_DIRTY ) {
468	unset_flags |= BUF_NEWPAGE;
469    }
470
471    if ( unset_flags & BUF_PINNED ) {
472	bhp->refcount--;
473	if ( bhp->refcount ) {
474		/* Turn off pin bit so it doesn't get unset */
475		unset_flags &= ~BUF_PINNED;
476	}
477    }
478    bhp->flags &= ~unset_flags;
479    MAKE_MRU(bhp);
480    if (RELEASE_MASTER) {
481	return(1);
482    }
483    return(0);
484}
485
486/*
487	Take a string name and produce an fid.
488
489	returns -1 on error
490
491	MIS -- this is a potential problem -- you keep actual names
492		here -- what if people run from different directories?
493*/
494extern	int
495buf_name_lookup ( fname )
496char	*fname;
497{
498    int	i;
499    int	fid;
500    int	ndx;
501
502    fid = -1;
503    if (GET_MASTER) {
504	    return(-1);
505    }
506    for ( i = 0; i < NUM_FILE_ENTRIES; i++ ) {
507	if ( buf_fids[i].offset == -1 ) {
508		fid = i;
509	} else {
510		if (!strcmp (fname, buf_strings+buf_fids[i].offset)) {
511			if (RELEASE_MASTER) {
512				return(-1);
513			}
514			buf_fids[i].refcount++;
515			return(i);
516		}
517	}
518    }
519    if ( fid == -1 ) {
520	error_log0 ( "No more file ID's\n" );
521    } else {
522	ndx = *buf_sp - strlen(fname) - 1;
523	if ( ndx < 0 ) {
524	    error_log0 ( "Out of string space\n" );
525	    fid = -1;
526	} else {
527	    *buf_sp = ndx;
528	    strcpy ( buf_strings+ndx, fname );
529	    buf_fids[fid].offset = ndx;
530	}
531	buf_fids[fid].refcount = 1;
532    }
533    if (RELEASE_MASTER) {
534	return(-1);
535    }
536    return(fid);
537}
538
539static	int
540bf_fid_to_fd ( fid )
541int	fid;
542{
543	struct stat sbuf;
544
545	assert ( (fid < NUM_FILE_ENTRIES) && (buf_fids[fid].offset != -1) );
546	if ( fds[fid] != -1 ) {
547	    return(fds[fid]);
548
549	}
550	fds[fid] = open ( buf_strings+buf_fids[fid].offset, O_RDWR|O_CREAT,
551			  0666 );
552	if ( fds[fid] < 0 ) {
553	    error_log3 ( "Error Opening File %s FID: %d FD: %d.  Errno = %d\n",
554			    buf_strings+buf_fids[fid].offset, fid, fds[fid],
555			    errno );
556	    return(-1);
557	}
558	error_log3 ( "Opening File %s FID: %d FD: %d\n",
559			buf_strings+buf_fids[fid].offset, fid, fds[fid] );
560	if ( buf_fids[fid].npages == -1 ) {
561		/* Initialize the npages field */
562		if ( fstat ( fds[fid], &sbuf ) ) {
563		    error_log3 ( "Error Fstating %s FID: %d.  Errno = %d\n",
564				buf_strings+buf_fids[fid].offset, fid, errno );
565		} else {
566		    buf_fids[fid].npages = ( sbuf.st_size / BUFSIZE );
567		}
568	}
569
570	return ( fds[fid] );
571}
572
573static int
574bf_put_page ( fd, bhp )
575int	fd;
576BUFHDR_T	*bhp;
577{
578	int	nbytes;
579
580	assert ( (bhp-bufhdr_table) < NUM_BUFS );
581	if ( lseek ( fd, bhp->id.obj_id << BUFSHIFT, L_SET ) < 0 ) {
582	    return(-1);
583	}
584	bhp->flags |= BUF_IO_IN_PROGRESS;
585	if (RELEASE_MASTER) {
586	    return(-1);
587	}
588	nbytes = write(fd, buf_table[bhp-bufhdr_table], BUFSIZE);
589	if (GET_MASTER) {
590	    return(-2);
591	}
592	if ( nbytes < 0 ) {
593		error_log1 ("Write failed with error code %d\n", errno);
594		return(-1);
595	} else if ( nbytes != BUFSIZE ) {
596		error_log1 ("Short write: %d bytes of %d\n", nbytes, BUFSIZE );
597	}
598	bhp->flags &= ~(BUF_DIRTY|BUF_IO_IN_PROGRESS);
599	return (0);
600}
601
602static BUFHDR_T	*
603bf_assign_buf ( ndx, obj, flags, len )
604int	ndx;
605OBJ_T	*obj;
606u_long	flags;
607int	*len;		/* Number of bytes read */
608{
609    BUFHDR_T	*bhp;
610    int		fd;
611
612    assert ( obj->file_id < NUM_FILE_ENTRIES );
613    bhp = bf_newbuf();
614    if ( !bhp ) {
615	return(NULL);
616    }
617    OBJ_ASSIGN ( (*obj), bhp->id );
618    if ( buf_hash_table[ndx] >= NUM_BUFS ) {
619	buf_hash_table[ndx] = bhp-bufhdr_table;
620    } else {
621	LISTPE_INSERT ( bufhdr_table, hash, bhp, buf_hash_table[ndx] );
622    }
623
624    bhp->flags |= BUF_VALID;
625    if ( flags & BF_PIN ) {
626	bhp->flags |= BUF_PINNED;
627	bhp->refcount++;
628#ifdef PIN_DEBUG
629	fprintf(stderr, "bf_assign_buf: %X PINNED (%d)\n",
630		buf_table + (bhp-bufhdr_table), bhp->refcount);
631#endif
632    }
633    fd = bf_fid_to_fd(obj->file_id);
634    if ( fd == -1 ) {
635	error_log1 ("Invalid fid %d\n", obj->file_id);
636	bhp->flags |= ~BUF_IO_ERROR;
637	return(NULL);
638    }
639    if ( obj->obj_id >= buf_fids[obj->file_id].npages) {
640	buf_fids[obj->file_id].npages = obj->obj_id+1;
641	*len = 0;
642    } else if ( flags & BF_EMPTY ) {
643	*len = 0;
644    } else {
645	bhp->flags |= BUF_IO_IN_PROGRESS;
646	if (RELEASE_MASTER) {
647		return(NULL);
648	}
649	if ( lseek ( fd, obj->obj_id << BUFSHIFT, L_SET ) < -1 ) {
650	    error_log2 ("Unable to perform seek on file: %d  to page %d",
651			obj->file_id, obj->obj_id );
652	    bhp->flags &= ~BUF_IO_IN_PROGRESS;
653	    bhp->flags |= ~BUF_IO_ERROR;
654	    return(NULL);
655	}
656	*len = read(fd, buf_table[bhp-bufhdr_table], BUFSIZE);
657	if ( *len < 0 ) {
658	    error_log2 ("Unable to perform read on file: %d  to page %d",
659			obj->file_id, obj->obj_id );
660	    bhp->flags &= ~BUF_IO_IN_PROGRESS;
661	    bhp->flags |= ~BUF_IO_ERROR;
662	    return(NULL);
663	}
664	if (GET_MASTER) {
665		return(NULL);
666	}
667	bhp->flags &= ~BUF_IO_IN_PROGRESS;
668	if ( bhp->wait_proc != -1 ) {
669	    /* wake up waiter and anyone waiting on it */
670#ifdef DEBUG
671	    printf("Waking transaction %d due to completed I/O\n",
672		bhp->wait_proc);
673#endif
674	    proc_wake_id ( bhp->wait_proc );
675	    bhp->wait_proc = -1;
676	}
677	MAKE_MRU(bhp);
678    }
679
680    if ( flags & BF_DIRTY ) {
681	bhp->flags |= BUF_DIRTY;
682    } else if ( *len < BUFSIZE ) {
683	bhp->flags |= BUF_NEWPAGE;
684    }
685    return ( bhp );
686}
687
688int
689buf_last ( fid )
690int	fid;
691{
692	int	val;
693
694	if (GET_MASTER) {
695		return(-1);
696	}
697	assert ( fid < NUM_FILE_ENTRIES );
698	if ( buf_fids[fid].npages == -1 ) {
699	    /* initialize npages field */
700	    (void) bf_fid_to_fd ( fid );
701	}
702	val = buf_fids[fid].npages;
703	if ( val ) {
704		val--;			/* Convert to page number */
705	}
706	if (RELEASE_MASTER) {
707		return(-1);
708	}
709	return(val);
710}
711
712#ifdef DEBUG
713extern void
714buf_dump ( id, all )
715int	id;
716int	all;
717{
718	int i;
719	BUFHDR_T	*bhp;
720
721	printf ( "LRU + %d\n", *buf_lru );
722	if ( all ) {
723	    printf("ID\tFID\tPID\tLNEXT\tLPREV\tHNEXT\tHPREV\tSLEEP\tFLAG\tREFS\n");
724	    for ( bhp = bufhdr_table, i = 0; i < NUM_BUFS; bhp++, i++ ) {
725		    printf ( "%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%x\t%d\n", i,
726			bhp->id.file_id, bhp->id.obj_id,
727			bhp->lru.next, bhp->lru.prev,
728			bhp->hash.next, bhp->hash.prev,
729			bhp->wait_proc, bhp->flags, bhp->refcount );
730	    }
731	} else {
732		if ( id >= NUM_BUFS ) {
733			printf ( "Buffer ID (%d) too high\n", id );
734			return;
735		}
736		bhp = bufhdr_table+id;
737		printf ( "%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%x\t%d\n", i,
738		    bhp->id.file_id, bhp->id.obj_id,
739		    bhp->lru.next, bhp->lru.prev,
740		    bhp->hash.next, bhp->hash.prev,
741		    bhp->wait_proc, bhp->flags, bhp->refcount );
742	}
743	return;
744}
745#endif
746
747