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