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