From 1311233cfbe0c8d7c7a17bdf5274fbcdd76d7702 Mon Sep 17 00:00:00 2001 From: David van Moolenbroek Date: Sun, 29 Mar 2015 15:26:23 +0000 Subject: [PATCH] libminixfs: keep track of block usage This patch changes the libminixfs API and implementation such that the library is at all times aware of how many total and used blocks there are in the file system. This removes the last upcall of libminixfs into file systems (fs_blockstats). In the process, make this part of the libminixfs API a little prettier and more robust. Change file systems accordingly. Since this change only adds to MFS being unable to deal with zones and blocks having different sizes, fail to mount such file systems immediately rather than triggering an assert later. Change-Id: I078e589c7e1be1fa691cf391bf5dfddd1baf2c86 --- minix/fs/ext2/balloc.c | 6 +-- minix/fs/ext2/mount.c | 2 + minix/fs/ext2/stadir.c | 12 ----- minix/fs/isofs/stadir.c | 6 --- minix/fs/isofs/super.c | 2 + minix/fs/mfs/glo.h | 2 + minix/fs/mfs/mount.c | 10 +++- minix/fs/mfs/proto.h | 1 - minix/fs/mfs/stadir.c | 3 +- minix/fs/mfs/stats.c | 19 ------- minix/fs/mfs/super.c | 30 +++++------ minix/include/minix/libminixfs.h | 11 ++-- minix/lib/libminixfs/cache.c | 88 +++++++++++++++++++++++--------- minix/tests/test72.c | 6 --- 14 files changed, 99 insertions(+), 99 deletions(-) diff --git a/minix/fs/ext2/balloc.c b/minix/fs/ext2/balloc.c index fd1e18505..e0d575203 100644 --- a/minix/fs/ext2/balloc.c +++ b/minix/fs/ext2/balloc.c @@ -232,7 +232,7 @@ struct inode *rip; /* used for preallocation */ gd->free_blocks_count -= EXT2_PREALLOC_BLOCKS; sp->s_free_blocks_count -= EXT2_PREALLOC_BLOCKS; - lmfs_blockschange(-EXT2_PREALLOC_BLOCKS); + lmfs_change_blockusage(EXT2_PREALLOC_BLOCKS); group_descriptors_dirty = 1; return block; } @@ -257,7 +257,7 @@ struct inode *rip; /* used for preallocation */ gd->free_blocks_count--; sp->s_free_blocks_count--; - lmfs_blockschange(-1); + lmfs_change_blockusage(1); group_descriptors_dirty = 1; if (update_bsearch && block != -1 && block != NO_BLOCK) { @@ -323,7 +323,7 @@ void free_block(struct super_block *sp, bit_t bit_returned) gd->free_blocks_count++; sp->s_free_blocks_count++; - lmfs_blockschange(1); + lmfs_change_blockusage(-1); group_descriptors_dirty = 1; diff --git a/minix/fs/ext2/mount.c b/minix/fs/ext2/mount.c index f2c619904..790e55970 100644 --- a/minix/fs/ext2/mount.c +++ b/minix/fs/ext2/mount.c @@ -89,6 +89,8 @@ int fs_mount(dev_t dev, unsigned int flags, struct fsdriver_node *root_node, } lmfs_set_blocksize(superblock->s_block_size); + lmfs_set_blockusage(superblock->s_blocks_count, + superblock->s_blocks_count - superblock->s_free_blocks_count); /* Get the root inode of the mounted file system. */ if ( (root_ip = get_inode(fs_dev, ROOT_INODE)) == NULL) { diff --git a/minix/fs/ext2/stadir.c b/minix/fs/ext2/stadir.c index 233705fcb..56bbdfae6 100644 --- a/minix/fs/ext2/stadir.c +++ b/minix/fs/ext2/stadir.c @@ -71,15 +71,3 @@ int fs_statvfs(struct statvfs *st) return(OK); } - -/*===========================================================================* - * blockstats * - *===========================================================================*/ -void fs_blockstats(u64_t *blocks, u64_t *free) -{ - struct super_block *sp = get_super(fs_dev); - - *blocks = sp->s_blocks_count; - *free = sp->s_free_blocks_count; -} - diff --git a/minix/fs/isofs/stadir.c b/minix/fs/isofs/stadir.c index 741f0b9d4..6825b1031 100644 --- a/minix/fs/isofs/stadir.c +++ b/minix/fs/isofs/stadir.c @@ -25,9 +25,3 @@ int fs_statvfs(struct statvfs *st) return OK; } - -void fs_blockstats(u64_t *blocks, u64_t *free) -{ - *blocks = v_pri.volume_space_size_l; - *free = 0; -} diff --git a/minix/fs/isofs/super.c b/minix/fs/isofs/super.c index 83d63ec8f..b8d7aa5a9 100644 --- a/minix/fs/isofs/super.c +++ b/minix/fs/isofs/super.c @@ -46,6 +46,8 @@ static int create_vol_pri_desc(struct iso9660_vol_pri_desc *vol_pri, char *buf, return EINVAL; lmfs_set_blocksize(vol_pri->logical_block_size_l); + lmfs_set_blockusage(vol_pri->volume_space_size_l, + vol_pri->volume_space_size_l); /* Read root directory record. */ root_record = (struct iso9660_dir_record *)vol_pri->root_directory; diff --git a/minix/fs/mfs/glo.h b/minix/fs/mfs/glo.h index 8e50d387d..175349230 100644 --- a/minix/fs/mfs/glo.h +++ b/minix/fs/mfs/glo.h @@ -15,6 +15,8 @@ EXTERN int cch[NR_INODES]; EXTERN dev_t fs_dev; /* The device that is handled by this FS proc. */ +EXTERN zone_t used_zones; + extern struct fsdriver mfs_table; #endif diff --git a/minix/fs/mfs/mount.c b/minix/fs/mfs/mount.c index 4bb751d39..3df824e6b 100644 --- a/minix/fs/mfs/mount.c +++ b/minix/fs/mfs/mount.c @@ -48,8 +48,16 @@ int fs_mount(dev_t dev, unsigned int flags, struct fsdriver_node *root_node, } printf("MFS: WARNING: FS 0x%llx unclean, mounting readonly\n", fs_dev); } - + lmfs_set_blocksize(superblock.s_block_size); + + /* Compute the current number of used zones, and report it to libminixfs. + * Note that libminixfs really wants numbers of *blocks*, but this MFS + * implementation dropped support for differing zone/block sizes a while ago. + */ + used_zones = superblock.s_zones - count_free_bits(&superblock, ZMAP); + + lmfs_set_blockusage(superblock.s_zones, used_zones); /* Get the root inode of the mounted file system. */ if( (root_ip = get_inode(fs_dev, ROOT_INODE)) == NULL) { diff --git a/minix/fs/mfs/proto.h b/minix/fs/mfs/proto.h index 10f6707dc..92a4ad638 100644 --- a/minix/fs/mfs/proto.h +++ b/minix/fs/mfs/proto.h @@ -87,7 +87,6 @@ unsigned int get_block_size(dev_t dev); struct super_block *get_super(dev_t dev); int read_super(struct super_block *sp); int write_super(struct super_block *sp); -u32_t get_used_blocks(struct super_block *sp); /* stats.c */ bit_t count_free_bits(struct super_block *sp, int map); diff --git a/minix/fs/mfs/stadir.c b/minix/fs/mfs/stadir.c index 765e0ea87..2e53ee959 100644 --- a/minix/fs/mfs/stadir.c +++ b/minix/fs/mfs/stadir.c @@ -89,7 +89,8 @@ int fs_statvfs(struct statvfs *st) scale = sp->s_log_zone_size; - fs_blockstats(&st->f_blocks, &st->f_bfree); + st->f_blocks = sp->s_zones; + st->f_bfree = sp->s_zones - used_zones; st->f_bavail = st->f_bfree; st->f_bsize = sp->s_block_size << scale; diff --git a/minix/fs/mfs/stats.c b/minix/fs/mfs/stats.c index d45b92c3f..cffa10b7e 100644 --- a/minix/fs/mfs/stats.c +++ b/minix/fs/mfs/stats.c @@ -87,22 +87,3 @@ int map; /* IMAP (inode map) or ZMAP (zone map) */ } while (--bcount > 0); return free_bits; } - - -/*===========================================================================* - * blockstats * - *===========================================================================*/ -void fs_blockstats(u64_t *blocks, u64_t *free) -{ - struct super_block *sp; - - sp = get_super(fs_dev); - - assert(sp); - assert(!sp->s_log_zone_size); - - *blocks = sp->s_zones; - *free = *blocks - get_used_blocks(sp); - - return; -} diff --git a/minix/fs/mfs/super.c b/minix/fs/mfs/super.c index f50df8425..f5e736a4b 100644 --- a/minix/fs/mfs/super.c +++ b/minix/fs/mfs/super.c @@ -24,8 +24,6 @@ #include "super.h" #include "const.h" -static u32_t used_blocks = 0; - /*===========================================================================* * alloc_bit * *===========================================================================*/ @@ -95,8 +93,8 @@ bit_t origin; /* number of bit to start searching at */ MARKDIRTY(bp); put_block(bp); if(map == ZMAP) { - used_blocks++; - lmfs_blockschange(1); + used_zones++; + lmfs_change_blockusage(1); } return(b); } @@ -153,8 +151,8 @@ bit_t bit_returned; /* number of bit to insert into the map */ put_block(bp); if(map == ZMAP) { - used_blocks--; - lmfs_blockschange(-1); + used_zones--; + lmfs_change_blockusage(-1); } } @@ -281,6 +279,14 @@ int read_super(struct super_block *sp) sp->s_max_size = (off_t) conv4(native, sp->s_max_size); sp->s_zones = (zone_t)conv4(native, sp->s_zones); + /* Zones consisting of multiple blocks are longer supported, so fail as early + * as possible. There is still a lot of code cleanup to do here, though. + */ + if (sp->s_log_zone_size != 0) { + printf("MFS: block and zone sizes are different\n"); + return EINVAL; + } + /* Calculate some other numbers that depend on the version here too, to * hide some of the differences. */ @@ -364,15 +370,3 @@ int write_super(struct super_block *sp) panic("can't write superblock of readonly filesystem"); return rw_super(sp, 1); } - -static int blocks_known = 0; - -u32_t get_used_blocks(struct super_block *sp) -{ - if(!blocks_known) { - /* how many blocks are in use? */ - used_blocks = sp->s_zones - count_free_bits(sp, ZMAP); - blocks_known = 1; - } - return used_blocks; -} diff --git a/minix/include/minix/libminixfs.h b/minix/include/minix/libminixfs.h index e6094515a..44aa80e96 100644 --- a/minix/include/minix/libminixfs.h +++ b/minix/include/minix/libminixfs.h @@ -35,9 +35,9 @@ int lmfs_bufs_in_use(void); int lmfs_nr_bufs(void); void lmfs_flushall(void); void lmfs_flushdev(dev_t dev); -int lmfs_fs_block_size(void); +size_t lmfs_fs_block_size(void); void lmfs_may_use_vmcache(int); -void lmfs_set_blocksize(int blocksize); +void lmfs_set_blocksize(size_t blocksize); void lmfs_reset_rdwt_err(void); int lmfs_rdwt_err(void); void lmfs_buf_pool(int new_nr_bufs); @@ -50,11 +50,8 @@ void lmfs_zero_block_ino(dev_t dev, ino_t ino, u64_t off); void lmfs_invalidate(dev_t device); void lmfs_rw_scattered(dev_t, struct buf **, int, int); void lmfs_setquiet(int q); -void lmfs_cache_reevaluate(void); -void lmfs_blockschange(int delta); - -/* calls that libminixfs does into fs */ -void fs_blockstats(u64_t *blocks, u64_t *free); +void lmfs_set_blockusage(fsblkcnt_t btotal, fsblkcnt_t bused); +void lmfs_change_blockusage(int delta); /* get_block arguments */ #define NORMAL 0 /* forces get_block to do disk read */ diff --git a/minix/lib/libminixfs/cache.c b/minix/lib/libminixfs/cache.c index 3b1aa0dce..e7a9fa98c 100644 --- a/minix/lib/libminixfs/cache.c +++ b/minix/lib/libminixfs/cache.c @@ -58,22 +58,21 @@ static int may_use_vmcache; static size_t fs_block_size = PAGE_SIZE; /* raw i/o block size */ +static fsblkcnt_t fs_btotal = 0, fs_bused = 0; + static int rdwt_err; static int quiet = 0; void lmfs_setquiet(int q) { quiet = q; } -static u32_t fs_bufs_heuristic(int minbufs, u32_t btotal, u64_t bfree, - int blocksize) +static int fs_bufs_heuristic(int minbufs, fsblkcnt_t btotal, + fsblkcnt_t bused, int blocksize) { struct vm_stats_info vsi; int bufs; u32_t kbytes_used_fs, kbytes_total_fs, kbcache, kb_fsmax; u32_t kbytes_remain_mem; - u64_t bused; - - bused = btotal-bfree; /* set a reasonable cache size; cache at most a certain * portion of the used FS, and at most a certain %age of remaining @@ -113,21 +112,49 @@ static u32_t fs_bufs_heuristic(int minbufs, u32_t btotal, u64_t bfree, return bufs; } -void lmfs_blockschange(int delta) +void lmfs_change_blockusage(int delta) { /* Change the number of allocated blocks by 'delta.' * Also accumulate the delta since the last cache re-evaluation. * If it is outside a certain band, ask the cache library to * re-evaluate the cache size. */ - static int bitdelta = 0; - bitdelta += delta; -#define BANDKB (10*1024) /* recheck cache every 10MB change */ - if(bitdelta*(int)fs_block_size/1024 > BANDKB || - bitdelta*(int)fs_block_size/1024 < -BANDKB) { - lmfs_cache_reevaluate(); - bitdelta = 0; - } + static int bitdelta = 0, warn_low = TRUE, warn_high = TRUE; + + /* Adjust the file system block usage counter accordingly. Do bounds + * checking, and report file system misbehavior. + */ + if (delta > 0 && (fsblkcnt_t)delta > fs_btotal - fs_bused) { + if (warn_high) { + printf("libminixfs: block usage overflow\n"); + warn_high = FALSE; + } + delta = (int)(fs_btotal - fs_bused); + } else if (delta < 0 && (fsblkcnt_t)-delta > fs_bused) { + if (warn_low) { + printf("libminixfs: block usage underflow\n"); + warn_low = FALSE; + } + delta = -(int)fs_bused; + } + fs_bused += delta; + + bitdelta += delta; + +#define BAND_KB (10*1024) /* recheck cache every 10MB change */ + + /* If the accumulated delta exceeds the configured threshold, resize + * the cache, but only if the cache isn't in use any more. In order to + * avoid that the latter case blocks a resize forever, we also call + * this function from lmfs_flushall(). Since lmfs_buf_pool() may call + * lmfs_flushall(), reset 'bitdelta' before doing the heuristics check. + */ + if (bufs_in_use == 0 && + (bitdelta*(int)fs_block_size/1024 > BAND_KB || + bitdelta*(int)fs_block_size/1024 < -BAND_KB)) { + bitdelta = 0; + cache_heuristic_check(); + } } void lmfs_markdirty(struct buf *bp) @@ -642,12 +669,16 @@ void lmfs_zero_block_ino(dev_t dev, ino_t ino, u64_t ino_off) put_block(bp, ONE_SHOT); } -void lmfs_cache_reevaluate(void) +void lmfs_set_blockusage(fsblkcnt_t btotal, fsblkcnt_t bused) { - if (bufs_in_use == 0) { - /* if the cache isn't in use any more, we could resize it. */ + + assert(bused <= btotal); + fs_btotal = btotal; + fs_bused = bused; + + /* if the cache isn't in use, we could resize it. */ + if (bufs_in_use == 0) cache_heuristic_check(); - } } /*===========================================================================* @@ -955,7 +986,7 @@ static void rm_lru(struct buf *bp) /*===========================================================================* * cache_resize * *===========================================================================*/ -static void cache_resize(unsigned int blocksize, unsigned int bufs) +static void cache_resize(size_t blocksize, unsigned int bufs) { struct buf *bp; @@ -973,11 +1004,8 @@ static void cache_resize(unsigned int blocksize, unsigned int bufs) static void cache_heuristic_check(void) { int bufs, d; - u64_t btotal, bfree; - fs_blockstats(&btotal, &bfree); - - bufs = fs_bufs_heuristic(10, btotal, bfree, fs_block_size); + bufs = fs_bufs_heuristic(MINBUFS, fs_btotal, fs_bused, fs_block_size); /* set the cache to the new heuristic size if the new one * is more than 10% off from the current one. @@ -992,7 +1020,7 @@ static void cache_heuristic_check(void) /*===========================================================================* * lmfs_set_blocksize * *===========================================================================*/ -void lmfs_set_blocksize(int new_block_size) +void lmfs_set_blocksize(size_t new_block_size) { cache_resize(new_block_size, MINBUFS); cache_heuristic_check(); @@ -1077,9 +1105,19 @@ void lmfs_flushall(void) for(bp = &buf[0]; bp < &buf[nr_bufs]; bp++) if(bp->lmfs_dev != NO_DEV && !lmfs_isclean(bp)) lmfs_flushdev(bp->lmfs_dev); + + /* This is the moment where it is least likely (although certainly not + * impossible!) that there are buffers in use, since buffers should not + * be held across file system syncs. See if we already intended to + * resize the buffer cache, but couldn't. Be aware that we may be + * called indirectly from within lmfs_change_blockusage(), so care must + * be taken not to recurse infinitely. TODO: see if it is better to + * resize the cache from here *only*, thus guaranteeing a clean cache. + */ + lmfs_change_blockusage(0); } -int lmfs_fs_block_size(void) +size_t lmfs_fs_block_size(void) { return fs_block_size; } diff --git a/minix/tests/test72.c b/minix/tests/test72.c index 70552a1c7..6c709692b 100644 --- a/minix/tests/test72.c +++ b/minix/tests/test72.c @@ -90,12 +90,6 @@ void testend(void) /* Fake some libminixfs client functions */ -void -fs_blockstats(u64_t *total, u64_t *free) -{ - *total = *free = 0; -} - static void allocate(int b) { assert(curblocksize > 0);