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