xref: /dragonfly/lib/libc/db/mpool/mpool.libtp (revision abd448c3b2d3508465e48d9cfdb163ef88fc242e)
1/******************************************************************************
2
3VERSION $FreeBSD: head/lib/libc/db/mpool/mpool.libtp 50476 1999-08-28 00:22:10Z peter $
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