1433d6423SLionel Sambuc 2433d6423SLionel Sambuc #define _SYSTEM 3433d6423SLionel Sambuc 4433d6423SLionel Sambuc #include <assert.h> 5433d6423SLionel Sambuc #include <errno.h> 6433d6423SLionel Sambuc #include <math.h> 7433d6423SLionel Sambuc #include <stdlib.h> 8433d6423SLionel Sambuc 9433d6423SLionel Sambuc #include <machine/vmparam.h> 10433d6423SLionel Sambuc 11433d6423SLionel Sambuc #include <sys/param.h> 12433d6423SLionel Sambuc #include <sys/mman.h> 13433d6423SLionel Sambuc 14433d6423SLionel Sambuc #include <minix/dmap.h> 15433d6423SLionel Sambuc #include <minix/libminixfs.h> 16433d6423SLionel Sambuc #include <minix/syslib.h> 17433d6423SLionel Sambuc #include <minix/sysutil.h> 18433d6423SLionel Sambuc #include <minix/u64.h> 19433d6423SLionel Sambuc #include <minix/bdev.h> 20433d6423SLionel Sambuc 21433d6423SLionel Sambuc #define BUFHASH(b) ((b) % nr_bufs) 22433d6423SLionel Sambuc #define MARKCLEAN lmfs_markclean 23433d6423SLionel Sambuc 24433d6423SLionel Sambuc #define MINBUFS 6 /* minimal no of bufs for sanity check */ 25433d6423SLionel Sambuc 26433d6423SLionel Sambuc static struct buf *front; /* points to least recently used free block */ 27433d6423SLionel Sambuc static struct buf *rear; /* points to most recently used free block */ 28433d6423SLionel Sambuc static unsigned int bufs_in_use;/* # bufs currently in use (not on free list)*/ 29433d6423SLionel Sambuc 30433d6423SLionel Sambuc static void rm_lru(struct buf *bp); 31433d6423SLionel Sambuc static void read_block(struct buf *); 32433d6423SLionel Sambuc static void freeblock(struct buf *bp); 33433d6423SLionel Sambuc static void cache_heuristic_check(int major); 34433d6423SLionel Sambuc 35433d6423SLionel Sambuc static int vmcache = 0; /* are we using vm's secondary cache? (initially not) */ 36433d6423SLionel Sambuc 37433d6423SLionel Sambuc static struct buf *buf; 38433d6423SLionel Sambuc static struct buf **buf_hash; /* the buffer hash table */ 39433d6423SLionel Sambuc static unsigned int nr_bufs; 40433d6423SLionel Sambuc static int may_use_vmcache; 41433d6423SLionel Sambuc 42*65f76edbSDavid van Moolenbroek static size_t fs_block_size = PAGE_SIZE; /* raw i/o block size */ 43433d6423SLionel Sambuc 44433d6423SLionel Sambuc static int rdwt_err; 45433d6423SLionel Sambuc 46433d6423SLionel Sambuc static int quiet = 0; 47433d6423SLionel Sambuc 48433d6423SLionel Sambuc void lmfs_setquiet(int q) { quiet = q; } 49433d6423SLionel Sambuc 50433d6423SLionel Sambuc static u32_t fs_bufs_heuristic(int minbufs, u32_t btotal, u64_t bfree, 51433d6423SLionel Sambuc int blocksize, dev_t majordev) 52433d6423SLionel Sambuc { 53433d6423SLionel Sambuc struct vm_stats_info vsi; 54433d6423SLionel Sambuc int bufs; 55433d6423SLionel Sambuc u32_t kbytes_used_fs, kbytes_total_fs, kbcache, kb_fsmax; 56433d6423SLionel Sambuc u32_t kbytes_remain_mem; 57433d6423SLionel Sambuc u64_t bused; 58433d6423SLionel Sambuc 59433d6423SLionel Sambuc bused = btotal-bfree; 60433d6423SLionel Sambuc 61433d6423SLionel Sambuc /* set a reasonable cache size; cache at most a certain 62433d6423SLionel Sambuc * portion of the used FS, and at most a certain %age of remaining 63433d6423SLionel Sambuc * memory 64433d6423SLionel Sambuc */ 65433d6423SLionel Sambuc if(vm_info_stats(&vsi) != OK) { 66433d6423SLionel Sambuc bufs = 1024; 67433d6423SLionel Sambuc if(!quiet) 68433d6423SLionel Sambuc printf("fslib: heuristic info fail: default to %d bufs\n", bufs); 69433d6423SLionel Sambuc return bufs; 70433d6423SLionel Sambuc } 71433d6423SLionel Sambuc 72433d6423SLionel Sambuc /* remaining free memory is unused memory plus memory in used for cache, 73433d6423SLionel Sambuc * as the cache can be evicted 74433d6423SLionel Sambuc */ 75433d6423SLionel Sambuc kbytes_remain_mem = (u64_t)(vsi.vsi_free + vsi.vsi_cached) * 76433d6423SLionel Sambuc vsi.vsi_pagesize / 1024; 77433d6423SLionel Sambuc 78433d6423SLionel Sambuc /* check fs usage. */ 79433d6423SLionel Sambuc kbytes_used_fs = (unsigned long)(((u64_t)bused * blocksize) / 1024); 80433d6423SLionel Sambuc kbytes_total_fs = (unsigned long)(((u64_t)btotal * blocksize) / 1024); 81433d6423SLionel Sambuc 82433d6423SLionel Sambuc /* heuristic for a desired cache size based on FS usage; 83433d6423SLionel Sambuc * but never bigger than half of the total filesystem 84433d6423SLionel Sambuc */ 85433d6423SLionel Sambuc kb_fsmax = sqrt_approx(kbytes_used_fs)*40; 86433d6423SLionel Sambuc kb_fsmax = MIN(kb_fsmax, kbytes_total_fs/2); 87433d6423SLionel Sambuc 88433d6423SLionel Sambuc /* heuristic for a maximum usage - 10% of remaining memory */ 89433d6423SLionel Sambuc kbcache = MIN(kbytes_remain_mem/10, kb_fsmax); 90433d6423SLionel Sambuc bufs = kbcache * 1024 / blocksize; 91433d6423SLionel Sambuc 92433d6423SLionel Sambuc /* but we simply need MINBUFS no matter what */ 93433d6423SLionel Sambuc if(bufs < minbufs) 94433d6423SLionel Sambuc bufs = minbufs; 95433d6423SLionel Sambuc 96433d6423SLionel Sambuc return bufs; 97433d6423SLionel Sambuc } 98433d6423SLionel Sambuc 99433d6423SLionel Sambuc void lmfs_blockschange(dev_t dev, int delta) 100433d6423SLionel Sambuc { 101433d6423SLionel Sambuc /* Change the number of allocated blocks by 'delta.' 102433d6423SLionel Sambuc * Also accumulate the delta since the last cache re-evaluation. 103433d6423SLionel Sambuc * If it is outside a certain band, ask the cache library to 104433d6423SLionel Sambuc * re-evaluate the cache size. 105433d6423SLionel Sambuc */ 106433d6423SLionel Sambuc static int bitdelta = 0; 107433d6423SLionel Sambuc bitdelta += delta; 108433d6423SLionel Sambuc #define BANDKB (10*1024) /* recheck cache every 10MB change */ 109*65f76edbSDavid van Moolenbroek if(bitdelta*(int)fs_block_size/1024 > BANDKB || 110*65f76edbSDavid van Moolenbroek bitdelta*(int)fs_block_size/1024 < -BANDKB) { 111433d6423SLionel Sambuc lmfs_cache_reevaluate(dev); 112433d6423SLionel Sambuc bitdelta = 0; 113433d6423SLionel Sambuc } 114433d6423SLionel Sambuc } 115433d6423SLionel Sambuc 116433d6423SLionel Sambuc void lmfs_markdirty(struct buf *bp) 117433d6423SLionel Sambuc { 118433d6423SLionel Sambuc bp->lmfs_flags |= VMMC_DIRTY; 119433d6423SLionel Sambuc } 120433d6423SLionel Sambuc 121433d6423SLionel Sambuc void lmfs_markclean(struct buf *bp) 122433d6423SLionel Sambuc { 123433d6423SLionel Sambuc bp->lmfs_flags &= ~VMMC_DIRTY; 124433d6423SLionel Sambuc } 125433d6423SLionel Sambuc 126433d6423SLionel Sambuc int lmfs_isclean(struct buf *bp) 127433d6423SLionel Sambuc { 128433d6423SLionel Sambuc return !(bp->lmfs_flags & VMMC_DIRTY); 129433d6423SLionel Sambuc } 130433d6423SLionel Sambuc 131433d6423SLionel Sambuc dev_t lmfs_dev(struct buf *bp) 132433d6423SLionel Sambuc { 133433d6423SLionel Sambuc return bp->lmfs_dev; 134433d6423SLionel Sambuc } 135433d6423SLionel Sambuc 136433d6423SLionel Sambuc int lmfs_bytes(struct buf *bp) 137433d6423SLionel Sambuc { 138433d6423SLionel Sambuc return bp->lmfs_bytes; 139433d6423SLionel Sambuc } 140433d6423SLionel Sambuc 141433d6423SLionel Sambuc static void free_unused_blocks(void) 142433d6423SLionel Sambuc { 143433d6423SLionel Sambuc struct buf *bp; 144433d6423SLionel Sambuc 145433d6423SLionel Sambuc int freed = 0, bytes = 0; 146433d6423SLionel Sambuc printf("libminixfs: freeing; %d blocks in use\n", bufs_in_use); 147433d6423SLionel Sambuc for(bp = &buf[0]; bp < &buf[nr_bufs]; bp++) { 148433d6423SLionel Sambuc if(bp->lmfs_bytes > 0 && bp->lmfs_count == 0) { 149433d6423SLionel Sambuc freed++; 150433d6423SLionel Sambuc bytes += bp->lmfs_bytes; 151433d6423SLionel Sambuc freeblock(bp); 152433d6423SLionel Sambuc } 153433d6423SLionel Sambuc } 154433d6423SLionel Sambuc printf("libminixfs: freeing; %d blocks, %d bytes\n", freed, bytes); 155433d6423SLionel Sambuc } 156433d6423SLionel Sambuc 157433d6423SLionel Sambuc static void lmfs_alloc_block(struct buf *bp) 158433d6423SLionel Sambuc { 159433d6423SLionel Sambuc int len; 160433d6423SLionel Sambuc ASSERT(!bp->data); 161433d6423SLionel Sambuc ASSERT(bp->lmfs_bytes == 0); 162433d6423SLionel Sambuc 163433d6423SLionel Sambuc len = roundup(fs_block_size, PAGE_SIZE); 164433d6423SLionel Sambuc 165433d6423SLionel Sambuc if((bp->data = mmap(0, fs_block_size, 166433d6423SLionel Sambuc PROT_READ|PROT_WRITE, MAP_PREALLOC|MAP_ANON, -1, 0)) == MAP_FAILED) { 167433d6423SLionel Sambuc free_unused_blocks(); 168433d6423SLionel Sambuc if((bp->data = mmap(0, fs_block_size, PROT_READ|PROT_WRITE, 169433d6423SLionel Sambuc MAP_PREALLOC|MAP_ANON, -1, 0)) == MAP_FAILED) { 170433d6423SLionel Sambuc panic("libminixfs: could not allocate block"); 171433d6423SLionel Sambuc } 172433d6423SLionel Sambuc } 173433d6423SLionel Sambuc assert(bp->data); 174433d6423SLionel Sambuc bp->lmfs_bytes = fs_block_size; 175433d6423SLionel Sambuc bp->lmfs_needsetcache = 1; 176433d6423SLionel Sambuc } 177433d6423SLionel Sambuc 178433d6423SLionel Sambuc /*===========================================================================* 179433d6423SLionel Sambuc * lmfs_get_block * 180433d6423SLionel Sambuc *===========================================================================*/ 181433d6423SLionel Sambuc struct buf *lmfs_get_block(register dev_t dev, register block_t block, 182433d6423SLionel Sambuc int only_search) 183433d6423SLionel Sambuc { 184433d6423SLionel Sambuc return lmfs_get_block_ino(dev, block, only_search, VMC_NO_INODE, 0); 185433d6423SLionel Sambuc } 186433d6423SLionel Sambuc 187*65f76edbSDavid van Moolenbroek static void munmap_t(void *a, int len) 188433d6423SLionel Sambuc { 189433d6423SLionel Sambuc vir_bytes av = (vir_bytes) a; 190433d6423SLionel Sambuc assert(a); 191433d6423SLionel Sambuc assert(a != MAP_FAILED); 192433d6423SLionel Sambuc assert(len > 0); 193433d6423SLionel Sambuc assert(!(av % PAGE_SIZE)); 194433d6423SLionel Sambuc 195433d6423SLionel Sambuc len = roundup(len, PAGE_SIZE); 196433d6423SLionel Sambuc 197433d6423SLionel Sambuc assert(!(len % PAGE_SIZE)); 198433d6423SLionel Sambuc 199433d6423SLionel Sambuc if(munmap(a, len) < 0) 200433d6423SLionel Sambuc panic("libminixfs cache: munmap failed"); 201433d6423SLionel Sambuc } 202433d6423SLionel Sambuc 203433d6423SLionel Sambuc static void raisecount(struct buf *bp) 204433d6423SLionel Sambuc { 205433d6423SLionel Sambuc assert(bufs_in_use >= 0); 206433d6423SLionel Sambuc ASSERT(bp->lmfs_count >= 0); 207433d6423SLionel Sambuc bp->lmfs_count++; 208433d6423SLionel Sambuc if(bp->lmfs_count == 1) bufs_in_use++; 209433d6423SLionel Sambuc assert(bufs_in_use > 0); 210433d6423SLionel Sambuc } 211433d6423SLionel Sambuc 212433d6423SLionel Sambuc static void lowercount(struct buf *bp) 213433d6423SLionel Sambuc { 214433d6423SLionel Sambuc assert(bufs_in_use > 0); 215433d6423SLionel Sambuc ASSERT(bp->lmfs_count > 0); 216433d6423SLionel Sambuc bp->lmfs_count--; 217433d6423SLionel Sambuc if(bp->lmfs_count == 0) bufs_in_use--; 218433d6423SLionel Sambuc assert(bufs_in_use >= 0); 219433d6423SLionel Sambuc } 220433d6423SLionel Sambuc 221433d6423SLionel Sambuc static void freeblock(struct buf *bp) 222433d6423SLionel Sambuc { 223433d6423SLionel Sambuc ASSERT(bp->lmfs_count == 0); 224433d6423SLionel Sambuc /* If the block taken is dirty, make it clean by writing it to the disk. 225433d6423SLionel Sambuc * Avoid hysteresis by flushing all other dirty blocks for the same device. 226433d6423SLionel Sambuc */ 227433d6423SLionel Sambuc if (bp->lmfs_dev != NO_DEV) { 228ebd3c067SDavid van Moolenbroek if (!lmfs_isclean(bp)) lmfs_flushdev(bp->lmfs_dev); 229433d6423SLionel Sambuc assert(bp->lmfs_bytes == fs_block_size); 230433d6423SLionel Sambuc bp->lmfs_dev = NO_DEV; 231433d6423SLionel Sambuc } 232433d6423SLionel Sambuc 233433d6423SLionel Sambuc /* Fill in block's parameters and add it to the hash chain where it goes. */ 234433d6423SLionel Sambuc MARKCLEAN(bp); /* NO_DEV blocks may be marked dirty */ 235433d6423SLionel Sambuc if(bp->lmfs_bytes > 0) { 236433d6423SLionel Sambuc assert(bp->data); 237433d6423SLionel Sambuc munmap_t(bp->data, bp->lmfs_bytes); 238433d6423SLionel Sambuc bp->lmfs_bytes = 0; 239433d6423SLionel Sambuc bp->data = NULL; 240433d6423SLionel Sambuc } else assert(!bp->data); 241433d6423SLionel Sambuc } 242433d6423SLionel Sambuc 243433d6423SLionel Sambuc /*===========================================================================* 244433d6423SLionel Sambuc * lmfs_get_block_ino * 245433d6423SLionel Sambuc *===========================================================================*/ 246433d6423SLionel Sambuc struct buf *lmfs_get_block_ino(dev_t dev, block_t block, int only_search, 247433d6423SLionel Sambuc ino_t ino, u64_t ino_off) 248433d6423SLionel Sambuc { 249433d6423SLionel Sambuc /* Check to see if the requested block is in the block cache. If so, return 250433d6423SLionel Sambuc * a pointer to it. If not, evict some other block and fetch it (unless 251433d6423SLionel Sambuc * 'only_search' is 1). All the blocks in the cache that are not in use 252433d6423SLionel Sambuc * are linked together in a chain, with 'front' pointing to the least recently 253433d6423SLionel Sambuc * used block and 'rear' to the most recently used block. If 'only_search' is 254433d6423SLionel Sambuc * 1, the block being requested will be overwritten in its entirety, so it is 255433d6423SLionel Sambuc * only necessary to see if it is in the cache; if it is not, any free buffer 256433d6423SLionel Sambuc * will do. It is not necessary to actually read the block in from disk. 257433d6423SLionel Sambuc * If 'only_search' is PREFETCH, the block need not be read from the disk, 258433d6423SLionel Sambuc * and the device is not to be marked on the block, so callers can tell if 259433d6423SLionel Sambuc * the block returned is valid. 260433d6423SLionel Sambuc * In addition to the LRU chain, there is also a hash chain to link together 261433d6423SLionel Sambuc * blocks whose block numbers end with the same bit strings, for fast lookup. 262433d6423SLionel Sambuc */ 263433d6423SLionel Sambuc 264433d6423SLionel Sambuc int b; 265433d6423SLionel Sambuc static struct buf *bp; 266433d6423SLionel Sambuc u64_t dev_off = (u64_t) block * fs_block_size; 267433d6423SLionel Sambuc struct buf *prev_ptr; 268433d6423SLionel Sambuc 269433d6423SLionel Sambuc assert(buf_hash); 270433d6423SLionel Sambuc assert(buf); 271433d6423SLionel Sambuc assert(nr_bufs > 0); 272433d6423SLionel Sambuc 273433d6423SLionel Sambuc ASSERT(fs_block_size > 0); 274433d6423SLionel Sambuc 275433d6423SLionel Sambuc assert(dev != NO_DEV); 276433d6423SLionel Sambuc 277433d6423SLionel Sambuc if((ino_off % fs_block_size)) { 278433d6423SLionel Sambuc 279433d6423SLionel Sambuc printf("cache: unaligned lmfs_get_block_ino ino_off %llu\n", 280433d6423SLionel Sambuc ino_off); 281433d6423SLionel Sambuc util_stacktrace(); 282433d6423SLionel Sambuc } 283433d6423SLionel Sambuc 284433d6423SLionel Sambuc /* Search the hash chain for (dev, block). */ 285433d6423SLionel Sambuc b = BUFHASH(block); 286433d6423SLionel Sambuc bp = buf_hash[b]; 287433d6423SLionel Sambuc while (bp != NULL) { 288433d6423SLionel Sambuc if (bp->lmfs_blocknr == block && bp->lmfs_dev == dev) { 289433d6423SLionel Sambuc if(bp->lmfs_flags & VMMC_EVICTED) { 290433d6423SLionel Sambuc /* We had it but VM evicted it; invalidate it. */ 291433d6423SLionel Sambuc ASSERT(bp->lmfs_count == 0); 292433d6423SLionel Sambuc ASSERT(!(bp->lmfs_flags & VMMC_BLOCK_LOCKED)); 293433d6423SLionel Sambuc ASSERT(!(bp->lmfs_flags & VMMC_DIRTY)); 294433d6423SLionel Sambuc bp->lmfs_dev = NO_DEV; 295433d6423SLionel Sambuc bp->lmfs_bytes = 0; 296433d6423SLionel Sambuc bp->data = NULL; 297433d6423SLionel Sambuc break; 298433d6423SLionel Sambuc } 299433d6423SLionel Sambuc /* Block needed has been found. */ 300433d6423SLionel Sambuc if (bp->lmfs_count == 0) { 301433d6423SLionel Sambuc rm_lru(bp); 302433d6423SLionel Sambuc ASSERT(bp->lmfs_needsetcache == 0); 303433d6423SLionel Sambuc ASSERT(!(bp->lmfs_flags & VMMC_BLOCK_LOCKED)); 304433d6423SLionel Sambuc bp->lmfs_flags |= VMMC_BLOCK_LOCKED; 305433d6423SLionel Sambuc } 306433d6423SLionel Sambuc raisecount(bp); 307433d6423SLionel Sambuc ASSERT(bp->lmfs_bytes == fs_block_size); 308433d6423SLionel Sambuc ASSERT(bp->lmfs_dev == dev); 309433d6423SLionel Sambuc ASSERT(bp->lmfs_dev != NO_DEV); 310433d6423SLionel Sambuc ASSERT(bp->lmfs_flags & VMMC_BLOCK_LOCKED); 311433d6423SLionel Sambuc ASSERT(bp->data); 312433d6423SLionel Sambuc 313433d6423SLionel Sambuc if(ino != VMC_NO_INODE) { 314433d6423SLionel Sambuc if(bp->lmfs_inode == VMC_NO_INODE 315433d6423SLionel Sambuc || bp->lmfs_inode != ino 316433d6423SLionel Sambuc || bp->lmfs_inode_offset != ino_off) { 317433d6423SLionel Sambuc bp->lmfs_inode = ino; 318433d6423SLionel Sambuc bp->lmfs_inode_offset = ino_off; 319433d6423SLionel Sambuc bp->lmfs_needsetcache = 1; 320433d6423SLionel Sambuc } 321433d6423SLionel Sambuc } 322433d6423SLionel Sambuc 323433d6423SLionel Sambuc return(bp); 324433d6423SLionel Sambuc } else { 325433d6423SLionel Sambuc /* This block is not the one sought. */ 326433d6423SLionel Sambuc bp = bp->lmfs_hash; /* move to next block on hash chain */ 327433d6423SLionel Sambuc } 328433d6423SLionel Sambuc } 329433d6423SLionel Sambuc 330433d6423SLionel Sambuc /* Desired block is not on available chain. Find a free block to use. */ 331433d6423SLionel Sambuc if(bp) { 332433d6423SLionel Sambuc ASSERT(bp->lmfs_flags & VMMC_EVICTED); 333433d6423SLionel Sambuc } else { 334433d6423SLionel Sambuc if ((bp = front) == NULL) panic("all buffers in use: %d", nr_bufs); 335433d6423SLionel Sambuc } 336433d6423SLionel Sambuc assert(bp); 337433d6423SLionel Sambuc 338433d6423SLionel Sambuc rm_lru(bp); 339433d6423SLionel Sambuc 340433d6423SLionel Sambuc /* Remove the block that was just taken from its hash chain. */ 341433d6423SLionel Sambuc b = BUFHASH(bp->lmfs_blocknr); 342433d6423SLionel Sambuc prev_ptr = buf_hash[b]; 343433d6423SLionel Sambuc if (prev_ptr == bp) { 344433d6423SLionel Sambuc buf_hash[b] = bp->lmfs_hash; 345433d6423SLionel Sambuc } else { 346433d6423SLionel Sambuc /* The block just taken is not on the front of its hash chain. */ 347433d6423SLionel Sambuc while (prev_ptr->lmfs_hash != NULL) 348433d6423SLionel Sambuc if (prev_ptr->lmfs_hash == bp) { 349433d6423SLionel Sambuc prev_ptr->lmfs_hash = bp->lmfs_hash; /* found it */ 350433d6423SLionel Sambuc break; 351433d6423SLionel Sambuc } else { 352433d6423SLionel Sambuc prev_ptr = prev_ptr->lmfs_hash; /* keep looking */ 353433d6423SLionel Sambuc } 354433d6423SLionel Sambuc } 355433d6423SLionel Sambuc 356433d6423SLionel Sambuc freeblock(bp); 357433d6423SLionel Sambuc 358433d6423SLionel Sambuc bp->lmfs_inode = ino; 359433d6423SLionel Sambuc bp->lmfs_inode_offset = ino_off; 360433d6423SLionel Sambuc 361433d6423SLionel Sambuc bp->lmfs_flags = VMMC_BLOCK_LOCKED; 362433d6423SLionel Sambuc bp->lmfs_needsetcache = 0; 363433d6423SLionel Sambuc bp->lmfs_dev = dev; /* fill in device number */ 364433d6423SLionel Sambuc bp->lmfs_blocknr = block; /* fill in block number */ 365433d6423SLionel Sambuc ASSERT(bp->lmfs_count == 0); 366433d6423SLionel Sambuc raisecount(bp); 367433d6423SLionel Sambuc b = BUFHASH(bp->lmfs_blocknr); 368433d6423SLionel Sambuc bp->lmfs_hash = buf_hash[b]; 369433d6423SLionel Sambuc 370433d6423SLionel Sambuc buf_hash[b] = bp; /* add to hash list */ 371433d6423SLionel Sambuc 372433d6423SLionel Sambuc assert(dev != NO_DEV); 373433d6423SLionel Sambuc 374433d6423SLionel Sambuc /* Block is not found in our cache, but we do want it 375433d6423SLionel Sambuc * if it's in the vm cache. 376433d6423SLionel Sambuc */ 377433d6423SLionel Sambuc assert(!bp->data); 378433d6423SLionel Sambuc assert(!bp->lmfs_bytes); 379433d6423SLionel Sambuc if(vmcache) { 380433d6423SLionel Sambuc if((bp->data = vm_map_cacheblock(dev, dev_off, ino, ino_off, 381433d6423SLionel Sambuc &bp->lmfs_flags, fs_block_size)) != MAP_FAILED) { 382433d6423SLionel Sambuc bp->lmfs_bytes = fs_block_size; 383433d6423SLionel Sambuc ASSERT(!bp->lmfs_needsetcache); 384433d6423SLionel Sambuc return bp; 385433d6423SLionel Sambuc } 386433d6423SLionel Sambuc } 387433d6423SLionel Sambuc bp->data = NULL; 388433d6423SLionel Sambuc 389433d6423SLionel Sambuc /* Not in the cache; reserve memory for its contents. */ 390433d6423SLionel Sambuc 391433d6423SLionel Sambuc lmfs_alloc_block(bp); 392433d6423SLionel Sambuc 393433d6423SLionel Sambuc assert(bp->data); 394433d6423SLionel Sambuc 395433d6423SLionel Sambuc if(only_search == PREFETCH) { 396433d6423SLionel Sambuc /* PREFETCH: don't do i/o. */ 397433d6423SLionel Sambuc bp->lmfs_dev = NO_DEV; 398433d6423SLionel Sambuc } else if (only_search == NORMAL) { 399433d6423SLionel Sambuc read_block(bp); 400433d6423SLionel Sambuc } else if(only_search == NO_READ) { 401433d6423SLionel Sambuc /* This block will be overwritten by new contents. */ 402433d6423SLionel Sambuc } else 403433d6423SLionel Sambuc panic("unexpected only_search value: %d", only_search); 404433d6423SLionel Sambuc 405433d6423SLionel Sambuc assert(bp->data); 406433d6423SLionel Sambuc 407433d6423SLionel Sambuc return(bp); /* return the newly acquired block */ 408433d6423SLionel Sambuc } 409433d6423SLionel Sambuc 410433d6423SLionel Sambuc /*===========================================================================* 411433d6423SLionel Sambuc * lmfs_put_block * 412433d6423SLionel Sambuc *===========================================================================*/ 413433d6423SLionel Sambuc void lmfs_put_block( 414433d6423SLionel Sambuc struct buf *bp, /* pointer to the buffer to be released */ 415433d6423SLionel Sambuc int block_type /* INODE_BLOCK, DIRECTORY_BLOCK, or whatever */ 416433d6423SLionel Sambuc ) 417433d6423SLionel Sambuc { 418433d6423SLionel Sambuc /* Return a block to the list of available blocks. Depending on 'block_type' 419433d6423SLionel Sambuc * it may be put on the front or rear of the LRU chain. Blocks that are 420433d6423SLionel Sambuc * expected to be needed again shortly (e.g., partially full data blocks) 421433d6423SLionel Sambuc * go on the rear; blocks that are unlikely to be needed again shortly 422433d6423SLionel Sambuc * (e.g., full data blocks) go on the front. Blocks whose loss can hurt 423433d6423SLionel Sambuc * the integrity of the file system (e.g., inode blocks) are written to 424433d6423SLionel Sambuc * disk immediately if they are dirty. 425433d6423SLionel Sambuc */ 426433d6423SLionel Sambuc dev_t dev; 427433d6423SLionel Sambuc off_t dev_off; 428433d6423SLionel Sambuc int r; 429433d6423SLionel Sambuc 430433d6423SLionel Sambuc if (bp == NULL) return; /* it is easier to check here than in caller */ 431433d6423SLionel Sambuc 432433d6423SLionel Sambuc dev = bp->lmfs_dev; 433433d6423SLionel Sambuc 434433d6423SLionel Sambuc dev_off = (off_t) bp->lmfs_blocknr * fs_block_size; 435433d6423SLionel Sambuc 436433d6423SLionel Sambuc lowercount(bp); 437433d6423SLionel Sambuc if (bp->lmfs_count != 0) return; /* block is still in use */ 438433d6423SLionel Sambuc 439433d6423SLionel Sambuc /* Put this block back on the LRU chain. */ 440433d6423SLionel Sambuc if (dev == DEV_RAM || (block_type & ONE_SHOT)) { 441433d6423SLionel Sambuc /* Block probably won't be needed quickly. Put it on front of chain. 442433d6423SLionel Sambuc * It will be the next block to be evicted from the cache. 443433d6423SLionel Sambuc */ 444433d6423SLionel Sambuc bp->lmfs_prev = NULL; 445433d6423SLionel Sambuc bp->lmfs_next = front; 446433d6423SLionel Sambuc if (front == NULL) 447433d6423SLionel Sambuc rear = bp; /* LRU chain was empty */ 448433d6423SLionel Sambuc else 449433d6423SLionel Sambuc front->lmfs_prev = bp; 450433d6423SLionel Sambuc front = bp; 451433d6423SLionel Sambuc } 452433d6423SLionel Sambuc else { 453433d6423SLionel Sambuc /* Block probably will be needed quickly. Put it on rear of chain. 454433d6423SLionel Sambuc * It will not be evicted from the cache for a long time. 455433d6423SLionel Sambuc */ 456433d6423SLionel Sambuc bp->lmfs_prev = rear; 457433d6423SLionel Sambuc bp->lmfs_next = NULL; 458433d6423SLionel Sambuc if (rear == NULL) 459433d6423SLionel Sambuc front = bp; 460433d6423SLionel Sambuc else 461433d6423SLionel Sambuc rear->lmfs_next = bp; 462433d6423SLionel Sambuc rear = bp; 463433d6423SLionel Sambuc } 464433d6423SLionel Sambuc 465433d6423SLionel Sambuc assert(bp->lmfs_flags & VMMC_BLOCK_LOCKED); 466433d6423SLionel Sambuc bp->lmfs_flags &= ~VMMC_BLOCK_LOCKED; 467433d6423SLionel Sambuc 468433d6423SLionel Sambuc /* block has sensible content - if necesary, identify it to VM */ 469433d6423SLionel Sambuc if(vmcache && bp->lmfs_needsetcache && dev != NO_DEV) { 470433d6423SLionel Sambuc if((r=vm_set_cacheblock(bp->data, dev, dev_off, 471433d6423SLionel Sambuc bp->lmfs_inode, bp->lmfs_inode_offset, 472433d6423SLionel Sambuc &bp->lmfs_flags, fs_block_size)) != OK) { 473433d6423SLionel Sambuc if(r == ENOSYS) { 474433d6423SLionel Sambuc printf("libminixfs: ENOSYS, disabling VM calls\n"); 475433d6423SLionel Sambuc vmcache = 0; 476433d6423SLionel Sambuc } else { 477433d6423SLionel Sambuc panic("libminixfs: setblock of %p dev 0x%llx off " 478433d6423SLionel Sambuc "0x%llx failed\n", bp->data, dev, dev_off); 479433d6423SLionel Sambuc } 480433d6423SLionel Sambuc } 481433d6423SLionel Sambuc } 482433d6423SLionel Sambuc bp->lmfs_needsetcache = 0; 483433d6423SLionel Sambuc 484433d6423SLionel Sambuc } 485433d6423SLionel Sambuc 486433d6423SLionel Sambuc void lmfs_cache_reevaluate(dev_t dev) 487433d6423SLionel Sambuc { 488433d6423SLionel Sambuc if(bufs_in_use == 0 && dev != NO_DEV) { 489433d6423SLionel Sambuc /* if the cache isn't in use any more, we could resize it. */ 490433d6423SLionel Sambuc cache_heuristic_check(major(dev)); 491433d6423SLionel Sambuc } 492433d6423SLionel Sambuc } 493433d6423SLionel Sambuc 494433d6423SLionel Sambuc /*===========================================================================* 495433d6423SLionel Sambuc * read_block * 496433d6423SLionel Sambuc *===========================================================================*/ 497433d6423SLionel Sambuc static void read_block( 498433d6423SLionel Sambuc struct buf *bp /* buffer pointer */ 499433d6423SLionel Sambuc ) 500433d6423SLionel Sambuc { 501433d6423SLionel Sambuc /* Read or write a disk block. This is the only routine in which actual disk 502433d6423SLionel Sambuc * I/O is invoked. If an error occurs, a message is printed here, but the error 503433d6423SLionel Sambuc * is not reported to the caller. If the error occurred while purging a block 504433d6423SLionel Sambuc * from the cache, it is not clear what the caller could do about it anyway. 505433d6423SLionel Sambuc */ 506433d6423SLionel Sambuc int r, op_failed; 507433d6423SLionel Sambuc off_t pos; 508433d6423SLionel Sambuc dev_t dev = bp->lmfs_dev; 509433d6423SLionel Sambuc 510433d6423SLionel Sambuc op_failed = 0; 511433d6423SLionel Sambuc 512433d6423SLionel Sambuc assert(dev != NO_DEV); 513433d6423SLionel Sambuc 514433d6423SLionel Sambuc ASSERT(bp->lmfs_bytes == fs_block_size); 515433d6423SLionel Sambuc ASSERT(fs_block_size > 0); 516433d6423SLionel Sambuc 517433d6423SLionel Sambuc pos = (off_t)bp->lmfs_blocknr * fs_block_size; 518433d6423SLionel Sambuc if(fs_block_size > PAGE_SIZE) { 519433d6423SLionel Sambuc #define MAXPAGES 20 520433d6423SLionel Sambuc vir_bytes blockrem, vaddr = (vir_bytes) bp->data; 521433d6423SLionel Sambuc int p = 0; 522433d6423SLionel Sambuc static iovec_t iovec[MAXPAGES]; 523433d6423SLionel Sambuc blockrem = fs_block_size; 524433d6423SLionel Sambuc while(blockrem > 0) { 525433d6423SLionel Sambuc vir_bytes chunk = blockrem >= PAGE_SIZE ? PAGE_SIZE : blockrem; 526433d6423SLionel Sambuc iovec[p].iov_addr = vaddr; 527433d6423SLionel Sambuc iovec[p].iov_size = chunk; 528433d6423SLionel Sambuc vaddr += chunk; 529433d6423SLionel Sambuc blockrem -= chunk; 530433d6423SLionel Sambuc p++; 531433d6423SLionel Sambuc } 532433d6423SLionel Sambuc r = bdev_gather(dev, pos, iovec, p, BDEV_NOFLAGS); 533433d6423SLionel Sambuc } else { 534433d6423SLionel Sambuc r = bdev_read(dev, pos, bp->data, fs_block_size, 535433d6423SLionel Sambuc BDEV_NOFLAGS); 536433d6423SLionel Sambuc } 537433d6423SLionel Sambuc if (r < 0) { 538433d6423SLionel Sambuc printf("fs cache: I/O error on device %d/%d, block %u\n", 539433d6423SLionel Sambuc major(dev), minor(dev), bp->lmfs_blocknr); 540433d6423SLionel Sambuc op_failed = 1; 541433d6423SLionel Sambuc } else if (r != (ssize_t) fs_block_size) { 542433d6423SLionel Sambuc r = END_OF_FILE; 543433d6423SLionel Sambuc op_failed = 1; 544433d6423SLionel Sambuc } 545433d6423SLionel Sambuc 546433d6423SLionel Sambuc if (op_failed) { 547433d6423SLionel Sambuc bp->lmfs_dev = NO_DEV; /* invalidate block */ 548433d6423SLionel Sambuc 549433d6423SLionel Sambuc /* Report read errors to interested parties. */ 550433d6423SLionel Sambuc rdwt_err = r; 551433d6423SLionel Sambuc } 552433d6423SLionel Sambuc 553433d6423SLionel Sambuc } 554433d6423SLionel Sambuc 555433d6423SLionel Sambuc /*===========================================================================* 556433d6423SLionel Sambuc * lmfs_invalidate * 557433d6423SLionel Sambuc *===========================================================================*/ 558433d6423SLionel Sambuc void lmfs_invalidate( 559433d6423SLionel Sambuc dev_t device /* device whose blocks are to be purged */ 560433d6423SLionel Sambuc ) 561433d6423SLionel Sambuc { 562433d6423SLionel Sambuc /* Remove all the blocks belonging to some device from the cache. */ 563433d6423SLionel Sambuc 564433d6423SLionel Sambuc register struct buf *bp; 565433d6423SLionel Sambuc 566433d6423SLionel Sambuc for (bp = &buf[0]; bp < &buf[nr_bufs]; bp++) { 567433d6423SLionel Sambuc if (bp->lmfs_dev == device) { 568433d6423SLionel Sambuc assert(bp->data); 569433d6423SLionel Sambuc assert(bp->lmfs_bytes > 0); 570433d6423SLionel Sambuc munmap_t(bp->data, bp->lmfs_bytes); 571433d6423SLionel Sambuc bp->lmfs_dev = NO_DEV; 572433d6423SLionel Sambuc bp->lmfs_bytes = 0; 573433d6423SLionel Sambuc bp->data = NULL; 574433d6423SLionel Sambuc } 575433d6423SLionel Sambuc } 576433d6423SLionel Sambuc 577433d6423SLionel Sambuc vm_clear_cache(device); 578433d6423SLionel Sambuc } 579433d6423SLionel Sambuc 580433d6423SLionel Sambuc /*===========================================================================* 581ebd3c067SDavid van Moolenbroek * lmfs_flushdev * 582433d6423SLionel Sambuc *===========================================================================*/ 583ebd3c067SDavid van Moolenbroek void lmfs_flushdev(dev_t dev) 584433d6423SLionel Sambuc { 585433d6423SLionel Sambuc /* Flush all dirty blocks for one device. */ 586433d6423SLionel Sambuc 587433d6423SLionel Sambuc register struct buf *bp; 588433d6423SLionel Sambuc static struct buf **dirty; /* static so it isn't on stack */ 589433d6423SLionel Sambuc static unsigned int dirtylistsize = 0; 590433d6423SLionel Sambuc int ndirty; 591433d6423SLionel Sambuc 592433d6423SLionel Sambuc if(dirtylistsize != nr_bufs) { 593433d6423SLionel Sambuc if(dirtylistsize > 0) { 594433d6423SLionel Sambuc assert(dirty != NULL); 595433d6423SLionel Sambuc free(dirty); 596433d6423SLionel Sambuc } 597433d6423SLionel Sambuc if(!(dirty = malloc(sizeof(dirty[0])*nr_bufs))) 598433d6423SLionel Sambuc panic("couldn't allocate dirty buf list"); 599433d6423SLionel Sambuc dirtylistsize = nr_bufs; 600433d6423SLionel Sambuc } 601433d6423SLionel Sambuc 602433d6423SLionel Sambuc for (bp = &buf[0], ndirty = 0; bp < &buf[nr_bufs]; bp++) { 603433d6423SLionel Sambuc if (!lmfs_isclean(bp) && bp->lmfs_dev == dev) { 604433d6423SLionel Sambuc dirty[ndirty++] = bp; 605433d6423SLionel Sambuc } 606433d6423SLionel Sambuc } 607433d6423SLionel Sambuc 608433d6423SLionel Sambuc lmfs_rw_scattered(dev, dirty, ndirty, WRITING); 609433d6423SLionel Sambuc } 610433d6423SLionel Sambuc 611433d6423SLionel Sambuc /*===========================================================================* 612433d6423SLionel Sambuc * lmfs_rw_scattered * 613433d6423SLionel Sambuc *===========================================================================*/ 614433d6423SLionel Sambuc void lmfs_rw_scattered( 615433d6423SLionel Sambuc dev_t dev, /* major-minor device number */ 616433d6423SLionel Sambuc struct buf **bufq, /* pointer to array of buffers */ 617433d6423SLionel Sambuc int bufqsize, /* number of buffers */ 618433d6423SLionel Sambuc int rw_flag /* READING or WRITING */ 619433d6423SLionel Sambuc ) 620433d6423SLionel Sambuc { 621433d6423SLionel Sambuc /* Read or write scattered data from a device. */ 622433d6423SLionel Sambuc 623433d6423SLionel Sambuc register struct buf *bp; 624433d6423SLionel Sambuc int gap; 625433d6423SLionel Sambuc register int i; 626433d6423SLionel Sambuc register iovec_t *iop; 627433d6423SLionel Sambuc static iovec_t iovec[NR_IOREQS]; 628433d6423SLionel Sambuc off_t pos; 629433d6423SLionel Sambuc int iov_per_block; 630*65f76edbSDavid van Moolenbroek unsigned int start_in_use = bufs_in_use, start_bufqsize = bufqsize; 631433d6423SLionel Sambuc 632433d6423SLionel Sambuc assert(bufqsize >= 0); 633433d6423SLionel Sambuc if(bufqsize == 0) return; 634433d6423SLionel Sambuc 635433d6423SLionel Sambuc /* for READING, check all buffers on the list are obtained and held 636433d6423SLionel Sambuc * (count > 0) 637433d6423SLionel Sambuc */ 638433d6423SLionel Sambuc if (rw_flag == READING) { 639433d6423SLionel Sambuc for(i = 0; i < bufqsize; i++) { 640433d6423SLionel Sambuc assert(bufq[i] != NULL); 641433d6423SLionel Sambuc assert(bufq[i]->lmfs_count > 0); 642433d6423SLionel Sambuc } 643433d6423SLionel Sambuc 644433d6423SLionel Sambuc /* therefore they are all 'in use' and must be at least this many */ 645433d6423SLionel Sambuc assert(start_in_use >= start_bufqsize); 646433d6423SLionel Sambuc } 647433d6423SLionel Sambuc 648433d6423SLionel Sambuc assert(dev != NO_DEV); 649433d6423SLionel Sambuc assert(fs_block_size > 0); 650433d6423SLionel Sambuc iov_per_block = roundup(fs_block_size, PAGE_SIZE) / PAGE_SIZE; 651433d6423SLionel Sambuc assert(iov_per_block < NR_IOREQS); 652433d6423SLionel Sambuc 653433d6423SLionel Sambuc /* (Shell) sort buffers on lmfs_blocknr. */ 654433d6423SLionel Sambuc gap = 1; 655433d6423SLionel Sambuc do 656433d6423SLionel Sambuc gap = 3 * gap + 1; 657433d6423SLionel Sambuc while (gap <= bufqsize); 658433d6423SLionel Sambuc while (gap != 1) { 659433d6423SLionel Sambuc int j; 660433d6423SLionel Sambuc gap /= 3; 661433d6423SLionel Sambuc for (j = gap; j < bufqsize; j++) { 662433d6423SLionel Sambuc for (i = j - gap; 663433d6423SLionel Sambuc i >= 0 && bufq[i]->lmfs_blocknr > bufq[i + gap]->lmfs_blocknr; 664433d6423SLionel Sambuc i -= gap) { 665433d6423SLionel Sambuc bp = bufq[i]; 666433d6423SLionel Sambuc bufq[i] = bufq[i + gap]; 667433d6423SLionel Sambuc bufq[i + gap] = bp; 668433d6423SLionel Sambuc } 669433d6423SLionel Sambuc } 670433d6423SLionel Sambuc } 671433d6423SLionel Sambuc 672433d6423SLionel Sambuc /* Set up I/O vector and do I/O. The result of bdev I/O is OK if everything 673433d6423SLionel Sambuc * went fine, otherwise the error code for the first failed transfer. 674433d6423SLionel Sambuc */ 675433d6423SLionel Sambuc while (bufqsize > 0) { 676433d6423SLionel Sambuc int nblocks = 0, niovecs = 0; 677433d6423SLionel Sambuc int r; 678433d6423SLionel Sambuc for (iop = iovec; nblocks < bufqsize; nblocks++) { 679433d6423SLionel Sambuc int p; 680433d6423SLionel Sambuc vir_bytes vdata, blockrem; 681433d6423SLionel Sambuc bp = bufq[nblocks]; 682433d6423SLionel Sambuc if (bp->lmfs_blocknr != (block_t) bufq[0]->lmfs_blocknr + nblocks) 683433d6423SLionel Sambuc break; 684433d6423SLionel Sambuc if(niovecs >= NR_IOREQS-iov_per_block) break; 685433d6423SLionel Sambuc vdata = (vir_bytes) bp->data; 686433d6423SLionel Sambuc blockrem = fs_block_size; 687433d6423SLionel Sambuc for(p = 0; p < iov_per_block; p++) { 688433d6423SLionel Sambuc vir_bytes chunk = blockrem < PAGE_SIZE ? blockrem : PAGE_SIZE; 689433d6423SLionel Sambuc iop->iov_addr = vdata; 690433d6423SLionel Sambuc iop->iov_size = chunk; 691433d6423SLionel Sambuc vdata += PAGE_SIZE; 692433d6423SLionel Sambuc blockrem -= chunk; 693433d6423SLionel Sambuc iop++; 694433d6423SLionel Sambuc niovecs++; 695433d6423SLionel Sambuc } 696433d6423SLionel Sambuc assert(p == iov_per_block); 697433d6423SLionel Sambuc assert(blockrem == 0); 698433d6423SLionel Sambuc } 699433d6423SLionel Sambuc 700433d6423SLionel Sambuc assert(nblocks > 0); 701433d6423SLionel Sambuc assert(niovecs > 0); 702433d6423SLionel Sambuc 703433d6423SLionel Sambuc pos = (off_t)bufq[0]->lmfs_blocknr * fs_block_size; 704433d6423SLionel Sambuc if (rw_flag == READING) 705433d6423SLionel Sambuc r = bdev_gather(dev, pos, iovec, niovecs, BDEV_NOFLAGS); 706433d6423SLionel Sambuc else 707433d6423SLionel Sambuc r = bdev_scatter(dev, pos, iovec, niovecs, BDEV_NOFLAGS); 708433d6423SLionel Sambuc 709433d6423SLionel Sambuc /* Harvest the results. The driver may have returned an error, or it 710433d6423SLionel Sambuc * may have done less than what we asked for. 711433d6423SLionel Sambuc */ 712433d6423SLionel Sambuc if (r < 0) { 713433d6423SLionel Sambuc printf("fs cache: I/O error %d on device %d/%d, block %u\n", 714433d6423SLionel Sambuc r, major(dev), minor(dev), bufq[0]->lmfs_blocknr); 715433d6423SLionel Sambuc } 716433d6423SLionel Sambuc for (i = 0; i < nblocks; i++) { 717433d6423SLionel Sambuc bp = bufq[i]; 718433d6423SLionel Sambuc if (r < (ssize_t) fs_block_size) { 719433d6423SLionel Sambuc /* Transfer failed. */ 720433d6423SLionel Sambuc if (i == 0) { 721433d6423SLionel Sambuc bp->lmfs_dev = NO_DEV; /* Invalidate block */ 722433d6423SLionel Sambuc } 723433d6423SLionel Sambuc break; 724433d6423SLionel Sambuc } 725433d6423SLionel Sambuc if (rw_flag == READING) { 726433d6423SLionel Sambuc bp->lmfs_dev = dev; /* validate block */ 727433d6423SLionel Sambuc lmfs_put_block(bp, PARTIAL_DATA_BLOCK); 728433d6423SLionel Sambuc } else { 729433d6423SLionel Sambuc MARKCLEAN(bp); 730433d6423SLionel Sambuc } 731433d6423SLionel Sambuc r -= fs_block_size; 732433d6423SLionel Sambuc } 733433d6423SLionel Sambuc 734433d6423SLionel Sambuc bufq += i; 735433d6423SLionel Sambuc bufqsize -= i; 736433d6423SLionel Sambuc 737433d6423SLionel Sambuc if (rw_flag == READING) { 738433d6423SLionel Sambuc /* Don't bother reading more than the device is willing to 739433d6423SLionel Sambuc * give at this time. Don't forget to release those extras. 740433d6423SLionel Sambuc */ 741433d6423SLionel Sambuc while (bufqsize > 0) { 742433d6423SLionel Sambuc lmfs_put_block(*bufq++, PARTIAL_DATA_BLOCK); 743433d6423SLionel Sambuc bufqsize--; 744433d6423SLionel Sambuc } 745433d6423SLionel Sambuc } 746433d6423SLionel Sambuc if (rw_flag == WRITING && i == 0) { 747433d6423SLionel Sambuc /* We're not making progress, this means we might keep 748433d6423SLionel Sambuc * looping. Buffers remain dirty if un-written. Buffers are 749433d6423SLionel Sambuc * lost if invalidate()d or LRU-removed while dirty. This 750433d6423SLionel Sambuc * is better than keeping unwritable blocks around forever.. 751433d6423SLionel Sambuc */ 752433d6423SLionel Sambuc break; 753433d6423SLionel Sambuc } 754433d6423SLionel Sambuc } 755433d6423SLionel Sambuc 756433d6423SLionel Sambuc if(rw_flag == READING) { 757433d6423SLionel Sambuc assert(start_in_use >= start_bufqsize); 758433d6423SLionel Sambuc 759433d6423SLionel Sambuc /* READING callers assume all bufs are released. */ 760433d6423SLionel Sambuc assert(start_in_use - start_bufqsize == bufs_in_use); 761433d6423SLionel Sambuc } 762433d6423SLionel Sambuc } 763433d6423SLionel Sambuc 764433d6423SLionel Sambuc /*===========================================================================* 765433d6423SLionel Sambuc * rm_lru * 766433d6423SLionel Sambuc *===========================================================================*/ 767433d6423SLionel Sambuc static void rm_lru(struct buf *bp) 768433d6423SLionel Sambuc { 769433d6423SLionel Sambuc /* Remove a block from its LRU chain. */ 770433d6423SLionel Sambuc struct buf *next_ptr, *prev_ptr; 771433d6423SLionel Sambuc 772433d6423SLionel Sambuc next_ptr = bp->lmfs_next; /* successor on LRU chain */ 773433d6423SLionel Sambuc prev_ptr = bp->lmfs_prev; /* predecessor on LRU chain */ 774433d6423SLionel Sambuc if (prev_ptr != NULL) 775433d6423SLionel Sambuc prev_ptr->lmfs_next = next_ptr; 776433d6423SLionel Sambuc else 777433d6423SLionel Sambuc front = next_ptr; /* this block was at front of chain */ 778433d6423SLionel Sambuc 779433d6423SLionel Sambuc if (next_ptr != NULL) 780433d6423SLionel Sambuc next_ptr->lmfs_prev = prev_ptr; 781433d6423SLionel Sambuc else 782433d6423SLionel Sambuc rear = prev_ptr; /* this block was at rear of chain */ 783433d6423SLionel Sambuc } 784433d6423SLionel Sambuc 785433d6423SLionel Sambuc /*===========================================================================* 786433d6423SLionel Sambuc * cache_resize * 787433d6423SLionel Sambuc *===========================================================================*/ 788433d6423SLionel Sambuc static void cache_resize(unsigned int blocksize, unsigned int bufs) 789433d6423SLionel Sambuc { 790433d6423SLionel Sambuc struct buf *bp; 791433d6423SLionel Sambuc 792433d6423SLionel Sambuc assert(blocksize > 0); 793433d6423SLionel Sambuc assert(bufs >= MINBUFS); 794433d6423SLionel Sambuc 795433d6423SLionel Sambuc for (bp = &buf[0]; bp < &buf[nr_bufs]; bp++) 796433d6423SLionel Sambuc if(bp->lmfs_count != 0) panic("change blocksize with buffer in use"); 797433d6423SLionel Sambuc 798433d6423SLionel Sambuc lmfs_buf_pool(bufs); 799433d6423SLionel Sambuc 800433d6423SLionel Sambuc fs_block_size = blocksize; 801433d6423SLionel Sambuc } 802433d6423SLionel Sambuc 803433d6423SLionel Sambuc static void cache_heuristic_check(int major) 804433d6423SLionel Sambuc { 805433d6423SLionel Sambuc int bufs, d; 806433d6423SLionel Sambuc u64_t btotal, bfree, bused; 807433d6423SLionel Sambuc 808433d6423SLionel Sambuc fs_blockstats(&btotal, &bfree, &bused); 809433d6423SLionel Sambuc 810433d6423SLionel Sambuc bufs = fs_bufs_heuristic(10, btotal, bfree, 811433d6423SLionel Sambuc fs_block_size, major); 812433d6423SLionel Sambuc 813433d6423SLionel Sambuc /* set the cache to the new heuristic size if the new one 814433d6423SLionel Sambuc * is more than 10% off from the current one. 815433d6423SLionel Sambuc */ 816433d6423SLionel Sambuc d = bufs-nr_bufs; 817433d6423SLionel Sambuc if(d < 0) d = -d; 818433d6423SLionel Sambuc if(d*100/nr_bufs > 10) { 819433d6423SLionel Sambuc cache_resize(fs_block_size, bufs); 820433d6423SLionel Sambuc } 821433d6423SLionel Sambuc } 822433d6423SLionel Sambuc 823433d6423SLionel Sambuc /*===========================================================================* 824433d6423SLionel Sambuc * lmfs_set_blocksize * 825433d6423SLionel Sambuc *===========================================================================*/ 826433d6423SLionel Sambuc void lmfs_set_blocksize(int new_block_size, int major) 827433d6423SLionel Sambuc { 828433d6423SLionel Sambuc cache_resize(new_block_size, MINBUFS); 829433d6423SLionel Sambuc cache_heuristic_check(major); 830433d6423SLionel Sambuc 831433d6423SLionel Sambuc /* Decide whether to use seconday cache or not. 832433d6423SLionel Sambuc * Only do this if 833433d6423SLionel Sambuc * - it's available, and 834433d6423SLionel Sambuc * - use of it hasn't been disabled for this fs, and 835433d6423SLionel Sambuc * - our main FS device isn't a memory device 836433d6423SLionel Sambuc */ 837433d6423SLionel Sambuc 838433d6423SLionel Sambuc vmcache = 0; 839433d6423SLionel Sambuc 840433d6423SLionel Sambuc if(may_use_vmcache && !(new_block_size % PAGE_SIZE)) 841433d6423SLionel Sambuc vmcache = 1; 842433d6423SLionel Sambuc } 843433d6423SLionel Sambuc 844433d6423SLionel Sambuc /*===========================================================================* 845433d6423SLionel Sambuc * lmfs_buf_pool * 846433d6423SLionel Sambuc *===========================================================================*/ 847433d6423SLionel Sambuc void lmfs_buf_pool(int new_nr_bufs) 848433d6423SLionel Sambuc { 849433d6423SLionel Sambuc /* Initialize the buffer pool. */ 850433d6423SLionel Sambuc register struct buf *bp; 851433d6423SLionel Sambuc 852433d6423SLionel Sambuc assert(new_nr_bufs >= MINBUFS); 853433d6423SLionel Sambuc 854433d6423SLionel Sambuc if(nr_bufs > 0) { 855433d6423SLionel Sambuc assert(buf); 856c5beebb6SDavid van Moolenbroek lmfs_flushall(); 857433d6423SLionel Sambuc for (bp = &buf[0]; bp < &buf[nr_bufs]; bp++) { 858433d6423SLionel Sambuc if(bp->data) { 859433d6423SLionel Sambuc assert(bp->lmfs_bytes > 0); 860433d6423SLionel Sambuc munmap_t(bp->data, bp->lmfs_bytes); 861433d6423SLionel Sambuc } 862433d6423SLionel Sambuc } 863433d6423SLionel Sambuc } 864433d6423SLionel Sambuc 865433d6423SLionel Sambuc if(buf) 866433d6423SLionel Sambuc free(buf); 867433d6423SLionel Sambuc 868433d6423SLionel Sambuc if(!(buf = calloc(sizeof(buf[0]), new_nr_bufs))) 869433d6423SLionel Sambuc panic("couldn't allocate buf list (%d)", new_nr_bufs); 870433d6423SLionel Sambuc 871433d6423SLionel Sambuc if(buf_hash) 872433d6423SLionel Sambuc free(buf_hash); 873433d6423SLionel Sambuc if(!(buf_hash = calloc(sizeof(buf_hash[0]), new_nr_bufs))) 874433d6423SLionel Sambuc panic("couldn't allocate buf hash list (%d)", new_nr_bufs); 875433d6423SLionel Sambuc 876433d6423SLionel Sambuc nr_bufs = new_nr_bufs; 877433d6423SLionel Sambuc 878433d6423SLionel Sambuc bufs_in_use = 0; 879433d6423SLionel Sambuc front = &buf[0]; 880433d6423SLionel Sambuc rear = &buf[nr_bufs - 1]; 881433d6423SLionel Sambuc 882433d6423SLionel Sambuc for (bp = &buf[0]; bp < &buf[nr_bufs]; bp++) { 883433d6423SLionel Sambuc bp->lmfs_blocknr = NO_BLOCK; 884433d6423SLionel Sambuc bp->lmfs_dev = NO_DEV; 885433d6423SLionel Sambuc bp->lmfs_next = bp + 1; 886433d6423SLionel Sambuc bp->lmfs_prev = bp - 1; 887433d6423SLionel Sambuc bp->data = NULL; 888433d6423SLionel Sambuc bp->lmfs_bytes = 0; 889433d6423SLionel Sambuc } 890433d6423SLionel Sambuc front->lmfs_prev = NULL; 891433d6423SLionel Sambuc rear->lmfs_next = NULL; 892433d6423SLionel Sambuc 893433d6423SLionel Sambuc for (bp = &buf[0]; bp < &buf[nr_bufs]; bp++) bp->lmfs_hash = bp->lmfs_next; 894433d6423SLionel Sambuc buf_hash[0] = front; 895433d6423SLionel Sambuc } 896433d6423SLionel Sambuc 897433d6423SLionel Sambuc int lmfs_bufs_in_use(void) 898433d6423SLionel Sambuc { 899433d6423SLionel Sambuc return bufs_in_use; 900433d6423SLionel Sambuc } 901433d6423SLionel Sambuc 902433d6423SLionel Sambuc int lmfs_nr_bufs(void) 903433d6423SLionel Sambuc { 904433d6423SLionel Sambuc return nr_bufs; 905433d6423SLionel Sambuc } 906433d6423SLionel Sambuc 907433d6423SLionel Sambuc void lmfs_flushall(void) 908433d6423SLionel Sambuc { 909433d6423SLionel Sambuc struct buf *bp; 910433d6423SLionel Sambuc for(bp = &buf[0]; bp < &buf[nr_bufs]; bp++) 911433d6423SLionel Sambuc if(bp->lmfs_dev != NO_DEV && !lmfs_isclean(bp)) 912ebd3c067SDavid van Moolenbroek lmfs_flushdev(bp->lmfs_dev); 913433d6423SLionel Sambuc } 914433d6423SLionel Sambuc 915433d6423SLionel Sambuc int lmfs_fs_block_size(void) 916433d6423SLionel Sambuc { 917433d6423SLionel Sambuc return fs_block_size; 918433d6423SLionel Sambuc } 919433d6423SLionel Sambuc 920433d6423SLionel Sambuc void lmfs_may_use_vmcache(int ok) 921433d6423SLionel Sambuc { 922433d6423SLionel Sambuc may_use_vmcache = ok; 923433d6423SLionel Sambuc } 924433d6423SLionel Sambuc 925433d6423SLionel Sambuc void lmfs_reset_rdwt_err(void) 926433d6423SLionel Sambuc { 927433d6423SLionel Sambuc rdwt_err = OK; 928433d6423SLionel Sambuc } 929433d6423SLionel Sambuc 930433d6423SLionel Sambuc int lmfs_rdwt_err(void) 931433d6423SLionel Sambuc { 932433d6423SLionel Sambuc return rdwt_err; 933433d6423SLionel Sambuc } 934