1*433d6423SLionel Sambuc 2*433d6423SLionel Sambuc #define _SYSTEM 3*433d6423SLionel Sambuc 4*433d6423SLionel Sambuc #include <assert.h> 5*433d6423SLionel Sambuc #include <errno.h> 6*433d6423SLionel Sambuc #include <math.h> 7*433d6423SLionel Sambuc #include <stdlib.h> 8*433d6423SLionel Sambuc 9*433d6423SLionel Sambuc #include <machine/vmparam.h> 10*433d6423SLionel Sambuc 11*433d6423SLionel Sambuc #include <sys/param.h> 12*433d6423SLionel Sambuc #include <sys/mman.h> 13*433d6423SLionel Sambuc 14*433d6423SLionel Sambuc #include <minix/dmap.h> 15*433d6423SLionel Sambuc #include <minix/libminixfs.h> 16*433d6423SLionel Sambuc #include <minix/syslib.h> 17*433d6423SLionel Sambuc #include <minix/sysutil.h> 18*433d6423SLionel Sambuc #include <minix/u64.h> 19*433d6423SLionel Sambuc #include <minix/bdev.h> 20*433d6423SLionel Sambuc 21*433d6423SLionel Sambuc #define BUFHASH(b) ((b) % nr_bufs) 22*433d6423SLionel Sambuc #define MARKCLEAN lmfs_markclean 23*433d6423SLionel Sambuc 24*433d6423SLionel Sambuc #define MINBUFS 6 /* minimal no of bufs for sanity check */ 25*433d6423SLionel Sambuc 26*433d6423SLionel Sambuc static struct buf *front; /* points to least recently used free block */ 27*433d6423SLionel Sambuc static struct buf *rear; /* points to most recently used free block */ 28*433d6423SLionel Sambuc static unsigned int bufs_in_use;/* # bufs currently in use (not on free list)*/ 29*433d6423SLionel Sambuc 30*433d6423SLionel Sambuc static void rm_lru(struct buf *bp); 31*433d6423SLionel Sambuc static void read_block(struct buf *); 32*433d6423SLionel Sambuc static void flushall(dev_t dev); 33*433d6423SLionel Sambuc static void freeblock(struct buf *bp); 34*433d6423SLionel Sambuc static void cache_heuristic_check(int major); 35*433d6423SLionel Sambuc 36*433d6423SLionel Sambuc static int vmcache = 0; /* are we using vm's secondary cache? (initially not) */ 37*433d6423SLionel Sambuc 38*433d6423SLionel Sambuc static struct buf *buf; 39*433d6423SLionel Sambuc static struct buf **buf_hash; /* the buffer hash table */ 40*433d6423SLionel Sambuc static unsigned int nr_bufs; 41*433d6423SLionel Sambuc static int may_use_vmcache; 42*433d6423SLionel Sambuc 43*433d6423SLionel Sambuc static int fs_block_size = PAGE_SIZE; /* raw i/o block size */ 44*433d6423SLionel Sambuc 45*433d6423SLionel Sambuc static int rdwt_err; 46*433d6423SLionel Sambuc 47*433d6423SLionel Sambuc static int quiet = 0; 48*433d6423SLionel Sambuc 49*433d6423SLionel Sambuc void lmfs_setquiet(int q) { quiet = q; } 50*433d6423SLionel Sambuc 51*433d6423SLionel Sambuc static u32_t fs_bufs_heuristic(int minbufs, u32_t btotal, u64_t bfree, 52*433d6423SLionel Sambuc int blocksize, dev_t majordev) 53*433d6423SLionel Sambuc { 54*433d6423SLionel Sambuc struct vm_stats_info vsi; 55*433d6423SLionel Sambuc int bufs; 56*433d6423SLionel Sambuc u32_t kbytes_used_fs, kbytes_total_fs, kbcache, kb_fsmax; 57*433d6423SLionel Sambuc u32_t kbytes_remain_mem; 58*433d6423SLionel Sambuc u64_t bused; 59*433d6423SLionel Sambuc 60*433d6423SLionel Sambuc bused = btotal-bfree; 61*433d6423SLionel Sambuc 62*433d6423SLionel Sambuc /* set a reasonable cache size; cache at most a certain 63*433d6423SLionel Sambuc * portion of the used FS, and at most a certain %age of remaining 64*433d6423SLionel Sambuc * memory 65*433d6423SLionel Sambuc */ 66*433d6423SLionel Sambuc if(vm_info_stats(&vsi) != OK) { 67*433d6423SLionel Sambuc bufs = 1024; 68*433d6423SLionel Sambuc if(!quiet) 69*433d6423SLionel Sambuc printf("fslib: heuristic info fail: default to %d bufs\n", bufs); 70*433d6423SLionel Sambuc return bufs; 71*433d6423SLionel Sambuc } 72*433d6423SLionel Sambuc 73*433d6423SLionel Sambuc /* remaining free memory is unused memory plus memory in used for cache, 74*433d6423SLionel Sambuc * as the cache can be evicted 75*433d6423SLionel Sambuc */ 76*433d6423SLionel Sambuc kbytes_remain_mem = (u64_t)(vsi.vsi_free + vsi.vsi_cached) * 77*433d6423SLionel Sambuc vsi.vsi_pagesize / 1024; 78*433d6423SLionel Sambuc 79*433d6423SLionel Sambuc /* check fs usage. */ 80*433d6423SLionel Sambuc kbytes_used_fs = (unsigned long)(((u64_t)bused * blocksize) / 1024); 81*433d6423SLionel Sambuc kbytes_total_fs = (unsigned long)(((u64_t)btotal * blocksize) / 1024); 82*433d6423SLionel Sambuc 83*433d6423SLionel Sambuc /* heuristic for a desired cache size based on FS usage; 84*433d6423SLionel Sambuc * but never bigger than half of the total filesystem 85*433d6423SLionel Sambuc */ 86*433d6423SLionel Sambuc kb_fsmax = sqrt_approx(kbytes_used_fs)*40; 87*433d6423SLionel Sambuc kb_fsmax = MIN(kb_fsmax, kbytes_total_fs/2); 88*433d6423SLionel Sambuc 89*433d6423SLionel Sambuc /* heuristic for a maximum usage - 10% of remaining memory */ 90*433d6423SLionel Sambuc kbcache = MIN(kbytes_remain_mem/10, kb_fsmax); 91*433d6423SLionel Sambuc bufs = kbcache * 1024 / blocksize; 92*433d6423SLionel Sambuc 93*433d6423SLionel Sambuc /* but we simply need MINBUFS no matter what */ 94*433d6423SLionel Sambuc if(bufs < minbufs) 95*433d6423SLionel Sambuc bufs = minbufs; 96*433d6423SLionel Sambuc 97*433d6423SLionel Sambuc return bufs; 98*433d6423SLionel Sambuc } 99*433d6423SLionel Sambuc 100*433d6423SLionel Sambuc void lmfs_blockschange(dev_t dev, int delta) 101*433d6423SLionel Sambuc { 102*433d6423SLionel Sambuc /* Change the number of allocated blocks by 'delta.' 103*433d6423SLionel Sambuc * Also accumulate the delta since the last cache re-evaluation. 104*433d6423SLionel Sambuc * If it is outside a certain band, ask the cache library to 105*433d6423SLionel Sambuc * re-evaluate the cache size. 106*433d6423SLionel Sambuc */ 107*433d6423SLionel Sambuc static int bitdelta = 0; 108*433d6423SLionel Sambuc bitdelta += delta; 109*433d6423SLionel Sambuc #define BANDKB (10*1024) /* recheck cache every 10MB change */ 110*433d6423SLionel Sambuc if(bitdelta*fs_block_size/1024 > BANDKB || 111*433d6423SLionel Sambuc bitdelta*fs_block_size/1024 < -BANDKB) { 112*433d6423SLionel Sambuc lmfs_cache_reevaluate(dev); 113*433d6423SLionel Sambuc bitdelta = 0; 114*433d6423SLionel Sambuc } 115*433d6423SLionel Sambuc } 116*433d6423SLionel Sambuc 117*433d6423SLionel Sambuc void lmfs_markdirty(struct buf *bp) 118*433d6423SLionel Sambuc { 119*433d6423SLionel Sambuc bp->lmfs_flags |= VMMC_DIRTY; 120*433d6423SLionel Sambuc } 121*433d6423SLionel Sambuc 122*433d6423SLionel Sambuc void lmfs_markclean(struct buf *bp) 123*433d6423SLionel Sambuc { 124*433d6423SLionel Sambuc bp->lmfs_flags &= ~VMMC_DIRTY; 125*433d6423SLionel Sambuc } 126*433d6423SLionel Sambuc 127*433d6423SLionel Sambuc int lmfs_isclean(struct buf *bp) 128*433d6423SLionel Sambuc { 129*433d6423SLionel Sambuc return !(bp->lmfs_flags & VMMC_DIRTY); 130*433d6423SLionel Sambuc } 131*433d6423SLionel Sambuc 132*433d6423SLionel Sambuc dev_t lmfs_dev(struct buf *bp) 133*433d6423SLionel Sambuc { 134*433d6423SLionel Sambuc return bp->lmfs_dev; 135*433d6423SLionel Sambuc } 136*433d6423SLionel Sambuc 137*433d6423SLionel Sambuc int lmfs_bytes(struct buf *bp) 138*433d6423SLionel Sambuc { 139*433d6423SLionel Sambuc return bp->lmfs_bytes; 140*433d6423SLionel Sambuc } 141*433d6423SLionel Sambuc 142*433d6423SLionel Sambuc static void free_unused_blocks(void) 143*433d6423SLionel Sambuc { 144*433d6423SLionel Sambuc struct buf *bp; 145*433d6423SLionel Sambuc 146*433d6423SLionel Sambuc int freed = 0, bytes = 0; 147*433d6423SLionel Sambuc printf("libminixfs: freeing; %d blocks in use\n", bufs_in_use); 148*433d6423SLionel Sambuc for(bp = &buf[0]; bp < &buf[nr_bufs]; bp++) { 149*433d6423SLionel Sambuc if(bp->lmfs_bytes > 0 && bp->lmfs_count == 0) { 150*433d6423SLionel Sambuc freed++; 151*433d6423SLionel Sambuc bytes += bp->lmfs_bytes; 152*433d6423SLionel Sambuc freeblock(bp); 153*433d6423SLionel Sambuc } 154*433d6423SLionel Sambuc } 155*433d6423SLionel Sambuc printf("libminixfs: freeing; %d blocks, %d bytes\n", freed, bytes); 156*433d6423SLionel Sambuc } 157*433d6423SLionel Sambuc 158*433d6423SLionel Sambuc static void lmfs_alloc_block(struct buf *bp) 159*433d6423SLionel Sambuc { 160*433d6423SLionel Sambuc int len; 161*433d6423SLionel Sambuc ASSERT(!bp->data); 162*433d6423SLionel Sambuc ASSERT(bp->lmfs_bytes == 0); 163*433d6423SLionel Sambuc 164*433d6423SLionel Sambuc len = roundup(fs_block_size, PAGE_SIZE); 165*433d6423SLionel Sambuc 166*433d6423SLionel Sambuc if((bp->data = mmap(0, fs_block_size, 167*433d6423SLionel Sambuc PROT_READ|PROT_WRITE, MAP_PREALLOC|MAP_ANON, -1, 0)) == MAP_FAILED) { 168*433d6423SLionel Sambuc free_unused_blocks(); 169*433d6423SLionel Sambuc if((bp->data = mmap(0, fs_block_size, PROT_READ|PROT_WRITE, 170*433d6423SLionel Sambuc MAP_PREALLOC|MAP_ANON, -1, 0)) == MAP_FAILED) { 171*433d6423SLionel Sambuc panic("libminixfs: could not allocate block"); 172*433d6423SLionel Sambuc } 173*433d6423SLionel Sambuc } 174*433d6423SLionel Sambuc assert(bp->data); 175*433d6423SLionel Sambuc bp->lmfs_bytes = fs_block_size; 176*433d6423SLionel Sambuc bp->lmfs_needsetcache = 1; 177*433d6423SLionel Sambuc } 178*433d6423SLionel Sambuc 179*433d6423SLionel Sambuc /*===========================================================================* 180*433d6423SLionel Sambuc * lmfs_get_block * 181*433d6423SLionel Sambuc *===========================================================================*/ 182*433d6423SLionel Sambuc struct buf *lmfs_get_block(register dev_t dev, register block_t block, 183*433d6423SLionel Sambuc int only_search) 184*433d6423SLionel Sambuc { 185*433d6423SLionel Sambuc return lmfs_get_block_ino(dev, block, only_search, VMC_NO_INODE, 0); 186*433d6423SLionel Sambuc } 187*433d6423SLionel Sambuc 188*433d6423SLionel Sambuc void munmap_t(void *a, int len) 189*433d6423SLionel Sambuc { 190*433d6423SLionel Sambuc vir_bytes av = (vir_bytes) a; 191*433d6423SLionel Sambuc assert(a); 192*433d6423SLionel Sambuc assert(a != MAP_FAILED); 193*433d6423SLionel Sambuc assert(len > 0); 194*433d6423SLionel Sambuc assert(!(av % PAGE_SIZE)); 195*433d6423SLionel Sambuc 196*433d6423SLionel Sambuc len = roundup(len, PAGE_SIZE); 197*433d6423SLionel Sambuc 198*433d6423SLionel Sambuc assert(!(len % PAGE_SIZE)); 199*433d6423SLionel Sambuc 200*433d6423SLionel Sambuc if(munmap(a, len) < 0) 201*433d6423SLionel Sambuc panic("libminixfs cache: munmap failed"); 202*433d6423SLionel Sambuc } 203*433d6423SLionel Sambuc 204*433d6423SLionel Sambuc static void raisecount(struct buf *bp) 205*433d6423SLionel Sambuc { 206*433d6423SLionel Sambuc assert(bufs_in_use >= 0); 207*433d6423SLionel Sambuc ASSERT(bp->lmfs_count >= 0); 208*433d6423SLionel Sambuc bp->lmfs_count++; 209*433d6423SLionel Sambuc if(bp->lmfs_count == 1) bufs_in_use++; 210*433d6423SLionel Sambuc assert(bufs_in_use > 0); 211*433d6423SLionel Sambuc } 212*433d6423SLionel Sambuc 213*433d6423SLionel Sambuc static void lowercount(struct buf *bp) 214*433d6423SLionel Sambuc { 215*433d6423SLionel Sambuc assert(bufs_in_use > 0); 216*433d6423SLionel Sambuc ASSERT(bp->lmfs_count > 0); 217*433d6423SLionel Sambuc bp->lmfs_count--; 218*433d6423SLionel Sambuc if(bp->lmfs_count == 0) bufs_in_use--; 219*433d6423SLionel Sambuc assert(bufs_in_use >= 0); 220*433d6423SLionel Sambuc } 221*433d6423SLionel Sambuc 222*433d6423SLionel Sambuc static void freeblock(struct buf *bp) 223*433d6423SLionel Sambuc { 224*433d6423SLionel Sambuc ASSERT(bp->lmfs_count == 0); 225*433d6423SLionel Sambuc /* If the block taken is dirty, make it clean by writing it to the disk. 226*433d6423SLionel Sambuc * Avoid hysteresis by flushing all other dirty blocks for the same device. 227*433d6423SLionel Sambuc */ 228*433d6423SLionel Sambuc if (bp->lmfs_dev != NO_DEV) { 229*433d6423SLionel Sambuc if (!lmfs_isclean(bp)) flushall(bp->lmfs_dev); 230*433d6423SLionel Sambuc assert(bp->lmfs_bytes == fs_block_size); 231*433d6423SLionel Sambuc bp->lmfs_dev = NO_DEV; 232*433d6423SLionel Sambuc } 233*433d6423SLionel Sambuc 234*433d6423SLionel Sambuc /* Fill in block's parameters and add it to the hash chain where it goes. */ 235*433d6423SLionel Sambuc MARKCLEAN(bp); /* NO_DEV blocks may be marked dirty */ 236*433d6423SLionel Sambuc if(bp->lmfs_bytes > 0) { 237*433d6423SLionel Sambuc assert(bp->data); 238*433d6423SLionel Sambuc munmap_t(bp->data, bp->lmfs_bytes); 239*433d6423SLionel Sambuc bp->lmfs_bytes = 0; 240*433d6423SLionel Sambuc bp->data = NULL; 241*433d6423SLionel Sambuc } else assert(!bp->data); 242*433d6423SLionel Sambuc } 243*433d6423SLionel Sambuc 244*433d6423SLionel Sambuc /*===========================================================================* 245*433d6423SLionel Sambuc * lmfs_get_block_ino * 246*433d6423SLionel Sambuc *===========================================================================*/ 247*433d6423SLionel Sambuc struct buf *lmfs_get_block_ino(dev_t dev, block_t block, int only_search, 248*433d6423SLionel Sambuc ino_t ino, u64_t ino_off) 249*433d6423SLionel Sambuc { 250*433d6423SLionel Sambuc /* Check to see if the requested block is in the block cache. If so, return 251*433d6423SLionel Sambuc * a pointer to it. If not, evict some other block and fetch it (unless 252*433d6423SLionel Sambuc * 'only_search' is 1). All the blocks in the cache that are not in use 253*433d6423SLionel Sambuc * are linked together in a chain, with 'front' pointing to the least recently 254*433d6423SLionel Sambuc * used block and 'rear' to the most recently used block. If 'only_search' is 255*433d6423SLionel Sambuc * 1, the block being requested will be overwritten in its entirety, so it is 256*433d6423SLionel Sambuc * only necessary to see if it is in the cache; if it is not, any free buffer 257*433d6423SLionel Sambuc * will do. It is not necessary to actually read the block in from disk. 258*433d6423SLionel Sambuc * If 'only_search' is PREFETCH, the block need not be read from the disk, 259*433d6423SLionel Sambuc * and the device is not to be marked on the block, so callers can tell if 260*433d6423SLionel Sambuc * the block returned is valid. 261*433d6423SLionel Sambuc * In addition to the LRU chain, there is also a hash chain to link together 262*433d6423SLionel Sambuc * blocks whose block numbers end with the same bit strings, for fast lookup. 263*433d6423SLionel Sambuc */ 264*433d6423SLionel Sambuc 265*433d6423SLionel Sambuc int b; 266*433d6423SLionel Sambuc static struct buf *bp; 267*433d6423SLionel Sambuc u64_t dev_off = (u64_t) block * fs_block_size; 268*433d6423SLionel Sambuc struct buf *prev_ptr; 269*433d6423SLionel Sambuc 270*433d6423SLionel Sambuc assert(buf_hash); 271*433d6423SLionel Sambuc assert(buf); 272*433d6423SLionel Sambuc assert(nr_bufs > 0); 273*433d6423SLionel Sambuc 274*433d6423SLionel Sambuc ASSERT(fs_block_size > 0); 275*433d6423SLionel Sambuc 276*433d6423SLionel Sambuc assert(dev != NO_DEV); 277*433d6423SLionel Sambuc 278*433d6423SLionel Sambuc if((ino_off % fs_block_size)) { 279*433d6423SLionel Sambuc 280*433d6423SLionel Sambuc printf("cache: unaligned lmfs_get_block_ino ino_off %llu\n", 281*433d6423SLionel Sambuc ino_off); 282*433d6423SLionel Sambuc util_stacktrace(); 283*433d6423SLionel Sambuc } 284*433d6423SLionel Sambuc 285*433d6423SLionel Sambuc /* Search the hash chain for (dev, block). */ 286*433d6423SLionel Sambuc b = BUFHASH(block); 287*433d6423SLionel Sambuc bp = buf_hash[b]; 288*433d6423SLionel Sambuc while (bp != NULL) { 289*433d6423SLionel Sambuc if (bp->lmfs_blocknr == block && bp->lmfs_dev == dev) { 290*433d6423SLionel Sambuc if(bp->lmfs_flags & VMMC_EVICTED) { 291*433d6423SLionel Sambuc /* We had it but VM evicted it; invalidate it. */ 292*433d6423SLionel Sambuc ASSERT(bp->lmfs_count == 0); 293*433d6423SLionel Sambuc ASSERT(!(bp->lmfs_flags & VMMC_BLOCK_LOCKED)); 294*433d6423SLionel Sambuc ASSERT(!(bp->lmfs_flags & VMMC_DIRTY)); 295*433d6423SLionel Sambuc bp->lmfs_dev = NO_DEV; 296*433d6423SLionel Sambuc bp->lmfs_bytes = 0; 297*433d6423SLionel Sambuc bp->data = NULL; 298*433d6423SLionel Sambuc break; 299*433d6423SLionel Sambuc } 300*433d6423SLionel Sambuc /* Block needed has been found. */ 301*433d6423SLionel Sambuc if (bp->lmfs_count == 0) { 302*433d6423SLionel Sambuc rm_lru(bp); 303*433d6423SLionel Sambuc ASSERT(bp->lmfs_needsetcache == 0); 304*433d6423SLionel Sambuc ASSERT(!(bp->lmfs_flags & VMMC_BLOCK_LOCKED)); 305*433d6423SLionel Sambuc bp->lmfs_flags |= VMMC_BLOCK_LOCKED; 306*433d6423SLionel Sambuc } 307*433d6423SLionel Sambuc raisecount(bp); 308*433d6423SLionel Sambuc ASSERT(bp->lmfs_bytes == fs_block_size); 309*433d6423SLionel Sambuc ASSERT(bp->lmfs_dev == dev); 310*433d6423SLionel Sambuc ASSERT(bp->lmfs_dev != NO_DEV); 311*433d6423SLionel Sambuc ASSERT(bp->lmfs_flags & VMMC_BLOCK_LOCKED); 312*433d6423SLionel Sambuc ASSERT(bp->data); 313*433d6423SLionel Sambuc 314*433d6423SLionel Sambuc if(ino != VMC_NO_INODE) { 315*433d6423SLionel Sambuc if(bp->lmfs_inode == VMC_NO_INODE 316*433d6423SLionel Sambuc || bp->lmfs_inode != ino 317*433d6423SLionel Sambuc || bp->lmfs_inode_offset != ino_off) { 318*433d6423SLionel Sambuc bp->lmfs_inode = ino; 319*433d6423SLionel Sambuc bp->lmfs_inode_offset = ino_off; 320*433d6423SLionel Sambuc bp->lmfs_needsetcache = 1; 321*433d6423SLionel Sambuc } 322*433d6423SLionel Sambuc } 323*433d6423SLionel Sambuc 324*433d6423SLionel Sambuc return(bp); 325*433d6423SLionel Sambuc } else { 326*433d6423SLionel Sambuc /* This block is not the one sought. */ 327*433d6423SLionel Sambuc bp = bp->lmfs_hash; /* move to next block on hash chain */ 328*433d6423SLionel Sambuc } 329*433d6423SLionel Sambuc } 330*433d6423SLionel Sambuc 331*433d6423SLionel Sambuc /* Desired block is not on available chain. Find a free block to use. */ 332*433d6423SLionel Sambuc if(bp) { 333*433d6423SLionel Sambuc ASSERT(bp->lmfs_flags & VMMC_EVICTED); 334*433d6423SLionel Sambuc } else { 335*433d6423SLionel Sambuc if ((bp = front) == NULL) panic("all buffers in use: %d", nr_bufs); 336*433d6423SLionel Sambuc } 337*433d6423SLionel Sambuc assert(bp); 338*433d6423SLionel Sambuc 339*433d6423SLionel Sambuc rm_lru(bp); 340*433d6423SLionel Sambuc 341*433d6423SLionel Sambuc /* Remove the block that was just taken from its hash chain. */ 342*433d6423SLionel Sambuc b = BUFHASH(bp->lmfs_blocknr); 343*433d6423SLionel Sambuc prev_ptr = buf_hash[b]; 344*433d6423SLionel Sambuc if (prev_ptr == bp) { 345*433d6423SLionel Sambuc buf_hash[b] = bp->lmfs_hash; 346*433d6423SLionel Sambuc } else { 347*433d6423SLionel Sambuc /* The block just taken is not on the front of its hash chain. */ 348*433d6423SLionel Sambuc while (prev_ptr->lmfs_hash != NULL) 349*433d6423SLionel Sambuc if (prev_ptr->lmfs_hash == bp) { 350*433d6423SLionel Sambuc prev_ptr->lmfs_hash = bp->lmfs_hash; /* found it */ 351*433d6423SLionel Sambuc break; 352*433d6423SLionel Sambuc } else { 353*433d6423SLionel Sambuc prev_ptr = prev_ptr->lmfs_hash; /* keep looking */ 354*433d6423SLionel Sambuc } 355*433d6423SLionel Sambuc } 356*433d6423SLionel Sambuc 357*433d6423SLionel Sambuc freeblock(bp); 358*433d6423SLionel Sambuc 359*433d6423SLionel Sambuc bp->lmfs_inode = ino; 360*433d6423SLionel Sambuc bp->lmfs_inode_offset = ino_off; 361*433d6423SLionel Sambuc 362*433d6423SLionel Sambuc bp->lmfs_flags = VMMC_BLOCK_LOCKED; 363*433d6423SLionel Sambuc bp->lmfs_needsetcache = 0; 364*433d6423SLionel Sambuc bp->lmfs_dev = dev; /* fill in device number */ 365*433d6423SLionel Sambuc bp->lmfs_blocknr = block; /* fill in block number */ 366*433d6423SLionel Sambuc ASSERT(bp->lmfs_count == 0); 367*433d6423SLionel Sambuc raisecount(bp); 368*433d6423SLionel Sambuc b = BUFHASH(bp->lmfs_blocknr); 369*433d6423SLionel Sambuc bp->lmfs_hash = buf_hash[b]; 370*433d6423SLionel Sambuc 371*433d6423SLionel Sambuc buf_hash[b] = bp; /* add to hash list */ 372*433d6423SLionel Sambuc 373*433d6423SLionel Sambuc assert(dev != NO_DEV); 374*433d6423SLionel Sambuc 375*433d6423SLionel Sambuc /* Block is not found in our cache, but we do want it 376*433d6423SLionel Sambuc * if it's in the vm cache. 377*433d6423SLionel Sambuc */ 378*433d6423SLionel Sambuc assert(!bp->data); 379*433d6423SLionel Sambuc assert(!bp->lmfs_bytes); 380*433d6423SLionel Sambuc if(vmcache) { 381*433d6423SLionel Sambuc if((bp->data = vm_map_cacheblock(dev, dev_off, ino, ino_off, 382*433d6423SLionel Sambuc &bp->lmfs_flags, fs_block_size)) != MAP_FAILED) { 383*433d6423SLionel Sambuc bp->lmfs_bytes = fs_block_size; 384*433d6423SLionel Sambuc ASSERT(!bp->lmfs_needsetcache); 385*433d6423SLionel Sambuc return bp; 386*433d6423SLionel Sambuc } 387*433d6423SLionel Sambuc } 388*433d6423SLionel Sambuc bp->data = NULL; 389*433d6423SLionel Sambuc 390*433d6423SLionel Sambuc /* Not in the cache; reserve memory for its contents. */ 391*433d6423SLionel Sambuc 392*433d6423SLionel Sambuc lmfs_alloc_block(bp); 393*433d6423SLionel Sambuc 394*433d6423SLionel Sambuc assert(bp->data); 395*433d6423SLionel Sambuc 396*433d6423SLionel Sambuc if(only_search == PREFETCH) { 397*433d6423SLionel Sambuc /* PREFETCH: don't do i/o. */ 398*433d6423SLionel Sambuc bp->lmfs_dev = NO_DEV; 399*433d6423SLionel Sambuc } else if (only_search == NORMAL) { 400*433d6423SLionel Sambuc read_block(bp); 401*433d6423SLionel Sambuc } else if(only_search == NO_READ) { 402*433d6423SLionel Sambuc /* This block will be overwritten by new contents. */ 403*433d6423SLionel Sambuc } else 404*433d6423SLionel Sambuc panic("unexpected only_search value: %d", only_search); 405*433d6423SLionel Sambuc 406*433d6423SLionel Sambuc assert(bp->data); 407*433d6423SLionel Sambuc 408*433d6423SLionel Sambuc return(bp); /* return the newly acquired block */ 409*433d6423SLionel Sambuc } 410*433d6423SLionel Sambuc 411*433d6423SLionel Sambuc /*===========================================================================* 412*433d6423SLionel Sambuc * lmfs_put_block * 413*433d6423SLionel Sambuc *===========================================================================*/ 414*433d6423SLionel Sambuc void lmfs_put_block( 415*433d6423SLionel Sambuc struct buf *bp, /* pointer to the buffer to be released */ 416*433d6423SLionel Sambuc int block_type /* INODE_BLOCK, DIRECTORY_BLOCK, or whatever */ 417*433d6423SLionel Sambuc ) 418*433d6423SLionel Sambuc { 419*433d6423SLionel Sambuc /* Return a block to the list of available blocks. Depending on 'block_type' 420*433d6423SLionel Sambuc * it may be put on the front or rear of the LRU chain. Blocks that are 421*433d6423SLionel Sambuc * expected to be needed again shortly (e.g., partially full data blocks) 422*433d6423SLionel Sambuc * go on the rear; blocks that are unlikely to be needed again shortly 423*433d6423SLionel Sambuc * (e.g., full data blocks) go on the front. Blocks whose loss can hurt 424*433d6423SLionel Sambuc * the integrity of the file system (e.g., inode blocks) are written to 425*433d6423SLionel Sambuc * disk immediately if they are dirty. 426*433d6423SLionel Sambuc */ 427*433d6423SLionel Sambuc dev_t dev; 428*433d6423SLionel Sambuc off_t dev_off; 429*433d6423SLionel Sambuc int r; 430*433d6423SLionel Sambuc 431*433d6423SLionel Sambuc if (bp == NULL) return; /* it is easier to check here than in caller */ 432*433d6423SLionel Sambuc 433*433d6423SLionel Sambuc dev = bp->lmfs_dev; 434*433d6423SLionel Sambuc 435*433d6423SLionel Sambuc dev_off = (off_t) bp->lmfs_blocknr * fs_block_size; 436*433d6423SLionel Sambuc 437*433d6423SLionel Sambuc lowercount(bp); 438*433d6423SLionel Sambuc if (bp->lmfs_count != 0) return; /* block is still in use */ 439*433d6423SLionel Sambuc 440*433d6423SLionel Sambuc /* Put this block back on the LRU chain. */ 441*433d6423SLionel Sambuc if (dev == DEV_RAM || (block_type & ONE_SHOT)) { 442*433d6423SLionel Sambuc /* Block probably won't be needed quickly. Put it on front of chain. 443*433d6423SLionel Sambuc * It will be the next block to be evicted from the cache. 444*433d6423SLionel Sambuc */ 445*433d6423SLionel Sambuc bp->lmfs_prev = NULL; 446*433d6423SLionel Sambuc bp->lmfs_next = front; 447*433d6423SLionel Sambuc if (front == NULL) 448*433d6423SLionel Sambuc rear = bp; /* LRU chain was empty */ 449*433d6423SLionel Sambuc else 450*433d6423SLionel Sambuc front->lmfs_prev = bp; 451*433d6423SLionel Sambuc front = bp; 452*433d6423SLionel Sambuc } 453*433d6423SLionel Sambuc else { 454*433d6423SLionel Sambuc /* Block probably will be needed quickly. Put it on rear of chain. 455*433d6423SLionel Sambuc * It will not be evicted from the cache for a long time. 456*433d6423SLionel Sambuc */ 457*433d6423SLionel Sambuc bp->lmfs_prev = rear; 458*433d6423SLionel Sambuc bp->lmfs_next = NULL; 459*433d6423SLionel Sambuc if (rear == NULL) 460*433d6423SLionel Sambuc front = bp; 461*433d6423SLionel Sambuc else 462*433d6423SLionel Sambuc rear->lmfs_next = bp; 463*433d6423SLionel Sambuc rear = bp; 464*433d6423SLionel Sambuc } 465*433d6423SLionel Sambuc 466*433d6423SLionel Sambuc assert(bp->lmfs_flags & VMMC_BLOCK_LOCKED); 467*433d6423SLionel Sambuc bp->lmfs_flags &= ~VMMC_BLOCK_LOCKED; 468*433d6423SLionel Sambuc 469*433d6423SLionel Sambuc /* block has sensible content - if necesary, identify it to VM */ 470*433d6423SLionel Sambuc if(vmcache && bp->lmfs_needsetcache && dev != NO_DEV) { 471*433d6423SLionel Sambuc if((r=vm_set_cacheblock(bp->data, dev, dev_off, 472*433d6423SLionel Sambuc bp->lmfs_inode, bp->lmfs_inode_offset, 473*433d6423SLionel Sambuc &bp->lmfs_flags, fs_block_size)) != OK) { 474*433d6423SLionel Sambuc if(r == ENOSYS) { 475*433d6423SLionel Sambuc printf("libminixfs: ENOSYS, disabling VM calls\n"); 476*433d6423SLionel Sambuc vmcache = 0; 477*433d6423SLionel Sambuc } else { 478*433d6423SLionel Sambuc panic("libminixfs: setblock of %p dev 0x%llx off " 479*433d6423SLionel Sambuc "0x%llx failed\n", bp->data, dev, dev_off); 480*433d6423SLionel Sambuc } 481*433d6423SLionel Sambuc } 482*433d6423SLionel Sambuc } 483*433d6423SLionel Sambuc bp->lmfs_needsetcache = 0; 484*433d6423SLionel Sambuc 485*433d6423SLionel Sambuc } 486*433d6423SLionel Sambuc 487*433d6423SLionel Sambuc void lmfs_cache_reevaluate(dev_t dev) 488*433d6423SLionel Sambuc { 489*433d6423SLionel Sambuc if(bufs_in_use == 0 && dev != NO_DEV) { 490*433d6423SLionel Sambuc /* if the cache isn't in use any more, we could resize it. */ 491*433d6423SLionel Sambuc cache_heuristic_check(major(dev)); 492*433d6423SLionel Sambuc } 493*433d6423SLionel Sambuc } 494*433d6423SLionel Sambuc 495*433d6423SLionel Sambuc /*===========================================================================* 496*433d6423SLionel Sambuc * read_block * 497*433d6423SLionel Sambuc *===========================================================================*/ 498*433d6423SLionel Sambuc static void read_block( 499*433d6423SLionel Sambuc struct buf *bp /* buffer pointer */ 500*433d6423SLionel Sambuc ) 501*433d6423SLionel Sambuc { 502*433d6423SLionel Sambuc /* Read or write a disk block. This is the only routine in which actual disk 503*433d6423SLionel Sambuc * I/O is invoked. If an error occurs, a message is printed here, but the error 504*433d6423SLionel Sambuc * is not reported to the caller. If the error occurred while purging a block 505*433d6423SLionel Sambuc * from the cache, it is not clear what the caller could do about it anyway. 506*433d6423SLionel Sambuc */ 507*433d6423SLionel Sambuc int r, op_failed; 508*433d6423SLionel Sambuc off_t pos; 509*433d6423SLionel Sambuc dev_t dev = bp->lmfs_dev; 510*433d6423SLionel Sambuc 511*433d6423SLionel Sambuc op_failed = 0; 512*433d6423SLionel Sambuc 513*433d6423SLionel Sambuc assert(dev != NO_DEV); 514*433d6423SLionel Sambuc 515*433d6423SLionel Sambuc ASSERT(bp->lmfs_bytes == fs_block_size); 516*433d6423SLionel Sambuc ASSERT(fs_block_size > 0); 517*433d6423SLionel Sambuc 518*433d6423SLionel Sambuc pos = (off_t)bp->lmfs_blocknr * fs_block_size; 519*433d6423SLionel Sambuc if(fs_block_size > PAGE_SIZE) { 520*433d6423SLionel Sambuc #define MAXPAGES 20 521*433d6423SLionel Sambuc vir_bytes blockrem, vaddr = (vir_bytes) bp->data; 522*433d6423SLionel Sambuc int p = 0; 523*433d6423SLionel Sambuc static iovec_t iovec[MAXPAGES]; 524*433d6423SLionel Sambuc blockrem = fs_block_size; 525*433d6423SLionel Sambuc while(blockrem > 0) { 526*433d6423SLionel Sambuc vir_bytes chunk = blockrem >= PAGE_SIZE ? PAGE_SIZE : blockrem; 527*433d6423SLionel Sambuc iovec[p].iov_addr = vaddr; 528*433d6423SLionel Sambuc iovec[p].iov_size = chunk; 529*433d6423SLionel Sambuc vaddr += chunk; 530*433d6423SLionel Sambuc blockrem -= chunk; 531*433d6423SLionel Sambuc p++; 532*433d6423SLionel Sambuc } 533*433d6423SLionel Sambuc r = bdev_gather(dev, pos, iovec, p, BDEV_NOFLAGS); 534*433d6423SLionel Sambuc } else { 535*433d6423SLionel Sambuc r = bdev_read(dev, pos, bp->data, fs_block_size, 536*433d6423SLionel Sambuc BDEV_NOFLAGS); 537*433d6423SLionel Sambuc } 538*433d6423SLionel Sambuc if (r < 0) { 539*433d6423SLionel Sambuc printf("fs cache: I/O error on device %d/%d, block %u\n", 540*433d6423SLionel Sambuc major(dev), minor(dev), bp->lmfs_blocknr); 541*433d6423SLionel Sambuc op_failed = 1; 542*433d6423SLionel Sambuc } else if (r != (ssize_t) fs_block_size) { 543*433d6423SLionel Sambuc r = END_OF_FILE; 544*433d6423SLionel Sambuc op_failed = 1; 545*433d6423SLionel Sambuc } 546*433d6423SLionel Sambuc 547*433d6423SLionel Sambuc if (op_failed) { 548*433d6423SLionel Sambuc bp->lmfs_dev = NO_DEV; /* invalidate block */ 549*433d6423SLionel Sambuc 550*433d6423SLionel Sambuc /* Report read errors to interested parties. */ 551*433d6423SLionel Sambuc rdwt_err = r; 552*433d6423SLionel Sambuc } 553*433d6423SLionel Sambuc 554*433d6423SLionel Sambuc } 555*433d6423SLionel Sambuc 556*433d6423SLionel Sambuc /*===========================================================================* 557*433d6423SLionel Sambuc * lmfs_invalidate * 558*433d6423SLionel Sambuc *===========================================================================*/ 559*433d6423SLionel Sambuc void lmfs_invalidate( 560*433d6423SLionel Sambuc dev_t device /* device whose blocks are to be purged */ 561*433d6423SLionel Sambuc ) 562*433d6423SLionel Sambuc { 563*433d6423SLionel Sambuc /* Remove all the blocks belonging to some device from the cache. */ 564*433d6423SLionel Sambuc 565*433d6423SLionel Sambuc register struct buf *bp; 566*433d6423SLionel Sambuc 567*433d6423SLionel Sambuc for (bp = &buf[0]; bp < &buf[nr_bufs]; bp++) { 568*433d6423SLionel Sambuc if (bp->lmfs_dev == device) { 569*433d6423SLionel Sambuc assert(bp->data); 570*433d6423SLionel Sambuc assert(bp->lmfs_bytes > 0); 571*433d6423SLionel Sambuc munmap_t(bp->data, bp->lmfs_bytes); 572*433d6423SLionel Sambuc bp->lmfs_dev = NO_DEV; 573*433d6423SLionel Sambuc bp->lmfs_bytes = 0; 574*433d6423SLionel Sambuc bp->data = NULL; 575*433d6423SLionel Sambuc } 576*433d6423SLionel Sambuc } 577*433d6423SLionel Sambuc 578*433d6423SLionel Sambuc vm_clear_cache(device); 579*433d6423SLionel Sambuc } 580*433d6423SLionel Sambuc 581*433d6423SLionel Sambuc /*===========================================================================* 582*433d6423SLionel Sambuc * flushall * 583*433d6423SLionel Sambuc *===========================================================================*/ 584*433d6423SLionel Sambuc static void flushall(dev_t dev) 585*433d6423SLionel Sambuc { 586*433d6423SLionel Sambuc /* Flush all dirty blocks for one device. */ 587*433d6423SLionel Sambuc 588*433d6423SLionel Sambuc register struct buf *bp; 589*433d6423SLionel Sambuc static struct buf **dirty; /* static so it isn't on stack */ 590*433d6423SLionel Sambuc static unsigned int dirtylistsize = 0; 591*433d6423SLionel Sambuc int ndirty; 592*433d6423SLionel Sambuc 593*433d6423SLionel Sambuc if(dirtylistsize != nr_bufs) { 594*433d6423SLionel Sambuc if(dirtylistsize > 0) { 595*433d6423SLionel Sambuc assert(dirty != NULL); 596*433d6423SLionel Sambuc free(dirty); 597*433d6423SLionel Sambuc } 598*433d6423SLionel Sambuc if(!(dirty = malloc(sizeof(dirty[0])*nr_bufs))) 599*433d6423SLionel Sambuc panic("couldn't allocate dirty buf list"); 600*433d6423SLionel Sambuc dirtylistsize = nr_bufs; 601*433d6423SLionel Sambuc } 602*433d6423SLionel Sambuc 603*433d6423SLionel Sambuc for (bp = &buf[0], ndirty = 0; bp < &buf[nr_bufs]; bp++) { 604*433d6423SLionel Sambuc if (!lmfs_isclean(bp) && bp->lmfs_dev == dev) { 605*433d6423SLionel Sambuc dirty[ndirty++] = bp; 606*433d6423SLionel Sambuc } 607*433d6423SLionel Sambuc } 608*433d6423SLionel Sambuc 609*433d6423SLionel Sambuc lmfs_rw_scattered(dev, dirty, ndirty, WRITING); 610*433d6423SLionel Sambuc } 611*433d6423SLionel Sambuc 612*433d6423SLionel Sambuc /*===========================================================================* 613*433d6423SLionel Sambuc * lmfs_rw_scattered * 614*433d6423SLionel Sambuc *===========================================================================*/ 615*433d6423SLionel Sambuc void lmfs_rw_scattered( 616*433d6423SLionel Sambuc dev_t dev, /* major-minor device number */ 617*433d6423SLionel Sambuc struct buf **bufq, /* pointer to array of buffers */ 618*433d6423SLionel Sambuc int bufqsize, /* number of buffers */ 619*433d6423SLionel Sambuc int rw_flag /* READING or WRITING */ 620*433d6423SLionel Sambuc ) 621*433d6423SLionel Sambuc { 622*433d6423SLionel Sambuc /* Read or write scattered data from a device. */ 623*433d6423SLionel Sambuc 624*433d6423SLionel Sambuc register struct buf *bp; 625*433d6423SLionel Sambuc int gap; 626*433d6423SLionel Sambuc register int i; 627*433d6423SLionel Sambuc register iovec_t *iop; 628*433d6423SLionel Sambuc static iovec_t iovec[NR_IOREQS]; 629*433d6423SLionel Sambuc off_t pos; 630*433d6423SLionel Sambuc int iov_per_block; 631*433d6423SLionel Sambuc int start_in_use = bufs_in_use, start_bufqsize = bufqsize; 632*433d6423SLionel Sambuc 633*433d6423SLionel Sambuc assert(bufqsize >= 0); 634*433d6423SLionel Sambuc if(bufqsize == 0) return; 635*433d6423SLionel Sambuc 636*433d6423SLionel Sambuc /* for READING, check all buffers on the list are obtained and held 637*433d6423SLionel Sambuc * (count > 0) 638*433d6423SLionel Sambuc */ 639*433d6423SLionel Sambuc if (rw_flag == READING) { 640*433d6423SLionel Sambuc for(i = 0; i < bufqsize; i++) { 641*433d6423SLionel Sambuc assert(bufq[i] != NULL); 642*433d6423SLionel Sambuc assert(bufq[i]->lmfs_count > 0); 643*433d6423SLionel Sambuc } 644*433d6423SLionel Sambuc 645*433d6423SLionel Sambuc /* therefore they are all 'in use' and must be at least this many */ 646*433d6423SLionel Sambuc assert(start_in_use >= start_bufqsize); 647*433d6423SLionel Sambuc } 648*433d6423SLionel Sambuc 649*433d6423SLionel Sambuc assert(dev != NO_DEV); 650*433d6423SLionel Sambuc assert(fs_block_size > 0); 651*433d6423SLionel Sambuc iov_per_block = roundup(fs_block_size, PAGE_SIZE) / PAGE_SIZE; 652*433d6423SLionel Sambuc assert(iov_per_block < NR_IOREQS); 653*433d6423SLionel Sambuc 654*433d6423SLionel Sambuc /* (Shell) sort buffers on lmfs_blocknr. */ 655*433d6423SLionel Sambuc gap = 1; 656*433d6423SLionel Sambuc do 657*433d6423SLionel Sambuc gap = 3 * gap + 1; 658*433d6423SLionel Sambuc while (gap <= bufqsize); 659*433d6423SLionel Sambuc while (gap != 1) { 660*433d6423SLionel Sambuc int j; 661*433d6423SLionel Sambuc gap /= 3; 662*433d6423SLionel Sambuc for (j = gap; j < bufqsize; j++) { 663*433d6423SLionel Sambuc for (i = j - gap; 664*433d6423SLionel Sambuc i >= 0 && bufq[i]->lmfs_blocknr > bufq[i + gap]->lmfs_blocknr; 665*433d6423SLionel Sambuc i -= gap) { 666*433d6423SLionel Sambuc bp = bufq[i]; 667*433d6423SLionel Sambuc bufq[i] = bufq[i + gap]; 668*433d6423SLionel Sambuc bufq[i + gap] = bp; 669*433d6423SLionel Sambuc } 670*433d6423SLionel Sambuc } 671*433d6423SLionel Sambuc } 672*433d6423SLionel Sambuc 673*433d6423SLionel Sambuc /* Set up I/O vector and do I/O. The result of bdev I/O is OK if everything 674*433d6423SLionel Sambuc * went fine, otherwise the error code for the first failed transfer. 675*433d6423SLionel Sambuc */ 676*433d6423SLionel Sambuc while (bufqsize > 0) { 677*433d6423SLionel Sambuc int nblocks = 0, niovecs = 0; 678*433d6423SLionel Sambuc int r; 679*433d6423SLionel Sambuc for (iop = iovec; nblocks < bufqsize; nblocks++) { 680*433d6423SLionel Sambuc int p; 681*433d6423SLionel Sambuc vir_bytes vdata, blockrem; 682*433d6423SLionel Sambuc bp = bufq[nblocks]; 683*433d6423SLionel Sambuc if (bp->lmfs_blocknr != (block_t) bufq[0]->lmfs_blocknr + nblocks) 684*433d6423SLionel Sambuc break; 685*433d6423SLionel Sambuc if(niovecs >= NR_IOREQS-iov_per_block) break; 686*433d6423SLionel Sambuc vdata = (vir_bytes) bp->data; 687*433d6423SLionel Sambuc blockrem = fs_block_size; 688*433d6423SLionel Sambuc for(p = 0; p < iov_per_block; p++) { 689*433d6423SLionel Sambuc vir_bytes chunk = blockrem < PAGE_SIZE ? blockrem : PAGE_SIZE; 690*433d6423SLionel Sambuc iop->iov_addr = vdata; 691*433d6423SLionel Sambuc iop->iov_size = chunk; 692*433d6423SLionel Sambuc vdata += PAGE_SIZE; 693*433d6423SLionel Sambuc blockrem -= chunk; 694*433d6423SLionel Sambuc iop++; 695*433d6423SLionel Sambuc niovecs++; 696*433d6423SLionel Sambuc } 697*433d6423SLionel Sambuc assert(p == iov_per_block); 698*433d6423SLionel Sambuc assert(blockrem == 0); 699*433d6423SLionel Sambuc } 700*433d6423SLionel Sambuc 701*433d6423SLionel Sambuc assert(nblocks > 0); 702*433d6423SLionel Sambuc assert(niovecs > 0); 703*433d6423SLionel Sambuc 704*433d6423SLionel Sambuc pos = (off_t)bufq[0]->lmfs_blocknr * fs_block_size; 705*433d6423SLionel Sambuc if (rw_flag == READING) 706*433d6423SLionel Sambuc r = bdev_gather(dev, pos, iovec, niovecs, BDEV_NOFLAGS); 707*433d6423SLionel Sambuc else 708*433d6423SLionel Sambuc r = bdev_scatter(dev, pos, iovec, niovecs, BDEV_NOFLAGS); 709*433d6423SLionel Sambuc 710*433d6423SLionel Sambuc /* Harvest the results. The driver may have returned an error, or it 711*433d6423SLionel Sambuc * may have done less than what we asked for. 712*433d6423SLionel Sambuc */ 713*433d6423SLionel Sambuc if (r < 0) { 714*433d6423SLionel Sambuc printf("fs cache: I/O error %d on device %d/%d, block %u\n", 715*433d6423SLionel Sambuc r, major(dev), minor(dev), bufq[0]->lmfs_blocknr); 716*433d6423SLionel Sambuc } 717*433d6423SLionel Sambuc for (i = 0; i < nblocks; i++) { 718*433d6423SLionel Sambuc bp = bufq[i]; 719*433d6423SLionel Sambuc if (r < (ssize_t) fs_block_size) { 720*433d6423SLionel Sambuc /* Transfer failed. */ 721*433d6423SLionel Sambuc if (i == 0) { 722*433d6423SLionel Sambuc bp->lmfs_dev = NO_DEV; /* Invalidate block */ 723*433d6423SLionel Sambuc } 724*433d6423SLionel Sambuc break; 725*433d6423SLionel Sambuc } 726*433d6423SLionel Sambuc if (rw_flag == READING) { 727*433d6423SLionel Sambuc bp->lmfs_dev = dev; /* validate block */ 728*433d6423SLionel Sambuc lmfs_put_block(bp, PARTIAL_DATA_BLOCK); 729*433d6423SLionel Sambuc } else { 730*433d6423SLionel Sambuc MARKCLEAN(bp); 731*433d6423SLionel Sambuc } 732*433d6423SLionel Sambuc r -= fs_block_size; 733*433d6423SLionel Sambuc } 734*433d6423SLionel Sambuc 735*433d6423SLionel Sambuc bufq += i; 736*433d6423SLionel Sambuc bufqsize -= i; 737*433d6423SLionel Sambuc 738*433d6423SLionel Sambuc if (rw_flag == READING) { 739*433d6423SLionel Sambuc /* Don't bother reading more than the device is willing to 740*433d6423SLionel Sambuc * give at this time. Don't forget to release those extras. 741*433d6423SLionel Sambuc */ 742*433d6423SLionel Sambuc while (bufqsize > 0) { 743*433d6423SLionel Sambuc lmfs_put_block(*bufq++, PARTIAL_DATA_BLOCK); 744*433d6423SLionel Sambuc bufqsize--; 745*433d6423SLionel Sambuc } 746*433d6423SLionel Sambuc } 747*433d6423SLionel Sambuc if (rw_flag == WRITING && i == 0) { 748*433d6423SLionel Sambuc /* We're not making progress, this means we might keep 749*433d6423SLionel Sambuc * looping. Buffers remain dirty if un-written. Buffers are 750*433d6423SLionel Sambuc * lost if invalidate()d or LRU-removed while dirty. This 751*433d6423SLionel Sambuc * is better than keeping unwritable blocks around forever.. 752*433d6423SLionel Sambuc */ 753*433d6423SLionel Sambuc break; 754*433d6423SLionel Sambuc } 755*433d6423SLionel Sambuc } 756*433d6423SLionel Sambuc 757*433d6423SLionel Sambuc if(rw_flag == READING) { 758*433d6423SLionel Sambuc assert(start_in_use >= start_bufqsize); 759*433d6423SLionel Sambuc 760*433d6423SLionel Sambuc /* READING callers assume all bufs are released. */ 761*433d6423SLionel Sambuc assert(start_in_use - start_bufqsize == bufs_in_use); 762*433d6423SLionel Sambuc } 763*433d6423SLionel Sambuc } 764*433d6423SLionel Sambuc 765*433d6423SLionel Sambuc /*===========================================================================* 766*433d6423SLionel Sambuc * rm_lru * 767*433d6423SLionel Sambuc *===========================================================================*/ 768*433d6423SLionel Sambuc static void rm_lru(struct buf *bp) 769*433d6423SLionel Sambuc { 770*433d6423SLionel Sambuc /* Remove a block from its LRU chain. */ 771*433d6423SLionel Sambuc struct buf *next_ptr, *prev_ptr; 772*433d6423SLionel Sambuc 773*433d6423SLionel Sambuc next_ptr = bp->lmfs_next; /* successor on LRU chain */ 774*433d6423SLionel Sambuc prev_ptr = bp->lmfs_prev; /* predecessor on LRU chain */ 775*433d6423SLionel Sambuc if (prev_ptr != NULL) 776*433d6423SLionel Sambuc prev_ptr->lmfs_next = next_ptr; 777*433d6423SLionel Sambuc else 778*433d6423SLionel Sambuc front = next_ptr; /* this block was at front of chain */ 779*433d6423SLionel Sambuc 780*433d6423SLionel Sambuc if (next_ptr != NULL) 781*433d6423SLionel Sambuc next_ptr->lmfs_prev = prev_ptr; 782*433d6423SLionel Sambuc else 783*433d6423SLionel Sambuc rear = prev_ptr; /* this block was at rear of chain */ 784*433d6423SLionel Sambuc } 785*433d6423SLionel Sambuc 786*433d6423SLionel Sambuc /*===========================================================================* 787*433d6423SLionel Sambuc * cache_resize * 788*433d6423SLionel Sambuc *===========================================================================*/ 789*433d6423SLionel Sambuc static void cache_resize(unsigned int blocksize, unsigned int bufs) 790*433d6423SLionel Sambuc { 791*433d6423SLionel Sambuc struct buf *bp; 792*433d6423SLionel Sambuc 793*433d6423SLionel Sambuc assert(blocksize > 0); 794*433d6423SLionel Sambuc assert(bufs >= MINBUFS); 795*433d6423SLionel Sambuc 796*433d6423SLionel Sambuc for (bp = &buf[0]; bp < &buf[nr_bufs]; bp++) 797*433d6423SLionel Sambuc if(bp->lmfs_count != 0) panic("change blocksize with buffer in use"); 798*433d6423SLionel Sambuc 799*433d6423SLionel Sambuc lmfs_buf_pool(bufs); 800*433d6423SLionel Sambuc 801*433d6423SLionel Sambuc fs_block_size = blocksize; 802*433d6423SLionel Sambuc } 803*433d6423SLionel Sambuc 804*433d6423SLionel Sambuc static void cache_heuristic_check(int major) 805*433d6423SLionel Sambuc { 806*433d6423SLionel Sambuc int bufs, d; 807*433d6423SLionel Sambuc u64_t btotal, bfree, bused; 808*433d6423SLionel Sambuc 809*433d6423SLionel Sambuc fs_blockstats(&btotal, &bfree, &bused); 810*433d6423SLionel Sambuc 811*433d6423SLionel Sambuc bufs = fs_bufs_heuristic(10, btotal, bfree, 812*433d6423SLionel Sambuc fs_block_size, major); 813*433d6423SLionel Sambuc 814*433d6423SLionel Sambuc /* set the cache to the new heuristic size if the new one 815*433d6423SLionel Sambuc * is more than 10% off from the current one. 816*433d6423SLionel Sambuc */ 817*433d6423SLionel Sambuc d = bufs-nr_bufs; 818*433d6423SLionel Sambuc if(d < 0) d = -d; 819*433d6423SLionel Sambuc if(d*100/nr_bufs > 10) { 820*433d6423SLionel Sambuc cache_resize(fs_block_size, bufs); 821*433d6423SLionel Sambuc } 822*433d6423SLionel Sambuc } 823*433d6423SLionel Sambuc 824*433d6423SLionel Sambuc /*===========================================================================* 825*433d6423SLionel Sambuc * lmfs_set_blocksize * 826*433d6423SLionel Sambuc *===========================================================================*/ 827*433d6423SLionel Sambuc void lmfs_set_blocksize(int new_block_size, int major) 828*433d6423SLionel Sambuc { 829*433d6423SLionel Sambuc cache_resize(new_block_size, MINBUFS); 830*433d6423SLionel Sambuc cache_heuristic_check(major); 831*433d6423SLionel Sambuc 832*433d6423SLionel Sambuc /* Decide whether to use seconday cache or not. 833*433d6423SLionel Sambuc * Only do this if 834*433d6423SLionel Sambuc * - it's available, and 835*433d6423SLionel Sambuc * - use of it hasn't been disabled for this fs, and 836*433d6423SLionel Sambuc * - our main FS device isn't a memory device 837*433d6423SLionel Sambuc */ 838*433d6423SLionel Sambuc 839*433d6423SLionel Sambuc vmcache = 0; 840*433d6423SLionel Sambuc 841*433d6423SLionel Sambuc if(may_use_vmcache && !(new_block_size % PAGE_SIZE)) 842*433d6423SLionel Sambuc vmcache = 1; 843*433d6423SLionel Sambuc } 844*433d6423SLionel Sambuc 845*433d6423SLionel Sambuc /*===========================================================================* 846*433d6423SLionel Sambuc * lmfs_buf_pool * 847*433d6423SLionel Sambuc *===========================================================================*/ 848*433d6423SLionel Sambuc void lmfs_buf_pool(int new_nr_bufs) 849*433d6423SLionel Sambuc { 850*433d6423SLionel Sambuc /* Initialize the buffer pool. */ 851*433d6423SLionel Sambuc register struct buf *bp; 852*433d6423SLionel Sambuc 853*433d6423SLionel Sambuc assert(new_nr_bufs >= MINBUFS); 854*433d6423SLionel Sambuc 855*433d6423SLionel Sambuc if(nr_bufs > 0) { 856*433d6423SLionel Sambuc assert(buf); 857*433d6423SLionel Sambuc (void) fs_sync(); 858*433d6423SLionel Sambuc for (bp = &buf[0]; bp < &buf[nr_bufs]; bp++) { 859*433d6423SLionel Sambuc if(bp->data) { 860*433d6423SLionel Sambuc assert(bp->lmfs_bytes > 0); 861*433d6423SLionel Sambuc munmap_t(bp->data, bp->lmfs_bytes); 862*433d6423SLionel Sambuc } 863*433d6423SLionel Sambuc } 864*433d6423SLionel Sambuc } 865*433d6423SLionel Sambuc 866*433d6423SLionel Sambuc if(buf) 867*433d6423SLionel Sambuc free(buf); 868*433d6423SLionel Sambuc 869*433d6423SLionel Sambuc if(!(buf = calloc(sizeof(buf[0]), new_nr_bufs))) 870*433d6423SLionel Sambuc panic("couldn't allocate buf list (%d)", new_nr_bufs); 871*433d6423SLionel Sambuc 872*433d6423SLionel Sambuc if(buf_hash) 873*433d6423SLionel Sambuc free(buf_hash); 874*433d6423SLionel Sambuc if(!(buf_hash = calloc(sizeof(buf_hash[0]), new_nr_bufs))) 875*433d6423SLionel Sambuc panic("couldn't allocate buf hash list (%d)", new_nr_bufs); 876*433d6423SLionel Sambuc 877*433d6423SLionel Sambuc nr_bufs = new_nr_bufs; 878*433d6423SLionel Sambuc 879*433d6423SLionel Sambuc bufs_in_use = 0; 880*433d6423SLionel Sambuc front = &buf[0]; 881*433d6423SLionel Sambuc rear = &buf[nr_bufs - 1]; 882*433d6423SLionel Sambuc 883*433d6423SLionel Sambuc for (bp = &buf[0]; bp < &buf[nr_bufs]; bp++) { 884*433d6423SLionel Sambuc bp->lmfs_blocknr = NO_BLOCK; 885*433d6423SLionel Sambuc bp->lmfs_dev = NO_DEV; 886*433d6423SLionel Sambuc bp->lmfs_next = bp + 1; 887*433d6423SLionel Sambuc bp->lmfs_prev = bp - 1; 888*433d6423SLionel Sambuc bp->data = NULL; 889*433d6423SLionel Sambuc bp->lmfs_bytes = 0; 890*433d6423SLionel Sambuc } 891*433d6423SLionel Sambuc front->lmfs_prev = NULL; 892*433d6423SLionel Sambuc rear->lmfs_next = NULL; 893*433d6423SLionel Sambuc 894*433d6423SLionel Sambuc for (bp = &buf[0]; bp < &buf[nr_bufs]; bp++) bp->lmfs_hash = bp->lmfs_next; 895*433d6423SLionel Sambuc buf_hash[0] = front; 896*433d6423SLionel Sambuc } 897*433d6423SLionel Sambuc 898*433d6423SLionel Sambuc int lmfs_bufs_in_use(void) 899*433d6423SLionel Sambuc { 900*433d6423SLionel Sambuc return bufs_in_use; 901*433d6423SLionel Sambuc } 902*433d6423SLionel Sambuc 903*433d6423SLionel Sambuc int lmfs_nr_bufs(void) 904*433d6423SLionel Sambuc { 905*433d6423SLionel Sambuc return nr_bufs; 906*433d6423SLionel Sambuc } 907*433d6423SLionel Sambuc 908*433d6423SLionel Sambuc void lmfs_flushall(void) 909*433d6423SLionel Sambuc { 910*433d6423SLionel Sambuc struct buf *bp; 911*433d6423SLionel Sambuc for(bp = &buf[0]; bp < &buf[nr_bufs]; bp++) 912*433d6423SLionel Sambuc if(bp->lmfs_dev != NO_DEV && !lmfs_isclean(bp)) 913*433d6423SLionel Sambuc flushall(bp->lmfs_dev); 914*433d6423SLionel Sambuc } 915*433d6423SLionel Sambuc 916*433d6423SLionel Sambuc int lmfs_fs_block_size(void) 917*433d6423SLionel Sambuc { 918*433d6423SLionel Sambuc return fs_block_size; 919*433d6423SLionel Sambuc } 920*433d6423SLionel Sambuc 921*433d6423SLionel Sambuc void lmfs_may_use_vmcache(int ok) 922*433d6423SLionel Sambuc { 923*433d6423SLionel Sambuc may_use_vmcache = ok; 924*433d6423SLionel Sambuc } 925*433d6423SLionel Sambuc 926*433d6423SLionel Sambuc void lmfs_reset_rdwt_err(void) 927*433d6423SLionel Sambuc { 928*433d6423SLionel Sambuc rdwt_err = OK; 929*433d6423SLionel Sambuc } 930*433d6423SLionel Sambuc 931*433d6423SLionel Sambuc int lmfs_rdwt_err(void) 932*433d6423SLionel Sambuc { 933*433d6423SLionel Sambuc return rdwt_err; 934*433d6423SLionel Sambuc } 935*433d6423SLionel Sambuc 936*433d6423SLionel Sambuc int lmfs_do_bpeek(message *m) 937*433d6423SLionel Sambuc { 938*433d6423SLionel Sambuc block_t startblock, b, limitblock; 939*433d6423SLionel Sambuc dev_t dev = m->m_vfs_fs_breadwrite.device; 940*433d6423SLionel Sambuc off_t extra, pos = m->m_vfs_fs_breadwrite.seek_pos; 941*433d6423SLionel Sambuc size_t len = m->m_vfs_fs_breadwrite.nbytes; 942*433d6423SLionel Sambuc struct buf *bp; 943*433d6423SLionel Sambuc 944*433d6423SLionel Sambuc assert(m->m_type == REQ_BPEEK); 945*433d6423SLionel Sambuc assert(fs_block_size > 0); 946*433d6423SLionel Sambuc assert(dev != NO_DEV); 947*433d6423SLionel Sambuc 948*433d6423SLionel Sambuc if(!vmcache) { return ENXIO; } 949*433d6423SLionel Sambuc 950*433d6423SLionel Sambuc assert(!(fs_block_size % PAGE_SIZE)); 951*433d6423SLionel Sambuc 952*433d6423SLionel Sambuc if((extra=(pos % fs_block_size))) { 953*433d6423SLionel Sambuc pos -= extra; 954*433d6423SLionel Sambuc len += extra; 955*433d6423SLionel Sambuc } 956*433d6423SLionel Sambuc 957*433d6423SLionel Sambuc len = roundup(len, fs_block_size); 958*433d6423SLionel Sambuc 959*433d6423SLionel Sambuc startblock = pos/fs_block_size; 960*433d6423SLionel Sambuc limitblock = startblock + len/fs_block_size; 961*433d6423SLionel Sambuc 962*433d6423SLionel Sambuc for(b = startblock; b < limitblock; b++) { 963*433d6423SLionel Sambuc bp = lmfs_get_block(dev, b, NORMAL); 964*433d6423SLionel Sambuc assert(bp); 965*433d6423SLionel Sambuc lmfs_put_block(bp, FULL_DATA_BLOCK); 966*433d6423SLionel Sambuc } 967*433d6423SLionel Sambuc 968*433d6423SLionel Sambuc return OK; 969*433d6423SLionel Sambuc } 970