1/****************************************************************************** 2 3VERSION $Id: buf.c,v 1.26 92/01/09 09:15:26 margo Exp $ 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