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