1433d6423SLionel Sambuc 2433d6423SLionel Sambuc #define _SYSTEM 3433d6423SLionel Sambuc 4433d6423SLionel Sambuc #include <assert.h> 5*6c46a77dSDavid van Moolenbroek #include <string.h> 6433d6423SLionel Sambuc #include <errno.h> 7433d6423SLionel Sambuc #include <math.h> 8433d6423SLionel Sambuc #include <stdlib.h> 9433d6423SLionel Sambuc 10433d6423SLionel Sambuc #include <machine/vmparam.h> 11433d6423SLionel Sambuc 12433d6423SLionel Sambuc #include <sys/param.h> 13433d6423SLionel Sambuc #include <sys/mman.h> 14433d6423SLionel Sambuc 15433d6423SLionel Sambuc #include <minix/dmap.h> 16433d6423SLionel Sambuc #include <minix/libminixfs.h> 17433d6423SLionel Sambuc #include <minix/syslib.h> 18433d6423SLionel Sambuc #include <minix/sysutil.h> 19433d6423SLionel Sambuc #include <minix/u64.h> 20433d6423SLionel Sambuc #include <minix/bdev.h> 21433d6423SLionel Sambuc 22*6c46a77dSDavid van Moolenbroek #include "inc.h" 23*6c46a77dSDavid van Moolenbroek 240314acfbSDavid van Moolenbroek /* Buffer (block) cache. To acquire a block, a routine calls lmfs_get_block(), 250314acfbSDavid van Moolenbroek * telling which block it wants. The block is then regarded as "in use" and 260314acfbSDavid van Moolenbroek * has its reference count incremented. All the blocks that are not in use are 270314acfbSDavid van Moolenbroek * chained together in an LRU list, with 'front' pointing to the least recently 280314acfbSDavid van Moolenbroek * used block, and 'rear' to the most recently used block. A reverse chain is 290314acfbSDavid van Moolenbroek * also maintained. Usage for LRU is measured by the time the put_block() is 300314acfbSDavid van Moolenbroek * done. The second parameter to put_block() can violate the LRU order and put 310314acfbSDavid van Moolenbroek * a block on the front of the list, if it will probably not be needed again. 320314acfbSDavid van Moolenbroek * This is used internally only; the lmfs_put_block() API call has no second 330314acfbSDavid van Moolenbroek * parameter. If a block is modified, the modifying routine must mark the 340314acfbSDavid van Moolenbroek * block as dirty, so the block will eventually be rewritten to the disk. 350314acfbSDavid van Moolenbroek */ 360314acfbSDavid van Moolenbroek 370314acfbSDavid van Moolenbroek /* Flags to put_block(). */ 380314acfbSDavid van Moolenbroek #define ONE_SHOT 0x1 /* set if block will not be needed again */ 390314acfbSDavid van Moolenbroek 40b65ad59eSDavid van Moolenbroek #define BUFHASH(b) ((unsigned int)((b) % nr_bufs)) 41433d6423SLionel Sambuc #define MARKCLEAN lmfs_markclean 42433d6423SLionel Sambuc 43433d6423SLionel Sambuc #define MINBUFS 6 /* minimal no of bufs for sanity check */ 44433d6423SLionel Sambuc 45433d6423SLionel Sambuc static struct buf *front; /* points to least recently used free block */ 46433d6423SLionel Sambuc static struct buf *rear; /* points to most recently used free block */ 47433d6423SLionel Sambuc static unsigned int bufs_in_use;/* # bufs currently in use (not on free list)*/ 48433d6423SLionel Sambuc 49433d6423SLionel Sambuc static void rm_lru(struct buf *bp); 50*6c46a77dSDavid van Moolenbroek static int read_block(struct buf *bp, size_t size); 51433d6423SLionel Sambuc static void freeblock(struct buf *bp); 520314acfbSDavid van Moolenbroek static void cache_heuristic_check(void); 530314acfbSDavid van Moolenbroek static void put_block(struct buf *bp, int put_flags); 54433d6423SLionel Sambuc 55433d6423SLionel Sambuc static int vmcache = 0; /* are we using vm's secondary cache? (initially not) */ 56433d6423SLionel Sambuc 57433d6423SLionel Sambuc static struct buf *buf; 58433d6423SLionel Sambuc static struct buf **buf_hash; /* the buffer hash table */ 59433d6423SLionel Sambuc static unsigned int nr_bufs; 60433d6423SLionel Sambuc static int may_use_vmcache; 61433d6423SLionel Sambuc 6265f76edbSDavid van Moolenbroek static size_t fs_block_size = PAGE_SIZE; /* raw i/o block size */ 63433d6423SLionel Sambuc 641311233cSDavid van Moolenbroek static fsblkcnt_t fs_btotal = 0, fs_bused = 0; 651311233cSDavid van Moolenbroek 66433d6423SLionel Sambuc static int quiet = 0; 67433d6423SLionel Sambuc 68433d6423SLionel Sambuc void lmfs_setquiet(int q) { quiet = q; } 69433d6423SLionel Sambuc 701311233cSDavid van Moolenbroek static int fs_bufs_heuristic(int minbufs, fsblkcnt_t btotal, 711311233cSDavid van Moolenbroek fsblkcnt_t bused, int blocksize) 72433d6423SLionel Sambuc { 73433d6423SLionel Sambuc struct vm_stats_info vsi; 74433d6423SLionel Sambuc int bufs; 75433d6423SLionel Sambuc u32_t kbytes_used_fs, kbytes_total_fs, kbcache, kb_fsmax; 76433d6423SLionel Sambuc u32_t kbytes_remain_mem; 77433d6423SLionel Sambuc 78433d6423SLionel Sambuc /* set a reasonable cache size; cache at most a certain 79433d6423SLionel Sambuc * portion of the used FS, and at most a certain %age of remaining 80433d6423SLionel Sambuc * memory 81433d6423SLionel Sambuc */ 82433d6423SLionel Sambuc if(vm_info_stats(&vsi) != OK) { 83433d6423SLionel Sambuc bufs = 1024; 84433d6423SLionel Sambuc if(!quiet) 85433d6423SLionel Sambuc printf("fslib: heuristic info fail: default to %d bufs\n", bufs); 86433d6423SLionel Sambuc return bufs; 87433d6423SLionel Sambuc } 88433d6423SLionel Sambuc 89433d6423SLionel Sambuc /* remaining free memory is unused memory plus memory in used for cache, 90433d6423SLionel Sambuc * as the cache can be evicted 91433d6423SLionel Sambuc */ 92433d6423SLionel Sambuc kbytes_remain_mem = (u64_t)(vsi.vsi_free + vsi.vsi_cached) * 93433d6423SLionel Sambuc vsi.vsi_pagesize / 1024; 94433d6423SLionel Sambuc 95433d6423SLionel Sambuc /* check fs usage. */ 96433d6423SLionel Sambuc kbytes_used_fs = (unsigned long)(((u64_t)bused * blocksize) / 1024); 97433d6423SLionel Sambuc kbytes_total_fs = (unsigned long)(((u64_t)btotal * blocksize) / 1024); 98433d6423SLionel Sambuc 99433d6423SLionel Sambuc /* heuristic for a desired cache size based on FS usage; 100433d6423SLionel Sambuc * but never bigger than half of the total filesystem 101433d6423SLionel Sambuc */ 102433d6423SLionel Sambuc kb_fsmax = sqrt_approx(kbytes_used_fs)*40; 103433d6423SLionel Sambuc kb_fsmax = MIN(kb_fsmax, kbytes_total_fs/2); 104433d6423SLionel Sambuc 105433d6423SLionel Sambuc /* heuristic for a maximum usage - 10% of remaining memory */ 106433d6423SLionel Sambuc kbcache = MIN(kbytes_remain_mem/10, kb_fsmax); 107433d6423SLionel Sambuc bufs = kbcache * 1024 / blocksize; 108433d6423SLionel Sambuc 109433d6423SLionel Sambuc /* but we simply need MINBUFS no matter what */ 110433d6423SLionel Sambuc if(bufs < minbufs) 111433d6423SLionel Sambuc bufs = minbufs; 112433d6423SLionel Sambuc 113433d6423SLionel Sambuc return bufs; 114433d6423SLionel Sambuc } 115433d6423SLionel Sambuc 1161311233cSDavid van Moolenbroek void lmfs_change_blockusage(int delta) 117433d6423SLionel Sambuc { 118433d6423SLionel Sambuc /* Change the number of allocated blocks by 'delta.' 119433d6423SLionel Sambuc * Also accumulate the delta since the last cache re-evaluation. 120433d6423SLionel Sambuc * If it is outside a certain band, ask the cache library to 121433d6423SLionel Sambuc * re-evaluate the cache size. 122433d6423SLionel Sambuc */ 1231311233cSDavid van Moolenbroek static int bitdelta = 0, warn_low = TRUE, warn_high = TRUE; 1241311233cSDavid van Moolenbroek 1251311233cSDavid van Moolenbroek /* Adjust the file system block usage counter accordingly. Do bounds 1261311233cSDavid van Moolenbroek * checking, and report file system misbehavior. 1271311233cSDavid van Moolenbroek */ 1281311233cSDavid van Moolenbroek if (delta > 0 && (fsblkcnt_t)delta > fs_btotal - fs_bused) { 1291311233cSDavid van Moolenbroek if (warn_high) { 1301311233cSDavid van Moolenbroek printf("libminixfs: block usage overflow\n"); 1311311233cSDavid van Moolenbroek warn_high = FALSE; 1321311233cSDavid van Moolenbroek } 1331311233cSDavid van Moolenbroek delta = (int)(fs_btotal - fs_bused); 1341311233cSDavid van Moolenbroek } else if (delta < 0 && (fsblkcnt_t)-delta > fs_bused) { 1351311233cSDavid van Moolenbroek if (warn_low) { 1361311233cSDavid van Moolenbroek printf("libminixfs: block usage underflow\n"); 1371311233cSDavid van Moolenbroek warn_low = FALSE; 1381311233cSDavid van Moolenbroek } 1391311233cSDavid van Moolenbroek delta = -(int)fs_bused; 1401311233cSDavid van Moolenbroek } 1411311233cSDavid van Moolenbroek fs_bused += delta; 1421311233cSDavid van Moolenbroek 143433d6423SLionel Sambuc bitdelta += delta; 1441311233cSDavid van Moolenbroek 1451311233cSDavid van Moolenbroek #define BAND_KB (10*1024) /* recheck cache every 10MB change */ 1461311233cSDavid van Moolenbroek 1471311233cSDavid van Moolenbroek /* If the accumulated delta exceeds the configured threshold, resize 1481311233cSDavid van Moolenbroek * the cache, but only if the cache isn't in use any more. In order to 1491311233cSDavid van Moolenbroek * avoid that the latter case blocks a resize forever, we also call 1501311233cSDavid van Moolenbroek * this function from lmfs_flushall(). Since lmfs_buf_pool() may call 1511311233cSDavid van Moolenbroek * lmfs_flushall(), reset 'bitdelta' before doing the heuristics check. 1521311233cSDavid van Moolenbroek */ 1531311233cSDavid van Moolenbroek if (bufs_in_use == 0 && 1541311233cSDavid van Moolenbroek (bitdelta*(int)fs_block_size/1024 > BAND_KB || 1551311233cSDavid van Moolenbroek bitdelta*(int)fs_block_size/1024 < -BAND_KB)) { 156433d6423SLionel Sambuc bitdelta = 0; 1571311233cSDavid van Moolenbroek cache_heuristic_check(); 158433d6423SLionel Sambuc } 159433d6423SLionel Sambuc } 160433d6423SLionel Sambuc 161433d6423SLionel Sambuc void lmfs_markdirty(struct buf *bp) 162433d6423SLionel Sambuc { 163433d6423SLionel Sambuc bp->lmfs_flags |= VMMC_DIRTY; 164433d6423SLionel Sambuc } 165433d6423SLionel Sambuc 166433d6423SLionel Sambuc void lmfs_markclean(struct buf *bp) 167433d6423SLionel Sambuc { 168433d6423SLionel Sambuc bp->lmfs_flags &= ~VMMC_DIRTY; 169433d6423SLionel Sambuc } 170433d6423SLionel Sambuc 171433d6423SLionel Sambuc int lmfs_isclean(struct buf *bp) 172433d6423SLionel Sambuc { 173433d6423SLionel Sambuc return !(bp->lmfs_flags & VMMC_DIRTY); 174433d6423SLionel Sambuc } 175433d6423SLionel Sambuc 176433d6423SLionel Sambuc dev_t lmfs_dev(struct buf *bp) 177433d6423SLionel Sambuc { 178433d6423SLionel Sambuc return bp->lmfs_dev; 179433d6423SLionel Sambuc } 180433d6423SLionel Sambuc 181433d6423SLionel Sambuc static void free_unused_blocks(void) 182433d6423SLionel Sambuc { 183433d6423SLionel Sambuc struct buf *bp; 184433d6423SLionel Sambuc 185433d6423SLionel Sambuc int freed = 0, bytes = 0; 186433d6423SLionel Sambuc printf("libminixfs: freeing; %d blocks in use\n", bufs_in_use); 187433d6423SLionel Sambuc for(bp = &buf[0]; bp < &buf[nr_bufs]; bp++) { 188433d6423SLionel Sambuc if(bp->lmfs_bytes > 0 && bp->lmfs_count == 0) { 189433d6423SLionel Sambuc freed++; 190433d6423SLionel Sambuc bytes += bp->lmfs_bytes; 191433d6423SLionel Sambuc freeblock(bp); 192433d6423SLionel Sambuc } 193433d6423SLionel Sambuc } 194433d6423SLionel Sambuc printf("libminixfs: freeing; %d blocks, %d bytes\n", freed, bytes); 195433d6423SLionel Sambuc } 196433d6423SLionel Sambuc 197*6c46a77dSDavid van Moolenbroek static void lmfs_alloc_block(struct buf *bp, size_t block_size) 198433d6423SLionel Sambuc { 199433d6423SLionel Sambuc int len; 200433d6423SLionel Sambuc ASSERT(!bp->data); 201433d6423SLionel Sambuc ASSERT(bp->lmfs_bytes == 0); 202433d6423SLionel Sambuc 203*6c46a77dSDavid van Moolenbroek len = roundup(block_size, PAGE_SIZE); 204433d6423SLionel Sambuc 205*6c46a77dSDavid van Moolenbroek if((bp->data = mmap(0, block_size, PROT_READ|PROT_WRITE, 206*6c46a77dSDavid van Moolenbroek MAP_PREALLOC|MAP_ANON, -1, 0)) == MAP_FAILED) { 207433d6423SLionel Sambuc free_unused_blocks(); 208*6c46a77dSDavid van Moolenbroek if((bp->data = mmap(0, block_size, PROT_READ|PROT_WRITE, 209433d6423SLionel Sambuc MAP_PREALLOC|MAP_ANON, -1, 0)) == MAP_FAILED) { 210433d6423SLionel Sambuc panic("libminixfs: could not allocate block"); 211433d6423SLionel Sambuc } 212433d6423SLionel Sambuc } 213433d6423SLionel Sambuc assert(bp->data); 214*6c46a77dSDavid van Moolenbroek bp->lmfs_bytes = block_size; 215433d6423SLionel Sambuc bp->lmfs_needsetcache = 1; 216433d6423SLionel Sambuc } 217433d6423SLionel Sambuc 218433d6423SLionel Sambuc /*===========================================================================* 219433d6423SLionel Sambuc * lmfs_get_block * 220433d6423SLionel Sambuc *===========================================================================*/ 221*6c46a77dSDavid van Moolenbroek int lmfs_get_block(struct buf **bpp, dev_t dev, block64_t block, int how) 222433d6423SLionel Sambuc { 223*6c46a77dSDavid van Moolenbroek return lmfs_get_block_ino(bpp, dev, block, how, VMC_NO_INODE, 0); 224433d6423SLionel Sambuc } 225433d6423SLionel Sambuc 22665f76edbSDavid van Moolenbroek static void munmap_t(void *a, int len) 227433d6423SLionel Sambuc { 228433d6423SLionel Sambuc vir_bytes av = (vir_bytes) a; 229433d6423SLionel Sambuc assert(a); 230433d6423SLionel Sambuc assert(a != MAP_FAILED); 231433d6423SLionel Sambuc assert(len > 0); 232433d6423SLionel Sambuc assert(!(av % PAGE_SIZE)); 233433d6423SLionel Sambuc 234433d6423SLionel Sambuc len = roundup(len, PAGE_SIZE); 235433d6423SLionel Sambuc 236433d6423SLionel Sambuc assert(!(len % PAGE_SIZE)); 237433d6423SLionel Sambuc 238433d6423SLionel Sambuc if(munmap(a, len) < 0) 239433d6423SLionel Sambuc panic("libminixfs cache: munmap failed"); 240433d6423SLionel Sambuc } 241433d6423SLionel Sambuc 242433d6423SLionel Sambuc static void raisecount(struct buf *bp) 243433d6423SLionel Sambuc { 244433d6423SLionel Sambuc assert(bufs_in_use >= 0); 245433d6423SLionel Sambuc ASSERT(bp->lmfs_count >= 0); 246433d6423SLionel Sambuc bp->lmfs_count++; 247433d6423SLionel Sambuc if(bp->lmfs_count == 1) bufs_in_use++; 248433d6423SLionel Sambuc assert(bufs_in_use > 0); 249433d6423SLionel Sambuc } 250433d6423SLionel Sambuc 251433d6423SLionel Sambuc static void lowercount(struct buf *bp) 252433d6423SLionel Sambuc { 253433d6423SLionel Sambuc assert(bufs_in_use > 0); 254433d6423SLionel Sambuc ASSERT(bp->lmfs_count > 0); 255433d6423SLionel Sambuc bp->lmfs_count--; 256433d6423SLionel Sambuc if(bp->lmfs_count == 0) bufs_in_use--; 257433d6423SLionel Sambuc assert(bufs_in_use >= 0); 258433d6423SLionel Sambuc } 259433d6423SLionel Sambuc 260433d6423SLionel Sambuc static void freeblock(struct buf *bp) 261433d6423SLionel Sambuc { 262433d6423SLionel Sambuc ASSERT(bp->lmfs_count == 0); 263433d6423SLionel Sambuc /* If the block taken is dirty, make it clean by writing it to the disk. 264433d6423SLionel Sambuc * Avoid hysteresis by flushing all other dirty blocks for the same device. 265433d6423SLionel Sambuc */ 266433d6423SLionel Sambuc if (bp->lmfs_dev != NO_DEV) { 267ebd3c067SDavid van Moolenbroek if (!lmfs_isclean(bp)) lmfs_flushdev(bp->lmfs_dev); 268*6c46a77dSDavid van Moolenbroek assert(bp->lmfs_bytes > 0); 269433d6423SLionel Sambuc bp->lmfs_dev = NO_DEV; 270433d6423SLionel Sambuc } 271433d6423SLionel Sambuc 272433d6423SLionel Sambuc /* Fill in block's parameters and add it to the hash chain where it goes. */ 273433d6423SLionel Sambuc MARKCLEAN(bp); /* NO_DEV blocks may be marked dirty */ 274433d6423SLionel Sambuc if(bp->lmfs_bytes > 0) { 275433d6423SLionel Sambuc assert(bp->data); 276433d6423SLionel Sambuc munmap_t(bp->data, bp->lmfs_bytes); 277433d6423SLionel Sambuc bp->lmfs_bytes = 0; 278433d6423SLionel Sambuc bp->data = NULL; 279433d6423SLionel Sambuc } else assert(!bp->data); 280433d6423SLionel Sambuc } 281433d6423SLionel Sambuc 282433d6423SLionel Sambuc /*===========================================================================* 283e94f856bSDavid van Moolenbroek * find_block * 284e94f856bSDavid van Moolenbroek *===========================================================================*/ 285e94f856bSDavid van Moolenbroek static struct buf *find_block(dev_t dev, block64_t block) 286e94f856bSDavid van Moolenbroek { 287e94f856bSDavid van Moolenbroek /* Search the hash chain for (dev, block). Return the buffer structure if 288e94f856bSDavid van Moolenbroek * found, or NULL otherwise. 289e94f856bSDavid van Moolenbroek */ 290e94f856bSDavid van Moolenbroek struct buf *bp; 291e94f856bSDavid van Moolenbroek int b; 292e94f856bSDavid van Moolenbroek 293e94f856bSDavid van Moolenbroek assert(dev != NO_DEV); 294e94f856bSDavid van Moolenbroek 295e94f856bSDavid van Moolenbroek b = BUFHASH(block); 296e94f856bSDavid van Moolenbroek for (bp = buf_hash[b]; bp != NULL; bp = bp->lmfs_hash) 297e94f856bSDavid van Moolenbroek if (bp->lmfs_blocknr == block && bp->lmfs_dev == dev) 298e94f856bSDavid van Moolenbroek return bp; 299e94f856bSDavid van Moolenbroek 300e94f856bSDavid van Moolenbroek return NULL; 301e94f856bSDavid van Moolenbroek } 302e94f856bSDavid van Moolenbroek 303e94f856bSDavid van Moolenbroek /*===========================================================================* 304*6c46a77dSDavid van Moolenbroek * get_block_ino * 305433d6423SLionel Sambuc *===========================================================================*/ 306*6c46a77dSDavid van Moolenbroek static int get_block_ino(struct buf **bpp, dev_t dev, block64_t block, int how, 307*6c46a77dSDavid van Moolenbroek ino_t ino, u64_t ino_off, size_t block_size) 308433d6423SLionel Sambuc { 309*6c46a77dSDavid van Moolenbroek /* Check to see if the requested block is in the block cache. The requested 310*6c46a77dSDavid van Moolenbroek * block is identified by the block number in 'block' on device 'dev', counted 311*6c46a77dSDavid van Moolenbroek * in the file system block size. The amount of data requested for this block 312*6c46a77dSDavid van Moolenbroek * is given in 'block_size', which may be less than the file system block size 313*6c46a77dSDavid van Moolenbroek * iff the requested block is the last (partial) block on a device. Note that 314*6c46a77dSDavid van Moolenbroek * the given block size does *not* affect the conversion of 'block' to a byte 315*6c46a77dSDavid van Moolenbroek * offset! Either way, if the block could be obtained, either from the cache 316*6c46a77dSDavid van Moolenbroek * or by reading from the device, return OK, with a pointer to the buffer 317*6c46a77dSDavid van Moolenbroek * structure stored in 'bpp'. If not, return a negative error code (and no 318*6c46a77dSDavid van Moolenbroek * buffer). If necessary, evict some other block and fetch the contents from 319*6c46a77dSDavid van Moolenbroek * disk (if 'how' is NORMAL). If 'how' is NO_READ, the caller intends to 320*6c46a77dSDavid van Moolenbroek * overwrite the requested block in its entirety, so it is only necessary to 321*6c46a77dSDavid van Moolenbroek * see if it is in the cache; if it is not, any free buffer will do. If 'how' 322cb9453caSDavid van Moolenbroek * is PREFETCH, the block need not be read from the disk, and the device is not 323cb9453caSDavid van Moolenbroek * to be marked on the block (i.e., set to NO_DEV), so callers can tell if the 324cb9453caSDavid van Moolenbroek * block returned is valid. If 'how' is PEEK, the function returns the block 325*6c46a77dSDavid van Moolenbroek * if it is in the cache or the VM cache, and an ENOENT error code otherwise. 326433d6423SLionel Sambuc * In addition to the LRU chain, there is also a hash chain to link together 327433d6423SLionel Sambuc * blocks whose block numbers end with the same bit strings, for fast lookup. 328433d6423SLionel Sambuc */ 329*6c46a77dSDavid van Moolenbroek int b, r; 330433d6423SLionel Sambuc static struct buf *bp; 331b65ad59eSDavid van Moolenbroek uint64_t dev_off; 332433d6423SLionel Sambuc struct buf *prev_ptr; 333433d6423SLionel Sambuc 334433d6423SLionel Sambuc assert(buf_hash); 335433d6423SLionel Sambuc assert(buf); 336433d6423SLionel Sambuc assert(nr_bufs > 0); 337433d6423SLionel Sambuc 338433d6423SLionel Sambuc ASSERT(fs_block_size > 0); 339433d6423SLionel Sambuc 340433d6423SLionel Sambuc assert(dev != NO_DEV); 341433d6423SLionel Sambuc 342b65ad59eSDavid van Moolenbroek assert(block <= UINT64_MAX / fs_block_size); 343b65ad59eSDavid van Moolenbroek 344b65ad59eSDavid van Moolenbroek dev_off = block * fs_block_size; 345b65ad59eSDavid van Moolenbroek 346433d6423SLionel Sambuc if((ino_off % fs_block_size)) { 347433d6423SLionel Sambuc 348433d6423SLionel Sambuc printf("cache: unaligned lmfs_get_block_ino ino_off %llu\n", 349433d6423SLionel Sambuc ino_off); 350433d6423SLionel Sambuc util_stacktrace(); 351433d6423SLionel Sambuc } 352433d6423SLionel Sambuc 353e94f856bSDavid van Moolenbroek /* See if the block is in the cache. If so, we can return it right away. */ 354e94f856bSDavid van Moolenbroek bp = find_block(dev, block); 355e94f856bSDavid van Moolenbroek if (bp != NULL && !(bp->lmfs_flags & VMMC_EVICTED)) { 356*6c46a77dSDavid van Moolenbroek ASSERT(bp->lmfs_dev == dev); 357*6c46a77dSDavid van Moolenbroek ASSERT(bp->lmfs_dev != NO_DEV); 358*6c46a77dSDavid van Moolenbroek 359*6c46a77dSDavid van Moolenbroek /* The block must have exactly the requested number of bytes. */ 360*6c46a77dSDavid van Moolenbroek if (bp->lmfs_bytes != block_size) 361*6c46a77dSDavid van Moolenbroek return EIO; 362*6c46a77dSDavid van Moolenbroek 363433d6423SLionel Sambuc /* Block needed has been found. */ 364433d6423SLionel Sambuc if (bp->lmfs_count == 0) { 365433d6423SLionel Sambuc rm_lru(bp); 366433d6423SLionel Sambuc ASSERT(bp->lmfs_needsetcache == 0); 367433d6423SLionel Sambuc ASSERT(!(bp->lmfs_flags & VMMC_BLOCK_LOCKED)); 368e94f856bSDavid van Moolenbroek /* FIXME: race condition against the VMMC_EVICTED check */ 369433d6423SLionel Sambuc bp->lmfs_flags |= VMMC_BLOCK_LOCKED; 370433d6423SLionel Sambuc } 371433d6423SLionel Sambuc raisecount(bp); 372433d6423SLionel Sambuc ASSERT(bp->lmfs_flags & VMMC_BLOCK_LOCKED); 373433d6423SLionel Sambuc ASSERT(bp->data); 374433d6423SLionel Sambuc 375433d6423SLionel Sambuc if(ino != VMC_NO_INODE) { 376433d6423SLionel Sambuc if(bp->lmfs_inode == VMC_NO_INODE 377433d6423SLionel Sambuc || bp->lmfs_inode != ino 378433d6423SLionel Sambuc || bp->lmfs_inode_offset != ino_off) { 379433d6423SLionel Sambuc bp->lmfs_inode = ino; 380433d6423SLionel Sambuc bp->lmfs_inode_offset = ino_off; 381433d6423SLionel Sambuc bp->lmfs_needsetcache = 1; 382433d6423SLionel Sambuc } 383433d6423SLionel Sambuc } 384433d6423SLionel Sambuc 385*6c46a77dSDavid van Moolenbroek *bpp = bp; 386*6c46a77dSDavid van Moolenbroek return OK; 387433d6423SLionel Sambuc } 388e94f856bSDavid van Moolenbroek 389e94f856bSDavid van Moolenbroek /* We had the block in the cache but VM evicted it; invalidate it. */ 390e94f856bSDavid van Moolenbroek if (bp != NULL) { 391e94f856bSDavid van Moolenbroek assert(bp->lmfs_flags & VMMC_EVICTED); 392e94f856bSDavid van Moolenbroek ASSERT(bp->lmfs_count == 0); 393e94f856bSDavid van Moolenbroek ASSERT(!(bp->lmfs_flags & VMMC_BLOCK_LOCKED)); 394e94f856bSDavid van Moolenbroek ASSERT(!(bp->lmfs_flags & VMMC_DIRTY)); 395e94f856bSDavid van Moolenbroek bp->lmfs_dev = NO_DEV; 396e94f856bSDavid van Moolenbroek bp->lmfs_bytes = 0; 397e94f856bSDavid van Moolenbroek bp->data = NULL; 398433d6423SLionel Sambuc } 399433d6423SLionel Sambuc 400433d6423SLionel Sambuc /* Desired block is not on available chain. Find a free block to use. */ 401433d6423SLionel Sambuc if(bp) { 402433d6423SLionel Sambuc ASSERT(bp->lmfs_flags & VMMC_EVICTED); 403433d6423SLionel Sambuc } else { 404433d6423SLionel Sambuc if ((bp = front) == NULL) panic("all buffers in use: %d", nr_bufs); 405433d6423SLionel Sambuc } 406433d6423SLionel Sambuc assert(bp); 407433d6423SLionel Sambuc 408433d6423SLionel Sambuc rm_lru(bp); 409433d6423SLionel Sambuc 410433d6423SLionel Sambuc /* Remove the block that was just taken from its hash chain. */ 411433d6423SLionel Sambuc b = BUFHASH(bp->lmfs_blocknr); 412433d6423SLionel Sambuc prev_ptr = buf_hash[b]; 413433d6423SLionel Sambuc if (prev_ptr == bp) { 414433d6423SLionel Sambuc buf_hash[b] = bp->lmfs_hash; 415433d6423SLionel Sambuc } else { 416433d6423SLionel Sambuc /* The block just taken is not on the front of its hash chain. */ 417433d6423SLionel Sambuc while (prev_ptr->lmfs_hash != NULL) 418433d6423SLionel Sambuc if (prev_ptr->lmfs_hash == bp) { 419433d6423SLionel Sambuc prev_ptr->lmfs_hash = bp->lmfs_hash; /* found it */ 420433d6423SLionel Sambuc break; 421433d6423SLionel Sambuc } else { 422433d6423SLionel Sambuc prev_ptr = prev_ptr->lmfs_hash; /* keep looking */ 423433d6423SLionel Sambuc } 424433d6423SLionel Sambuc } 425433d6423SLionel Sambuc 426433d6423SLionel Sambuc freeblock(bp); 427433d6423SLionel Sambuc 428433d6423SLionel Sambuc bp->lmfs_inode = ino; 429433d6423SLionel Sambuc bp->lmfs_inode_offset = ino_off; 430433d6423SLionel Sambuc 431433d6423SLionel Sambuc bp->lmfs_flags = VMMC_BLOCK_LOCKED; 432433d6423SLionel Sambuc bp->lmfs_needsetcache = 0; 433433d6423SLionel Sambuc bp->lmfs_dev = dev; /* fill in device number */ 434433d6423SLionel Sambuc bp->lmfs_blocknr = block; /* fill in block number */ 435433d6423SLionel Sambuc ASSERT(bp->lmfs_count == 0); 436433d6423SLionel Sambuc raisecount(bp); 437433d6423SLionel Sambuc b = BUFHASH(bp->lmfs_blocknr); 438433d6423SLionel Sambuc bp->lmfs_hash = buf_hash[b]; 439433d6423SLionel Sambuc 440433d6423SLionel Sambuc buf_hash[b] = bp; /* add to hash list */ 441433d6423SLionel Sambuc 442433d6423SLionel Sambuc assert(dev != NO_DEV); 443433d6423SLionel Sambuc 444433d6423SLionel Sambuc /* Block is not found in our cache, but we do want it 445433d6423SLionel Sambuc * if it's in the vm cache. 446433d6423SLionel Sambuc */ 447433d6423SLionel Sambuc assert(!bp->data); 448433d6423SLionel Sambuc assert(!bp->lmfs_bytes); 449433d6423SLionel Sambuc if(vmcache) { 450433d6423SLionel Sambuc if((bp->data = vm_map_cacheblock(dev, dev_off, ino, ino_off, 451*6c46a77dSDavid van Moolenbroek &bp->lmfs_flags, roundup(block_size, PAGE_SIZE))) != MAP_FAILED) { 452*6c46a77dSDavid van Moolenbroek bp->lmfs_bytes = block_size; 453433d6423SLionel Sambuc ASSERT(!bp->lmfs_needsetcache); 454*6c46a77dSDavid van Moolenbroek *bpp = bp; 455*6c46a77dSDavid van Moolenbroek return OK; 456433d6423SLionel Sambuc } 457433d6423SLionel Sambuc } 458433d6423SLionel Sambuc bp->data = NULL; 459433d6423SLionel Sambuc 460cb9453caSDavid van Moolenbroek /* The block is not in the cache, and VM does not know about it. If we were 461cb9453caSDavid van Moolenbroek * requested to search for the block only, we can now return failure to the 462cb9453caSDavid van Moolenbroek * caller. Return the block to the pool without allocating data pages, since 463cb9453caSDavid van Moolenbroek * these would be freed upon recycling the block anyway. 464cb9453caSDavid van Moolenbroek */ 465cb9453caSDavid van Moolenbroek if (how == PEEK) { 466cb9453caSDavid van Moolenbroek bp->lmfs_dev = NO_DEV; 467cb9453caSDavid van Moolenbroek 4680314acfbSDavid van Moolenbroek put_block(bp, ONE_SHOT); 469cb9453caSDavid van Moolenbroek 470*6c46a77dSDavid van Moolenbroek return ENOENT; 471cb9453caSDavid van Moolenbroek } 472cb9453caSDavid van Moolenbroek 473433d6423SLionel Sambuc /* Not in the cache; reserve memory for its contents. */ 474433d6423SLionel Sambuc 475*6c46a77dSDavid van Moolenbroek lmfs_alloc_block(bp, block_size); 476433d6423SLionel Sambuc 477433d6423SLionel Sambuc assert(bp->data); 478433d6423SLionel Sambuc 479cb9453caSDavid van Moolenbroek if(how == PREFETCH) { 480433d6423SLionel Sambuc /* PREFETCH: don't do i/o. */ 481433d6423SLionel Sambuc bp->lmfs_dev = NO_DEV; 482cb9453caSDavid van Moolenbroek } else if (how == NORMAL) { 483*6c46a77dSDavid van Moolenbroek /* Try to read the block. Return an error code on failure. */ 484*6c46a77dSDavid van Moolenbroek if ((r = read_block(bp, block_size)) != OK) { 485*6c46a77dSDavid van Moolenbroek put_block(bp, 0); 486*6c46a77dSDavid van Moolenbroek 487*6c46a77dSDavid van Moolenbroek return r; 488*6c46a77dSDavid van Moolenbroek } 489cb9453caSDavid van Moolenbroek } else if(how == NO_READ) { 490433d6423SLionel Sambuc /* This block will be overwritten by new contents. */ 491433d6423SLionel Sambuc } else 492cb9453caSDavid van Moolenbroek panic("unexpected 'how' value: %d", how); 493433d6423SLionel Sambuc 494433d6423SLionel Sambuc assert(bp->data); 495433d6423SLionel Sambuc 496*6c46a77dSDavid van Moolenbroek *bpp = bp; /* return the newly acquired block */ 497*6c46a77dSDavid van Moolenbroek return OK; 498*6c46a77dSDavid van Moolenbroek } 499*6c46a77dSDavid van Moolenbroek 500*6c46a77dSDavid van Moolenbroek /*===========================================================================* 501*6c46a77dSDavid van Moolenbroek * lmfs_get_block_ino * 502*6c46a77dSDavid van Moolenbroek *===========================================================================*/ 503*6c46a77dSDavid van Moolenbroek int lmfs_get_block_ino(struct buf **bpp, dev_t dev, block64_t block, int how, 504*6c46a77dSDavid van Moolenbroek ino_t ino, u64_t ino_off) 505*6c46a77dSDavid van Moolenbroek { 506*6c46a77dSDavid van Moolenbroek return get_block_ino(bpp, dev, block, how, ino, ino_off, fs_block_size); 507*6c46a77dSDavid van Moolenbroek } 508*6c46a77dSDavid van Moolenbroek 509*6c46a77dSDavid van Moolenbroek /*===========================================================================* 510*6c46a77dSDavid van Moolenbroek * lmfs_get_partial_block * 511*6c46a77dSDavid van Moolenbroek *===========================================================================*/ 512*6c46a77dSDavid van Moolenbroek int lmfs_get_partial_block(struct buf **bpp, dev_t dev, block64_t block, 513*6c46a77dSDavid van Moolenbroek int how, size_t block_size) 514*6c46a77dSDavid van Moolenbroek { 515*6c46a77dSDavid van Moolenbroek return get_block_ino(bpp, dev, block, how, VMC_NO_INODE, 0, block_size); 516433d6423SLionel Sambuc } 517433d6423SLionel Sambuc 518433d6423SLionel Sambuc /*===========================================================================* 5190314acfbSDavid van Moolenbroek * put_block * 520433d6423SLionel Sambuc *===========================================================================*/ 5210314acfbSDavid van Moolenbroek static void put_block(struct buf *bp, int put_flags) 522433d6423SLionel Sambuc { 5230314acfbSDavid van Moolenbroek /* Return a block to the list of available blocks. Depending on 'put_flags' 524433d6423SLionel Sambuc * it may be put on the front or rear of the LRU chain. Blocks that are 5250314acfbSDavid van Moolenbroek * expected to be needed again at some point go on the rear; blocks that are 5260314acfbSDavid van Moolenbroek * unlikely to be needed again at all go on the front. 527433d6423SLionel Sambuc */ 528433d6423SLionel Sambuc dev_t dev; 529b65ad59eSDavid van Moolenbroek uint64_t dev_off; 530d75faf18SDavid van Moolenbroek int r, setflags; 531433d6423SLionel Sambuc 5320314acfbSDavid van Moolenbroek assert(bp != NULL); 533433d6423SLionel Sambuc 534433d6423SLionel Sambuc dev = bp->lmfs_dev; 535433d6423SLionel Sambuc 536b65ad59eSDavid van Moolenbroek dev_off = bp->lmfs_blocknr * fs_block_size; 537433d6423SLionel Sambuc 538433d6423SLionel Sambuc lowercount(bp); 539433d6423SLionel Sambuc if (bp->lmfs_count != 0) return; /* block is still in use */ 540433d6423SLionel Sambuc 541433d6423SLionel Sambuc /* Put this block back on the LRU chain. */ 5420314acfbSDavid van Moolenbroek if (dev == NO_DEV || dev == DEV_RAM || (put_flags & ONE_SHOT)) { 5430314acfbSDavid van Moolenbroek /* Block will not be needed again. Put it on front of chain. 544433d6423SLionel Sambuc * It will be the next block to be evicted from the cache. 545433d6423SLionel Sambuc */ 546433d6423SLionel Sambuc bp->lmfs_prev = NULL; 547433d6423SLionel Sambuc bp->lmfs_next = front; 548433d6423SLionel Sambuc if (front == NULL) 549433d6423SLionel Sambuc rear = bp; /* LRU chain was empty */ 550433d6423SLionel Sambuc else 551433d6423SLionel Sambuc front->lmfs_prev = bp; 552433d6423SLionel Sambuc front = bp; 553433d6423SLionel Sambuc } 554433d6423SLionel Sambuc else { 5550314acfbSDavid van Moolenbroek /* Block may be needed again. Put it on rear of chain. 556433d6423SLionel Sambuc * It will not be evicted from the cache for a long time. 557433d6423SLionel Sambuc */ 558433d6423SLionel Sambuc bp->lmfs_prev = rear; 559433d6423SLionel Sambuc bp->lmfs_next = NULL; 560433d6423SLionel Sambuc if (rear == NULL) 561433d6423SLionel Sambuc front = bp; 562433d6423SLionel Sambuc else 563433d6423SLionel Sambuc rear->lmfs_next = bp; 564433d6423SLionel Sambuc rear = bp; 565433d6423SLionel Sambuc } 566433d6423SLionel Sambuc 567433d6423SLionel Sambuc assert(bp->lmfs_flags & VMMC_BLOCK_LOCKED); 568433d6423SLionel Sambuc bp->lmfs_flags &= ~VMMC_BLOCK_LOCKED; 569433d6423SLionel Sambuc 570cb9453caSDavid van Moolenbroek /* block has sensible content - if necessary, identify it to VM */ 571433d6423SLionel Sambuc if(vmcache && bp->lmfs_needsetcache && dev != NO_DEV) { 572cb9453caSDavid van Moolenbroek assert(bp->data); 573cb9453caSDavid van Moolenbroek 5740314acfbSDavid van Moolenbroek setflags = (put_flags & ONE_SHOT) ? VMSF_ONCE : 0; 575*6c46a77dSDavid van Moolenbroek 576d75faf18SDavid van Moolenbroek if ((r = vm_set_cacheblock(bp->data, dev, dev_off, bp->lmfs_inode, 577*6c46a77dSDavid van Moolenbroek bp->lmfs_inode_offset, &bp->lmfs_flags, 578*6c46a77dSDavid van Moolenbroek roundup(bp->lmfs_bytes, PAGE_SIZE), setflags)) != OK) { 579433d6423SLionel Sambuc if(r == ENOSYS) { 580433d6423SLionel Sambuc printf("libminixfs: ENOSYS, disabling VM calls\n"); 581433d6423SLionel Sambuc vmcache = 0; 582*6c46a77dSDavid van Moolenbroek } else if (r == ENOMEM) { 583*6c46a77dSDavid van Moolenbroek /* Do not panic in this case. Running out of memory is 584*6c46a77dSDavid van Moolenbroek * bad, especially since it may lead to applications 585*6c46a77dSDavid van Moolenbroek * crashing when trying to access memory-mapped pages 586*6c46a77dSDavid van Moolenbroek * we haven't been able to pass off to the VM cache, 587*6c46a77dSDavid van Moolenbroek * but the entire file system crashing is always worse. 588*6c46a77dSDavid van Moolenbroek */ 589*6c46a77dSDavid van Moolenbroek printf("libminixfs: no memory for cache block!\n"); 590433d6423SLionel Sambuc } else { 591433d6423SLionel Sambuc panic("libminixfs: setblock of %p dev 0x%llx off " 592433d6423SLionel Sambuc "0x%llx failed\n", bp->data, dev, dev_off); 593433d6423SLionel Sambuc } 594433d6423SLionel Sambuc } 595433d6423SLionel Sambuc } 596433d6423SLionel Sambuc bp->lmfs_needsetcache = 0; 597d75faf18SDavid van Moolenbroek 598d75faf18SDavid van Moolenbroek /* Now that we (may) have given the block to VM, invalidate the block if it 599d75faf18SDavid van Moolenbroek * is a one-shot block. Otherwise, it may still be reobtained immediately 600d75faf18SDavid van Moolenbroek * after, which could be a problem if VM already forgot the block and we are 601d75faf18SDavid van Moolenbroek * expected to pass it to VM again, which then wouldn't happen. 602d75faf18SDavid van Moolenbroek */ 6030314acfbSDavid van Moolenbroek if (put_flags & ONE_SHOT) 604d75faf18SDavid van Moolenbroek bp->lmfs_dev = NO_DEV; 605e94f856bSDavid van Moolenbroek } 606433d6423SLionel Sambuc 607e94f856bSDavid van Moolenbroek /*===========================================================================* 6080314acfbSDavid van Moolenbroek * lmfs_put_block * 6090314acfbSDavid van Moolenbroek *===========================================================================*/ 6100314acfbSDavid van Moolenbroek void lmfs_put_block(struct buf *bp) 6110314acfbSDavid van Moolenbroek { 6120314acfbSDavid van Moolenbroek /* User interface to put_block(). */ 6130314acfbSDavid van Moolenbroek 6140314acfbSDavid van Moolenbroek if (bp == NULL) return; /* for poorly written file systems */ 6150314acfbSDavid van Moolenbroek 6160314acfbSDavid van Moolenbroek put_block(bp, 0); 6170314acfbSDavid van Moolenbroek } 6180314acfbSDavid van Moolenbroek 6190314acfbSDavid van Moolenbroek /*===========================================================================* 620e94f856bSDavid van Moolenbroek * lmfs_free_block * 621e94f856bSDavid van Moolenbroek *===========================================================================*/ 622e94f856bSDavid van Moolenbroek void lmfs_free_block(dev_t dev, block64_t block) 623e94f856bSDavid van Moolenbroek { 624e94f856bSDavid van Moolenbroek /* The file system has just freed the given block. The block may previously 625e94f856bSDavid van Moolenbroek * have been in use as data block for an inode. Therefore, we now need to tell 626e94f856bSDavid van Moolenbroek * VM that the block is no longer associated with an inode. If we fail to do so 627e94f856bSDavid van Moolenbroek * and the inode now has a hole at this location, mapping in the hole would 628e94f856bSDavid van Moolenbroek * yield the old block contents rather than a zeroed page. In addition, if the 629e94f856bSDavid van Moolenbroek * block is in the cache, it will be removed, even if it was dirty. 630e94f856bSDavid van Moolenbroek */ 631e94f856bSDavid van Moolenbroek struct buf *bp; 632e94f856bSDavid van Moolenbroek int r; 633e94f856bSDavid van Moolenbroek 634e94f856bSDavid van Moolenbroek /* Tell VM to forget about the block. The primary purpose of this call is to 635e94f856bSDavid van Moolenbroek * break the inode association, but since the block is part of a mounted file 636e94f856bSDavid van Moolenbroek * system, it is not expected to be accessed directly anyway. So, save some 637e94f856bSDavid van Moolenbroek * cache memory by throwing it out of the VM cache altogether. 638e94f856bSDavid van Moolenbroek */ 639e94f856bSDavid van Moolenbroek if (vmcache) { 640e94f856bSDavid van Moolenbroek if ((r = vm_forget_cacheblock(dev, block * fs_block_size, 641e94f856bSDavid van Moolenbroek fs_block_size)) != OK) 642e94f856bSDavid van Moolenbroek printf("libminixfs: vm_forget_cacheblock failed (%d)\n", r); 643e94f856bSDavid van Moolenbroek } 644e94f856bSDavid van Moolenbroek 645e94f856bSDavid van Moolenbroek if ((bp = find_block(dev, block)) != NULL) { 646e94f856bSDavid van Moolenbroek lmfs_markclean(bp); 647e94f856bSDavid van Moolenbroek 648e94f856bSDavid van Moolenbroek /* Invalidate the block. The block may or may not be in use right now, 649e94f856bSDavid van Moolenbroek * so don't be smart about freeing memory or repositioning in the LRU. 650e94f856bSDavid van Moolenbroek */ 651e94f856bSDavid van Moolenbroek bp->lmfs_dev = NO_DEV; 652e94f856bSDavid van Moolenbroek } 653e94f856bSDavid van Moolenbroek 654e94f856bSDavid van Moolenbroek /* Note that this is *not* the right place to implement TRIM support. Even 655e94f856bSDavid van Moolenbroek * though the block is freed, on the device it may still be part of a 656e94f856bSDavid van Moolenbroek * previous checkpoint or snapshot of some sort. Only the file system can 657e94f856bSDavid van Moolenbroek * be trusted to decide which blocks can be reused on the device! 658e94f856bSDavid van Moolenbroek */ 659433d6423SLionel Sambuc } 660433d6423SLionel Sambuc 661d75faf18SDavid van Moolenbroek /*===========================================================================* 662d75faf18SDavid van Moolenbroek * lmfs_zero_block_ino * 663d75faf18SDavid van Moolenbroek *===========================================================================*/ 664d75faf18SDavid van Moolenbroek void lmfs_zero_block_ino(dev_t dev, ino_t ino, u64_t ino_off) 665d75faf18SDavid van Moolenbroek { 666d75faf18SDavid van Moolenbroek /* Files may have holes. From an application perspective, these are just file 667d75faf18SDavid van Moolenbroek * regions filled with zeroes. From a file system perspective however, holes 668d75faf18SDavid van Moolenbroek * may represent unallocated regions on disk. Thus, these holes do not have 669d75faf18SDavid van Moolenbroek * corresponding blocks on the disk, and therefore also no block number. 670d75faf18SDavid van Moolenbroek * Therefore, we cannot simply use lmfs_get_block_ino() for them. For reads, 671d75faf18SDavid van Moolenbroek * this is not a problem, since the file system can just zero out the target 672d75faf18SDavid van Moolenbroek * application buffer instead. For mapped pages however, this *is* a problem, 673d75faf18SDavid van Moolenbroek * since the VM cache needs to be told about the corresponding block, and VM 674d75faf18SDavid van Moolenbroek * does not accept blocks without a device offset. The role of this function is 675d75faf18SDavid van Moolenbroek * therefore to tell VM about the hole using a fake device offset. The device 676d75faf18SDavid van Moolenbroek * offsets are picked so that the VM cache will see a block memory-mapped for 677d75faf18SDavid van Moolenbroek * the hole in the file, while the same block is not visible when 678d75faf18SDavid van Moolenbroek * memory-mapping the block device. 679d75faf18SDavid van Moolenbroek */ 680d75faf18SDavid van Moolenbroek struct buf *bp; 681d75faf18SDavid van Moolenbroek static block64_t fake_block = 0; 682*6c46a77dSDavid van Moolenbroek int r; 683d75faf18SDavid van Moolenbroek 684d75faf18SDavid van Moolenbroek if (!vmcache) 685d75faf18SDavid van Moolenbroek return; 686d75faf18SDavid van Moolenbroek 687d75faf18SDavid van Moolenbroek assert(fs_block_size > 0); 688d75faf18SDavid van Moolenbroek 689d75faf18SDavid van Moolenbroek /* Pick a block number which is above the threshold of what can possibly be 690d75faf18SDavid van Moolenbroek * mapped in by mmap'ing the device, since off_t is signed, and it is safe to 691d75faf18SDavid van Moolenbroek * say that it will take a while before we have 8-exabyte devices. Pick a 692d75faf18SDavid van Moolenbroek * different block number each time to avoid possible concurrency issues. 693d75faf18SDavid van Moolenbroek * FIXME: it does not seem like VM actually verifies mmap offsets though.. 694d75faf18SDavid van Moolenbroek */ 695d75faf18SDavid van Moolenbroek if (fake_block == 0 || ++fake_block >= UINT64_MAX / fs_block_size) 696d75faf18SDavid van Moolenbroek fake_block = ((uint64_t)INT64_MAX + 1) / fs_block_size; 697d75faf18SDavid van Moolenbroek 698d75faf18SDavid van Moolenbroek /* Obtain a block. */ 699*6c46a77dSDavid van Moolenbroek if ((r = lmfs_get_block_ino(&bp, dev, fake_block, NO_READ, ino, 700*6c46a77dSDavid van Moolenbroek ino_off)) != OK) 701*6c46a77dSDavid van Moolenbroek panic("libminixfs: getting a NO_READ block failed: %d", r); 702d75faf18SDavid van Moolenbroek assert(bp != NULL); 703d75faf18SDavid van Moolenbroek assert(bp->lmfs_dev != NO_DEV); 704d75faf18SDavid van Moolenbroek 705d75faf18SDavid van Moolenbroek /* The block is already zeroed, as it has just been allocated with mmap. File 706d75faf18SDavid van Moolenbroek * systems do not rely on this assumption yet, so if VM ever gets changed to 707d75faf18SDavid van Moolenbroek * not clear the blocks we allocate (e.g., by recycling pages in the VM cache 708d75faf18SDavid van Moolenbroek * for the same process, which would be safe), we need to add a memset here. 709d75faf18SDavid van Moolenbroek */ 710d75faf18SDavid van Moolenbroek 711d75faf18SDavid van Moolenbroek /* Release the block. We don't expect it to be accessed ever again. Moreover, 712d75faf18SDavid van Moolenbroek * if we keep the block around in the VM cache, it may erroneously be mapped 713d75faf18SDavid van Moolenbroek * in beyond the file end later. Hence, use VMSF_ONCE when passing it to VM. 714d75faf18SDavid van Moolenbroek * TODO: tell VM that it is an all-zeroes block, so that VM can deduplicate 715d75faf18SDavid van Moolenbroek * all such pages in its cache. 716d75faf18SDavid van Moolenbroek */ 7170314acfbSDavid van Moolenbroek put_block(bp, ONE_SHOT); 718d75faf18SDavid van Moolenbroek } 719d75faf18SDavid van Moolenbroek 7201311233cSDavid van Moolenbroek void lmfs_set_blockusage(fsblkcnt_t btotal, fsblkcnt_t bused) 721433d6423SLionel Sambuc { 7221311233cSDavid van Moolenbroek 7231311233cSDavid van Moolenbroek assert(bused <= btotal); 7241311233cSDavid van Moolenbroek fs_btotal = btotal; 7251311233cSDavid van Moolenbroek fs_bused = bused; 7261311233cSDavid van Moolenbroek 7271311233cSDavid van Moolenbroek /* if the cache isn't in use, we could resize it. */ 7281311233cSDavid van Moolenbroek if (bufs_in_use == 0) 7290314acfbSDavid van Moolenbroek cache_heuristic_check(); 730433d6423SLionel Sambuc } 731433d6423SLionel Sambuc 732433d6423SLionel Sambuc /*===========================================================================* 733433d6423SLionel Sambuc * read_block * 734433d6423SLionel Sambuc *===========================================================================*/ 735*6c46a77dSDavid van Moolenbroek static int read_block(struct buf *bp, size_t block_size) 736433d6423SLionel Sambuc { 737*6c46a77dSDavid van Moolenbroek /* Read a disk block of 'size' bytes. The given size is always the FS block 738*6c46a77dSDavid van Moolenbroek * size, except for the last block of a device. If an I/O error occurs, 739*6c46a77dSDavid van Moolenbroek * invalidate the block and return an error code. 740433d6423SLionel Sambuc */ 741*6c46a77dSDavid van Moolenbroek ssize_t r; 742433d6423SLionel Sambuc off_t pos; 743433d6423SLionel Sambuc dev_t dev = bp->lmfs_dev; 744433d6423SLionel Sambuc 745433d6423SLionel Sambuc assert(dev != NO_DEV); 746433d6423SLionel Sambuc 747*6c46a77dSDavid van Moolenbroek ASSERT(bp->lmfs_bytes == block_size); 748433d6423SLionel Sambuc ASSERT(fs_block_size > 0); 749433d6423SLionel Sambuc 750433d6423SLionel Sambuc pos = (off_t)bp->lmfs_blocknr * fs_block_size; 751*6c46a77dSDavid van Moolenbroek if (block_size > PAGE_SIZE) { 752433d6423SLionel Sambuc #define MAXPAGES 20 753433d6423SLionel Sambuc vir_bytes blockrem, vaddr = (vir_bytes) bp->data; 754433d6423SLionel Sambuc int p = 0; 755433d6423SLionel Sambuc static iovec_t iovec[MAXPAGES]; 756*6c46a77dSDavid van Moolenbroek blockrem = block_size; 757433d6423SLionel Sambuc while(blockrem > 0) { 758433d6423SLionel Sambuc vir_bytes chunk = blockrem >= PAGE_SIZE ? PAGE_SIZE : blockrem; 759433d6423SLionel Sambuc iovec[p].iov_addr = vaddr; 760433d6423SLionel Sambuc iovec[p].iov_size = chunk; 761433d6423SLionel Sambuc vaddr += chunk; 762433d6423SLionel Sambuc blockrem -= chunk; 763433d6423SLionel Sambuc p++; 764433d6423SLionel Sambuc } 765433d6423SLionel Sambuc r = bdev_gather(dev, pos, iovec, p, BDEV_NOFLAGS); 766433d6423SLionel Sambuc } else { 767*6c46a77dSDavid van Moolenbroek r = bdev_read(dev, pos, bp->data, block_size, BDEV_NOFLAGS); 768433d6423SLionel Sambuc } 769*6c46a77dSDavid van Moolenbroek if (r != (ssize_t)block_size) { 770*6c46a77dSDavid van Moolenbroek printf("fs cache: I/O error on device %d/%d, block %"PRIu64" (%zd)\n", 771*6c46a77dSDavid van Moolenbroek major(dev), minor(dev), bp->lmfs_blocknr, r); 772*6c46a77dSDavid van Moolenbroek if (r >= 0) 773*6c46a77dSDavid van Moolenbroek r = EIO; /* TODO: retry retrieving (just) the remaining part */ 774433d6423SLionel Sambuc 775433d6423SLionel Sambuc bp->lmfs_dev = NO_DEV; /* invalidate block */ 776433d6423SLionel Sambuc 777*6c46a77dSDavid van Moolenbroek return r; 778433d6423SLionel Sambuc } 779433d6423SLionel Sambuc 780*6c46a77dSDavid van Moolenbroek return OK; 781433d6423SLionel Sambuc } 782433d6423SLionel Sambuc 783433d6423SLionel Sambuc /*===========================================================================* 784433d6423SLionel Sambuc * lmfs_invalidate * 785433d6423SLionel Sambuc *===========================================================================*/ 786433d6423SLionel Sambuc void lmfs_invalidate( 787433d6423SLionel Sambuc dev_t device /* device whose blocks are to be purged */ 788433d6423SLionel Sambuc ) 789433d6423SLionel Sambuc { 790433d6423SLionel Sambuc /* Remove all the blocks belonging to some device from the cache. */ 791433d6423SLionel Sambuc 792433d6423SLionel Sambuc register struct buf *bp; 793433d6423SLionel Sambuc 794cb9453caSDavid van Moolenbroek assert(device != NO_DEV); 795cb9453caSDavid van Moolenbroek 796433d6423SLionel Sambuc for (bp = &buf[0]; bp < &buf[nr_bufs]; bp++) { 797433d6423SLionel Sambuc if (bp->lmfs_dev == device) { 798433d6423SLionel Sambuc assert(bp->data); 799433d6423SLionel Sambuc assert(bp->lmfs_bytes > 0); 800433d6423SLionel Sambuc munmap_t(bp->data, bp->lmfs_bytes); 801433d6423SLionel Sambuc bp->lmfs_dev = NO_DEV; 802433d6423SLionel Sambuc bp->lmfs_bytes = 0; 803433d6423SLionel Sambuc bp->data = NULL; 804433d6423SLionel Sambuc } 805433d6423SLionel Sambuc } 806433d6423SLionel Sambuc 807e94f856bSDavid van Moolenbroek /* Clear the cache even if VM caching is disabled for the file system: 808e94f856bSDavid van Moolenbroek * caching may be disabled as side effect of an error, leaving blocks behind 809e94f856bSDavid van Moolenbroek * in the actual VM cache. 810e94f856bSDavid van Moolenbroek */ 811433d6423SLionel Sambuc vm_clear_cache(device); 812433d6423SLionel Sambuc } 813433d6423SLionel Sambuc 814433d6423SLionel Sambuc /*===========================================================================* 815ebd3c067SDavid van Moolenbroek * lmfs_flushdev * 816433d6423SLionel Sambuc *===========================================================================*/ 817ebd3c067SDavid van Moolenbroek void lmfs_flushdev(dev_t dev) 818433d6423SLionel Sambuc { 819433d6423SLionel Sambuc /* Flush all dirty blocks for one device. */ 820433d6423SLionel Sambuc 821433d6423SLionel Sambuc register struct buf *bp; 822b8f6d4a6SDavid van Moolenbroek static struct buf **dirty; 823433d6423SLionel Sambuc static unsigned int dirtylistsize = 0; 824433d6423SLionel Sambuc int ndirty; 825433d6423SLionel Sambuc 826433d6423SLionel Sambuc if(dirtylistsize != nr_bufs) { 827433d6423SLionel Sambuc if(dirtylistsize > 0) { 828433d6423SLionel Sambuc assert(dirty != NULL); 829433d6423SLionel Sambuc free(dirty); 830433d6423SLionel Sambuc } 831433d6423SLionel Sambuc if(!(dirty = malloc(sizeof(dirty[0])*nr_bufs))) 832433d6423SLionel Sambuc panic("couldn't allocate dirty buf list"); 833433d6423SLionel Sambuc dirtylistsize = nr_bufs; 834433d6423SLionel Sambuc } 835433d6423SLionel Sambuc 836433d6423SLionel Sambuc for (bp = &buf[0], ndirty = 0; bp < &buf[nr_bufs]; bp++) { 837b8f6d4a6SDavid van Moolenbroek /* Do not flush dirty blocks that are in use (lmfs_count>0): the file 838b8f6d4a6SDavid van Moolenbroek * system may mark the block as dirty before changing its contents, in 839b8f6d4a6SDavid van Moolenbroek * which case the new contents could end up being lost. 840b8f6d4a6SDavid van Moolenbroek */ 841b8f6d4a6SDavid van Moolenbroek if (!lmfs_isclean(bp) && bp->lmfs_dev == dev && bp->lmfs_count == 0) { 842433d6423SLionel Sambuc dirty[ndirty++] = bp; 843433d6423SLionel Sambuc } 844433d6423SLionel Sambuc } 845433d6423SLionel Sambuc 846433d6423SLionel Sambuc lmfs_rw_scattered(dev, dirty, ndirty, WRITING); 847433d6423SLionel Sambuc } 848433d6423SLionel Sambuc 849433d6423SLionel Sambuc /*===========================================================================* 850433d6423SLionel Sambuc * lmfs_rw_scattered * 851433d6423SLionel Sambuc *===========================================================================*/ 852433d6423SLionel Sambuc void lmfs_rw_scattered( 853433d6423SLionel Sambuc dev_t dev, /* major-minor device number */ 854433d6423SLionel Sambuc struct buf **bufq, /* pointer to array of buffers */ 855433d6423SLionel Sambuc int bufqsize, /* number of buffers */ 856433d6423SLionel Sambuc int rw_flag /* READING or WRITING */ 857433d6423SLionel Sambuc ) 858433d6423SLionel Sambuc { 859433d6423SLionel Sambuc /* Read or write scattered data from a device. */ 860433d6423SLionel Sambuc 861433d6423SLionel Sambuc register struct buf *bp; 862433d6423SLionel Sambuc int gap; 863433d6423SLionel Sambuc register int i; 864433d6423SLionel Sambuc register iovec_t *iop; 865433d6423SLionel Sambuc static iovec_t iovec[NR_IOREQS]; 866433d6423SLionel Sambuc off_t pos; 867433d6423SLionel Sambuc int iov_per_block; 86865f76edbSDavid van Moolenbroek unsigned int start_in_use = bufs_in_use, start_bufqsize = bufqsize; 869433d6423SLionel Sambuc 870433d6423SLionel Sambuc assert(bufqsize >= 0); 871433d6423SLionel Sambuc if(bufqsize == 0) return; 872433d6423SLionel Sambuc 873433d6423SLionel Sambuc /* for READING, check all buffers on the list are obtained and held 874433d6423SLionel Sambuc * (count > 0) 875433d6423SLionel Sambuc */ 876433d6423SLionel Sambuc if (rw_flag == READING) { 877433d6423SLionel Sambuc for(i = 0; i < bufqsize; i++) { 878433d6423SLionel Sambuc assert(bufq[i] != NULL); 879433d6423SLionel Sambuc assert(bufq[i]->lmfs_count > 0); 880433d6423SLionel Sambuc } 881433d6423SLionel Sambuc 882433d6423SLionel Sambuc /* therefore they are all 'in use' and must be at least this many */ 883433d6423SLionel Sambuc assert(start_in_use >= start_bufqsize); 884433d6423SLionel Sambuc } 885433d6423SLionel Sambuc 886433d6423SLionel Sambuc assert(dev != NO_DEV); 887433d6423SLionel Sambuc assert(fs_block_size > 0); 888*6c46a77dSDavid van Moolenbroek assert(howmany(fs_block_size, PAGE_SIZE) <= NR_IOREQS); 889433d6423SLionel Sambuc 890433d6423SLionel Sambuc /* (Shell) sort buffers on lmfs_blocknr. */ 891433d6423SLionel Sambuc gap = 1; 892433d6423SLionel Sambuc do 893433d6423SLionel Sambuc gap = 3 * gap + 1; 894433d6423SLionel Sambuc while (gap <= bufqsize); 895433d6423SLionel Sambuc while (gap != 1) { 896433d6423SLionel Sambuc int j; 897433d6423SLionel Sambuc gap /= 3; 898433d6423SLionel Sambuc for (j = gap; j < bufqsize; j++) { 899433d6423SLionel Sambuc for (i = j - gap; 900433d6423SLionel Sambuc i >= 0 && bufq[i]->lmfs_blocknr > bufq[i + gap]->lmfs_blocknr; 901433d6423SLionel Sambuc i -= gap) { 902433d6423SLionel Sambuc bp = bufq[i]; 903433d6423SLionel Sambuc bufq[i] = bufq[i + gap]; 904433d6423SLionel Sambuc bufq[i + gap] = bp; 905433d6423SLionel Sambuc } 906433d6423SLionel Sambuc } 907433d6423SLionel Sambuc } 908433d6423SLionel Sambuc 909433d6423SLionel Sambuc /* Set up I/O vector and do I/O. The result of bdev I/O is OK if everything 910433d6423SLionel Sambuc * went fine, otherwise the error code for the first failed transfer. 911433d6423SLionel Sambuc */ 912433d6423SLionel Sambuc while (bufqsize > 0) { 913433d6423SLionel Sambuc int nblocks = 0, niovecs = 0; 914433d6423SLionel Sambuc int r; 915433d6423SLionel Sambuc for (iop = iovec; nblocks < bufqsize; nblocks++) { 916433d6423SLionel Sambuc int p; 917433d6423SLionel Sambuc vir_bytes vdata, blockrem; 918433d6423SLionel Sambuc bp = bufq[nblocks]; 919b65ad59eSDavid van Moolenbroek if (bp->lmfs_blocknr != bufq[0]->lmfs_blocknr + nblocks) 920433d6423SLionel Sambuc break; 921*6c46a77dSDavid van Moolenbroek blockrem = bp->lmfs_bytes; 922*6c46a77dSDavid van Moolenbroek iov_per_block = howmany(blockrem, PAGE_SIZE); 923433d6423SLionel Sambuc if(niovecs >= NR_IOREQS-iov_per_block) break; 924433d6423SLionel Sambuc vdata = (vir_bytes) bp->data; 925433d6423SLionel Sambuc for(p = 0; p < iov_per_block; p++) { 926*6c46a77dSDavid van Moolenbroek vir_bytes chunk = 927*6c46a77dSDavid van Moolenbroek blockrem < PAGE_SIZE ? blockrem : PAGE_SIZE; 928433d6423SLionel Sambuc iop->iov_addr = vdata; 929433d6423SLionel Sambuc iop->iov_size = chunk; 930433d6423SLionel Sambuc vdata += PAGE_SIZE; 931433d6423SLionel Sambuc blockrem -= chunk; 932433d6423SLionel Sambuc iop++; 933433d6423SLionel Sambuc niovecs++; 934433d6423SLionel Sambuc } 935433d6423SLionel Sambuc assert(p == iov_per_block); 936433d6423SLionel Sambuc assert(blockrem == 0); 937433d6423SLionel Sambuc } 938433d6423SLionel Sambuc 939433d6423SLionel Sambuc assert(nblocks > 0); 940433d6423SLionel Sambuc assert(niovecs > 0); 941433d6423SLionel Sambuc 942433d6423SLionel Sambuc pos = (off_t)bufq[0]->lmfs_blocknr * fs_block_size; 943433d6423SLionel Sambuc if (rw_flag == READING) 944433d6423SLionel Sambuc r = bdev_gather(dev, pos, iovec, niovecs, BDEV_NOFLAGS); 945433d6423SLionel Sambuc else 946433d6423SLionel Sambuc r = bdev_scatter(dev, pos, iovec, niovecs, BDEV_NOFLAGS); 947433d6423SLionel Sambuc 948433d6423SLionel Sambuc /* Harvest the results. The driver may have returned an error, or it 949433d6423SLionel Sambuc * may have done less than what we asked for. 950433d6423SLionel Sambuc */ 951433d6423SLionel Sambuc if (r < 0) { 952b65ad59eSDavid van Moolenbroek printf("fs cache: I/O error %d on device %d/%d, " 953b65ad59eSDavid van Moolenbroek "block %"PRIu64"\n", 954433d6423SLionel Sambuc r, major(dev), minor(dev), bufq[0]->lmfs_blocknr); 955433d6423SLionel Sambuc } 956433d6423SLionel Sambuc for (i = 0; i < nblocks; i++) { 957433d6423SLionel Sambuc bp = bufq[i]; 958*6c46a77dSDavid van Moolenbroek if (r < (ssize_t)bp->lmfs_bytes) { 959433d6423SLionel Sambuc /* Transfer failed. */ 960433d6423SLionel Sambuc if (i == 0) { 961433d6423SLionel Sambuc bp->lmfs_dev = NO_DEV; /* Invalidate block */ 962433d6423SLionel Sambuc } 963433d6423SLionel Sambuc break; 964433d6423SLionel Sambuc } 965433d6423SLionel Sambuc if (rw_flag == READING) { 966433d6423SLionel Sambuc bp->lmfs_dev = dev; /* validate block */ 9670314acfbSDavid van Moolenbroek lmfs_put_block(bp); 968433d6423SLionel Sambuc } else { 969433d6423SLionel Sambuc MARKCLEAN(bp); 970433d6423SLionel Sambuc } 971*6c46a77dSDavid van Moolenbroek r -= bp->lmfs_bytes; 972433d6423SLionel Sambuc } 973433d6423SLionel Sambuc 974433d6423SLionel Sambuc bufq += i; 975433d6423SLionel Sambuc bufqsize -= i; 976433d6423SLionel Sambuc 977433d6423SLionel Sambuc if (rw_flag == READING) { 978433d6423SLionel Sambuc /* Don't bother reading more than the device is willing to 979433d6423SLionel Sambuc * give at this time. Don't forget to release those extras. 980433d6423SLionel Sambuc */ 981433d6423SLionel Sambuc while (bufqsize > 0) { 9820314acfbSDavid van Moolenbroek lmfs_put_block(*bufq++); 983433d6423SLionel Sambuc bufqsize--; 984433d6423SLionel Sambuc } 985433d6423SLionel Sambuc } 986433d6423SLionel Sambuc if (rw_flag == WRITING && i == 0) { 987433d6423SLionel Sambuc /* We're not making progress, this means we might keep 988433d6423SLionel Sambuc * looping. Buffers remain dirty if un-written. Buffers are 989433d6423SLionel Sambuc * lost if invalidate()d or LRU-removed while dirty. This 990433d6423SLionel Sambuc * is better than keeping unwritable blocks around forever.. 991433d6423SLionel Sambuc */ 992433d6423SLionel Sambuc break; 993433d6423SLionel Sambuc } 994433d6423SLionel Sambuc } 995433d6423SLionel Sambuc 996433d6423SLionel Sambuc if(rw_flag == READING) { 997433d6423SLionel Sambuc assert(start_in_use >= start_bufqsize); 998433d6423SLionel Sambuc 999433d6423SLionel Sambuc /* READING callers assume all bufs are released. */ 1000433d6423SLionel Sambuc assert(start_in_use - start_bufqsize == bufs_in_use); 1001433d6423SLionel Sambuc } 1002433d6423SLionel Sambuc } 1003433d6423SLionel Sambuc 1004433d6423SLionel Sambuc /*===========================================================================* 1005433d6423SLionel Sambuc * rm_lru * 1006433d6423SLionel Sambuc *===========================================================================*/ 1007433d6423SLionel Sambuc static void rm_lru(struct buf *bp) 1008433d6423SLionel Sambuc { 1009433d6423SLionel Sambuc /* Remove a block from its LRU chain. */ 1010433d6423SLionel Sambuc struct buf *next_ptr, *prev_ptr; 1011433d6423SLionel Sambuc 1012433d6423SLionel Sambuc next_ptr = bp->lmfs_next; /* successor on LRU chain */ 1013433d6423SLionel Sambuc prev_ptr = bp->lmfs_prev; /* predecessor on LRU chain */ 1014433d6423SLionel Sambuc if (prev_ptr != NULL) 1015433d6423SLionel Sambuc prev_ptr->lmfs_next = next_ptr; 1016433d6423SLionel Sambuc else 1017433d6423SLionel Sambuc front = next_ptr; /* this block was at front of chain */ 1018433d6423SLionel Sambuc 1019433d6423SLionel Sambuc if (next_ptr != NULL) 1020433d6423SLionel Sambuc next_ptr->lmfs_prev = prev_ptr; 1021433d6423SLionel Sambuc else 1022433d6423SLionel Sambuc rear = prev_ptr; /* this block was at rear of chain */ 1023433d6423SLionel Sambuc } 1024433d6423SLionel Sambuc 1025433d6423SLionel Sambuc /*===========================================================================* 1026433d6423SLionel Sambuc * cache_resize * 1027433d6423SLionel Sambuc *===========================================================================*/ 10281311233cSDavid van Moolenbroek static void cache_resize(size_t blocksize, unsigned int bufs) 1029433d6423SLionel Sambuc { 1030433d6423SLionel Sambuc struct buf *bp; 1031433d6423SLionel Sambuc 1032433d6423SLionel Sambuc assert(blocksize > 0); 1033433d6423SLionel Sambuc assert(bufs >= MINBUFS); 1034433d6423SLionel Sambuc 1035433d6423SLionel Sambuc for (bp = &buf[0]; bp < &buf[nr_bufs]; bp++) 1036433d6423SLionel Sambuc if(bp->lmfs_count != 0) panic("change blocksize with buffer in use"); 1037433d6423SLionel Sambuc 1038433d6423SLionel Sambuc lmfs_buf_pool(bufs); 1039433d6423SLionel Sambuc 1040433d6423SLionel Sambuc fs_block_size = blocksize; 1041433d6423SLionel Sambuc } 1042433d6423SLionel Sambuc 10430314acfbSDavid van Moolenbroek static void cache_heuristic_check(void) 1044433d6423SLionel Sambuc { 1045433d6423SLionel Sambuc int bufs, d; 1046433d6423SLionel Sambuc 10471311233cSDavid van Moolenbroek bufs = fs_bufs_heuristic(MINBUFS, fs_btotal, fs_bused, fs_block_size); 1048433d6423SLionel Sambuc 1049433d6423SLionel Sambuc /* set the cache to the new heuristic size if the new one 1050433d6423SLionel Sambuc * is more than 10% off from the current one. 1051433d6423SLionel Sambuc */ 1052433d6423SLionel Sambuc d = bufs-nr_bufs; 1053433d6423SLionel Sambuc if(d < 0) d = -d; 1054433d6423SLionel Sambuc if(d*100/nr_bufs > 10) { 1055433d6423SLionel Sambuc cache_resize(fs_block_size, bufs); 1056433d6423SLionel Sambuc } 1057433d6423SLionel Sambuc } 1058433d6423SLionel Sambuc 1059433d6423SLionel Sambuc /*===========================================================================* 1060433d6423SLionel Sambuc * lmfs_set_blocksize * 1061433d6423SLionel Sambuc *===========================================================================*/ 10621311233cSDavid van Moolenbroek void lmfs_set_blocksize(size_t new_block_size) 1063433d6423SLionel Sambuc { 1064433d6423SLionel Sambuc cache_resize(new_block_size, MINBUFS); 10650314acfbSDavid van Moolenbroek cache_heuristic_check(); 1066433d6423SLionel Sambuc 1067433d6423SLionel Sambuc /* Decide whether to use seconday cache or not. 10680314acfbSDavid van Moolenbroek * Only do this if the block size is a multiple of the page size, and using 10690314acfbSDavid van Moolenbroek * the VM cache has been enabled for this FS. 1070433d6423SLionel Sambuc */ 1071433d6423SLionel Sambuc 1072433d6423SLionel Sambuc vmcache = 0; 1073433d6423SLionel Sambuc 1074433d6423SLionel Sambuc if(may_use_vmcache && !(new_block_size % PAGE_SIZE)) 1075433d6423SLionel Sambuc vmcache = 1; 1076433d6423SLionel Sambuc } 1077433d6423SLionel Sambuc 1078433d6423SLionel Sambuc /*===========================================================================* 1079433d6423SLionel Sambuc * lmfs_buf_pool * 1080433d6423SLionel Sambuc *===========================================================================*/ 1081433d6423SLionel Sambuc void lmfs_buf_pool(int new_nr_bufs) 1082433d6423SLionel Sambuc { 1083433d6423SLionel Sambuc /* Initialize the buffer pool. */ 1084433d6423SLionel Sambuc register struct buf *bp; 1085433d6423SLionel Sambuc 1086433d6423SLionel Sambuc assert(new_nr_bufs >= MINBUFS); 1087433d6423SLionel Sambuc 1088433d6423SLionel Sambuc if(nr_bufs > 0) { 1089433d6423SLionel Sambuc assert(buf); 1090c5beebb6SDavid van Moolenbroek lmfs_flushall(); 1091433d6423SLionel Sambuc for (bp = &buf[0]; bp < &buf[nr_bufs]; bp++) { 1092433d6423SLionel Sambuc if(bp->data) { 1093433d6423SLionel Sambuc assert(bp->lmfs_bytes > 0); 1094433d6423SLionel Sambuc munmap_t(bp->data, bp->lmfs_bytes); 1095433d6423SLionel Sambuc } 1096433d6423SLionel Sambuc } 1097433d6423SLionel Sambuc } 1098433d6423SLionel Sambuc 1099433d6423SLionel Sambuc if(buf) 1100433d6423SLionel Sambuc free(buf); 1101433d6423SLionel Sambuc 1102433d6423SLionel Sambuc if(!(buf = calloc(sizeof(buf[0]), new_nr_bufs))) 1103433d6423SLionel Sambuc panic("couldn't allocate buf list (%d)", new_nr_bufs); 1104433d6423SLionel Sambuc 1105433d6423SLionel Sambuc if(buf_hash) 1106433d6423SLionel Sambuc free(buf_hash); 1107433d6423SLionel Sambuc if(!(buf_hash = calloc(sizeof(buf_hash[0]), new_nr_bufs))) 1108433d6423SLionel Sambuc panic("couldn't allocate buf hash list (%d)", new_nr_bufs); 1109433d6423SLionel Sambuc 1110433d6423SLionel Sambuc nr_bufs = new_nr_bufs; 1111433d6423SLionel Sambuc 1112433d6423SLionel Sambuc bufs_in_use = 0; 1113433d6423SLionel Sambuc front = &buf[0]; 1114433d6423SLionel Sambuc rear = &buf[nr_bufs - 1]; 1115433d6423SLionel Sambuc 1116433d6423SLionel Sambuc for (bp = &buf[0]; bp < &buf[nr_bufs]; bp++) { 1117433d6423SLionel Sambuc bp->lmfs_blocknr = NO_BLOCK; 1118433d6423SLionel Sambuc bp->lmfs_dev = NO_DEV; 1119433d6423SLionel Sambuc bp->lmfs_next = bp + 1; 1120433d6423SLionel Sambuc bp->lmfs_prev = bp - 1; 1121433d6423SLionel Sambuc bp->data = NULL; 1122433d6423SLionel Sambuc bp->lmfs_bytes = 0; 1123433d6423SLionel Sambuc } 1124433d6423SLionel Sambuc front->lmfs_prev = NULL; 1125433d6423SLionel Sambuc rear->lmfs_next = NULL; 1126433d6423SLionel Sambuc 1127433d6423SLionel Sambuc for (bp = &buf[0]; bp < &buf[nr_bufs]; bp++) bp->lmfs_hash = bp->lmfs_next; 1128433d6423SLionel Sambuc buf_hash[0] = front; 1129433d6423SLionel Sambuc } 1130433d6423SLionel Sambuc 1131433d6423SLionel Sambuc int lmfs_bufs_in_use(void) 1132433d6423SLionel Sambuc { 1133433d6423SLionel Sambuc return bufs_in_use; 1134433d6423SLionel Sambuc } 1135433d6423SLionel Sambuc 1136433d6423SLionel Sambuc int lmfs_nr_bufs(void) 1137433d6423SLionel Sambuc { 1138433d6423SLionel Sambuc return nr_bufs; 1139433d6423SLionel Sambuc } 1140433d6423SLionel Sambuc 1141433d6423SLionel Sambuc void lmfs_flushall(void) 1142433d6423SLionel Sambuc { 1143433d6423SLionel Sambuc struct buf *bp; 1144433d6423SLionel Sambuc for(bp = &buf[0]; bp < &buf[nr_bufs]; bp++) 1145433d6423SLionel Sambuc if(bp->lmfs_dev != NO_DEV && !lmfs_isclean(bp)) 1146ebd3c067SDavid van Moolenbroek lmfs_flushdev(bp->lmfs_dev); 11471311233cSDavid van Moolenbroek 11481311233cSDavid van Moolenbroek /* This is the moment where it is least likely (although certainly not 11491311233cSDavid van Moolenbroek * impossible!) that there are buffers in use, since buffers should not 11501311233cSDavid van Moolenbroek * be held across file system syncs. See if we already intended to 11511311233cSDavid van Moolenbroek * resize the buffer cache, but couldn't. Be aware that we may be 11521311233cSDavid van Moolenbroek * called indirectly from within lmfs_change_blockusage(), so care must 11531311233cSDavid van Moolenbroek * be taken not to recurse infinitely. TODO: see if it is better to 11541311233cSDavid van Moolenbroek * resize the cache from here *only*, thus guaranteeing a clean cache. 11551311233cSDavid van Moolenbroek */ 11561311233cSDavid van Moolenbroek lmfs_change_blockusage(0); 1157433d6423SLionel Sambuc } 1158433d6423SLionel Sambuc 11591311233cSDavid van Moolenbroek size_t lmfs_fs_block_size(void) 1160433d6423SLionel Sambuc { 1161433d6423SLionel Sambuc return fs_block_size; 1162433d6423SLionel Sambuc } 1163433d6423SLionel Sambuc 1164433d6423SLionel Sambuc void lmfs_may_use_vmcache(int ok) 1165433d6423SLionel Sambuc { 1166433d6423SLionel Sambuc may_use_vmcache = ok; 1167433d6423SLionel Sambuc } 1168