aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ext4
diff options
context:
space:
mode:
authorKazuya Mio <k-mio@sx.jp.nec.com>2011-05-24 18:30:07 -0400
committerTheodore Ts'o <tytso@mit.edu>2011-05-24 18:30:07 -0400
commitd02a9391f79cab65cde74cd9e8ccd2290a565229 (patch)
treea70993ce201661a360ade69c3a5925b653e9c542 /fs/ext4
parent28739eea9cd42598b632972f5cc64a458c5d40b3 (diff)
ext4: ensure f_bfree returned by ext4_statfs() is non-negative
I found the issue that the number of free blocks went negative. # stat -f /mnt/mp1/ File: "/mnt/mp1/" ID: e175ccb83a872efe Namelen: 255 Type: ext2/ext3 Block size: 4096 Fundamental block size: 4096 Blocks: Total: 258022 Free: -15 Available: -13122 Inodes: Total: 65536 Free: 63029 f_bfree in struct statfs will go negative when the filesystem has few free blocks. Because the number of dirty blocks is bigger than the number of free blocks in the following two cases. CASE 1: ext4_da_writepages mpage_da_map_and_submit ext4_map_blocks ext4_ext_map_blocks ext4_mb_new_blocks ext4_mb_diskspace_used percpu_counter_sub(&sbi->s_freeblocks_counter, ac->ac_b_ex.fe_len); <--- interrupt statfs systemcall ---> ext4_da_update_reserve_space percpu_counter_sub(&sbi->s_dirtyblocks_counter, used + ei->i_allocated_meta_blocks); CASE 2: ext4_write_begin __block_write_begin ext4_map_blocks ext4_ext_map_blocks ext4_mb_new_blocks ext4_mb_diskspace_used percpu_counter_sub(&sbi->s_freeblocks_counter, ac->ac_b_ex.fe_len); <--- interrupt statfs systemcall ---> percpu_counter_sub(&sbi->s_dirtyblocks_counter, reserv_blks); To avoid the issue, this patch ensures that f_bfree is non-negative. Signed-off-by: Kazuya Mio <k-mio@sx.jp.nec.com>
Diffstat (limited to 'fs/ext4')
-rw-r--r--fs/ext4/super.c5
1 files changed, 4 insertions, 1 deletions
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 874dd25b748..266174b268c 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -4458,6 +4458,7 @@ static int ext4_statfs(struct dentry *dentry, struct kstatfs *buf)
4458 struct ext4_sb_info *sbi = EXT4_SB(sb); 4458 struct ext4_sb_info *sbi = EXT4_SB(sb);
4459 struct ext4_super_block *es = sbi->s_es; 4459 struct ext4_super_block *es = sbi->s_es;
4460 u64 fsid; 4460 u64 fsid;
4461 s64 bfree;
4461 4462
4462 if (test_opt(sb, MINIX_DF)) { 4463 if (test_opt(sb, MINIX_DF)) {
4463 sbi->s_overhead_last = 0; 4464 sbi->s_overhead_last = 0;
@@ -4501,8 +4502,10 @@ static int ext4_statfs(struct dentry *dentry, struct kstatfs *buf)
4501 buf->f_type = EXT4_SUPER_MAGIC; 4502 buf->f_type = EXT4_SUPER_MAGIC;
4502 buf->f_bsize = sb->s_blocksize; 4503 buf->f_bsize = sb->s_blocksize;
4503 buf->f_blocks = ext4_blocks_count(es) - sbi->s_overhead_last; 4504 buf->f_blocks = ext4_blocks_count(es) - sbi->s_overhead_last;
4504 buf->f_bfree = percpu_counter_sum_positive(&sbi->s_freeblocks_counter) - 4505 bfree = percpu_counter_sum_positive(&sbi->s_freeblocks_counter) -
4505 percpu_counter_sum_positive(&sbi->s_dirtyblocks_counter); 4506 percpu_counter_sum_positive(&sbi->s_dirtyblocks_counter);
4507 /* prevent underflow in case that few free space is available */
4508 buf->f_bfree = max_t(s64, bfree, 0);
4506 buf->f_bavail = buf->f_bfree - ext4_r_blocks_count(es); 4509 buf->f_bavail = buf->f_bfree - ext4_r_blocks_count(es);
4507 if (buf->f_bfree < ext4_r_blocks_count(es)) 4510 if (buf->f_bfree < ext4_r_blocks_count(es))
4508 buf->f_bavail = 0; 4511 buf->f_bavail = 0;