add db.1.85
[nvi.git] / db.1.85 / mpool / mpool.libtp
blob8c0fc27a0f62635349577ac8d4057eab93e9fd14
1 /******************************************************************************
3 VERSION $Id: buf.c,v 1.26 92/01/09 09:15:26 margo Exp $
4 PACKAGE:        User Level Shared Memory Manager
6 DESCRIPTION:    
7         This package provides a buffer pool interface implemented as
8         a collection of file pages mapped into shared memory.
10         Based on Mark's buffer manager
12 ROUTINES: 
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         
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"
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.
48 #define GET_MASTER      get_sem ( buf_spinlock )
49 #define RELEASE_MASTER  release_sem ( buf_spinlock )
51 #define LRUID   *buf_lru
52 #define LRUP    (bufhdr_table+*buf_lru)
53 #define MRU     bufhdr_table[*buf_lru].lru.prev
55 /* Global indicator that you have started reusing buffers */
56 int     do_statistics = 0;
58     Process Statics (pointers into shared memory)
60 static  BUF_T   *buf_table = 0;
61 static  BUFHDR_T        *bufhdr_table;
62 static  int     *buf_hash_table;
63 static  int     *buf_lru;               /* LRU is the free list */
64 static  int     buf_spinlock;
65 static  FINFO_T *buf_fids;
66 static  int     *buf_sp;                /* Pointer to string free space */
67 static  char    *buf_strings;
69 /* Process Local FID->FD table */
70 static  int     fds[NUM_FILE_ENTRIES];
72 /* Static routines */
73 static  BUFHDR_T        *bf_assign_buf();
74 static  int             bf_fid_to_fd();
75 static  BUFHDR_T        *bf_newbuf();
76 static  int             bf_put_page();
79     Return  0 on success
80             1 on failure
82 extern int
83 buf_init ( )
85     ADDR_T      buf_region;
86     BUFHDR_T    *bhp;
87     int         i;
88     int         ref_count;
89     int         *spinlockp;
91     /*
92         Initialize Process local structures
93     */
94     for ( i = 0; i < NUM_FILE_ENTRIES; i++ ) {
95         fds[i] = -1;
96     }
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 );
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);
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;
123         /* Now initialize the buffer manager */
125         /* 1. Free list */
126         *buf_lru = 0;
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;
140         /* 3. Hash Table */
141         for ( i = 0; i < NUMTABLE_ENTRIES; i++ ) {
142                 buf_hash_table[i] = NUM_BUFS;
143         }
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         }
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);
162 extern  void
163 buf_exit()
165     int ref;
166     int i;
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;
181         We need an empty buffer.  Find the LRU unpinned NON-Dirty page.
183 static BUFHDR_T *
184 bf_newbuf()
186     int         fd;
187     int         lruid;
188     int         nbytes;
189     int         ndx;
190     BUFHDR_T    *bhp;
192     lruid = LRUID;
193     for ( bhp = LRUP; 
194           bhp->flags & (BUF_PINNED|BUF_IO_IN_PROGRESS); 
195           bhp = LISTP_NEXTP (bufhdr_table, lru, bhp ) ) {
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); 
231     return(bhp);
234     buf_alloc
236     Add a page to a file and return a buffer for it.
239 ADDR_T
240 buf_alloc ( fid, new_pageno )
241 int     fid;
242 int     *new_pageno;
244         BUFHDR_T        *bhp;
245         int     fd;
246         int     len;
247         int     ndx;
248         OBJ_T   fobj;
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);
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         }
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 
288 Might want to add a flag that sets an LSN for this buffer is the
289 DIRTY flag is set
291 Eventually, you may want a flag that indicates the I/O and lock
292 request should be shipped off together, but not for now.
294 extern ADDR_T
295 buf_get ( file_id, page_id, flags, len )
296 int     file_id;
297 int     page_id;
298 u_long  flags;
299 int     *len;           /* Number of bytes read into buffer */
301         BUFHDR_T        *bhp;
302         int             bufid;
303         int             fd;
304         int             ndx;
305         int             next_bufid;
306         int             stat;
307         OBJ_T           fobj;   
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 ) ) {
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             }
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 {
367 not_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 */
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         }
388         MIS - do I want to add file links to buffer pool?
390 extern int
391 buf_sync ( fid, close )
392 int     fid;
393 int     close;          /* should we dec refcount and possibly
394                            invalidate all the buffers */
396     int i;
397     int fd;
398     int invalidate;
399     BUFHDR_T    *bhp;
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--;
420     if (RELEASE_MASTER) {
421         return(1);
422     }
423     return(0);
428 extern int
429 buf_flags ( addr, set_flags, unset_flags )
430 ADDR_T  addr;
431 u_long  set_flags;
432 u_long  unset_flags;
434     int         bufid;
435     BUFHDR_T    *bhp;
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     }
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);
487         Take a string name and produce an fid.
489         returns -1 on error
491         MIS -- this is a potential problem -- you keep actual names
492                 here -- what if people run from different directories?
494 extern  int
495 buf_name_lookup ( fname )
496 char    *fname;
498     int i;
499     int fid;
500     int ndx;
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);
539 static  int
540 bf_fid_to_fd ( fid ) 
541 int     fid;
543         struct stat sbuf;
545         assert ( (fid < NUM_FILE_ENTRIES) && (buf_fids[fid].offset != -1) );
546         if ( fds[fid] != -1 ) {
547             return(fds[fid]);
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         }
570         return ( fds[fid] );
573 static int
574 bf_put_page ( fd, bhp ) 
575 int     fd;
576 BUFHDR_T        *bhp;
578         int     nbytes;
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);
602 static BUFHDR_T *
603 bf_assign_buf ( ndx, obj, flags, len )
604 int     ndx;
605 OBJ_T   *obj;
606 u_long  flags;
607 int     *len;           /* Number of bytes read */
609     BUFHDR_T    *bhp;
610     int         fd;
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     }
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     }
680     if ( flags & BF_DIRTY ) {
681         bhp->flags |= BUF_DIRTY;
682     } else if ( *len < BUFSIZE ) {
683         bhp->flags |= BUF_NEWPAGE;
684     }
685     return ( bhp );
689 buf_last ( fid )
690 int     fid;
692         int     val;
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);
712 #ifdef DEBUG
713 extern void
714 buf_dump ( id, all )
715 int     id;
716 int     all;
718         int i;
719         BUFHDR_T        *bhp;
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;
745 #endif