diff options
Diffstat (limited to 'fs/ext4/super.c')
| -rw-r--r-- | fs/ext4/super.c | 174 | 
1 files changed, 124 insertions, 50 deletions
diff --git a/fs/ext4/super.c b/fs/ext4/super.c index eb7aa3e4ef0..78b7ede2efa 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -3085,6 +3085,114 @@ static int set_journal_csum_feature_set(struct super_block *sb)  	return ret;  } +/* + * Note: calculating the overhead so we can be compatible with + * historical BSD practice is quite difficult in the face of + * clusters/bigalloc.  This is because multiple metadata blocks from + * different block group can end up in the same allocation cluster. + * Calculating the exact overhead in the face of clustered allocation + * requires either O(all block bitmaps) in memory or O(number of block + * groups**2) in time.  We will still calculate the superblock for + * older file systems --- and if we come across with a bigalloc file + * system with zero in s_overhead_clusters the estimate will be close to + * correct especially for very large cluster sizes --- but for newer + * file systems, it's better to calculate this figure once at mkfs + * time, and store it in the superblock.  If the superblock value is + * present (even for non-bigalloc file systems), we will use it. + */ +static int count_overhead(struct super_block *sb, ext4_group_t grp, +			  char *buf) +{ +	struct ext4_sb_info	*sbi = EXT4_SB(sb); +	struct ext4_group_desc	*gdp; +	ext4_fsblk_t		first_block, last_block, b; +	ext4_group_t		i, ngroups = ext4_get_groups_count(sb); +	int			s, j, count = 0; + +	first_block = le32_to_cpu(sbi->s_es->s_first_data_block) + +		(grp * EXT4_BLOCKS_PER_GROUP(sb)); +	last_block = first_block + EXT4_BLOCKS_PER_GROUP(sb) - 1; +	for (i = 0; i < ngroups; i++) { +		gdp = ext4_get_group_desc(sb, i, NULL); +		b = ext4_block_bitmap(sb, gdp); +		if (b >= first_block && b <= last_block) { +			ext4_set_bit(EXT4_B2C(sbi, b - first_block), buf); +			count++; +		} +		b = ext4_inode_bitmap(sb, gdp); +		if (b >= first_block && b <= last_block) { +			ext4_set_bit(EXT4_B2C(sbi, b - first_block), buf); +			count++; +		} +		b = ext4_inode_table(sb, gdp); +		if (b >= first_block && b + sbi->s_itb_per_group <= last_block) +			for (j = 0; j < sbi->s_itb_per_group; j++, b++) { +				int c = EXT4_B2C(sbi, b - first_block); +				ext4_set_bit(c, buf); +				count++; +			} +		if (i != grp) +			continue; +		s = 0; +		if (ext4_bg_has_super(sb, grp)) { +			ext4_set_bit(s++, buf); +			count++; +		} +		for (j = ext4_bg_num_gdb(sb, grp); j > 0; j--) { +			ext4_set_bit(EXT4_B2C(sbi, s++), buf); +			count++; +		} +	} +	if (!count) +		return 0; +	return EXT4_CLUSTERS_PER_GROUP(sb) - +		ext4_count_free(buf, EXT4_CLUSTERS_PER_GROUP(sb) / 8); +} + +/* + * Compute the overhead and stash it in sbi->s_overhead + */ +int ext4_calculate_overhead(struct super_block *sb) +{ +	struct ext4_sb_info *sbi = EXT4_SB(sb); +	struct ext4_super_block *es = sbi->s_es; +	ext4_group_t i, ngroups = ext4_get_groups_count(sb); +	ext4_fsblk_t overhead = 0; +	char *buf = (char *) get_zeroed_page(GFP_KERNEL); + +	memset(buf, 0, PAGE_SIZE); +	if (!buf) +		return -ENOMEM; + +	/* +	 * Compute the overhead (FS structures).  This is constant +	 * for a given filesystem unless the number of block groups +	 * changes so we cache the previous value until it does. +	 */ + +	/* +	 * All of the blocks before first_data_block are overhead +	 */ +	overhead = EXT4_B2C(sbi, le32_to_cpu(es->s_first_data_block)); + +	/* +	 * Add the overhead found in each block group +	 */ +	for (i = 0; i < ngroups; i++) { +		int blks; + +		blks = count_overhead(sb, i, buf); +		overhead += blks; +		if (blks) +			memset(buf, 0, PAGE_SIZE); +		cond_resched(); +	} +	sbi->s_overhead = overhead; +	smp_wmb(); +	free_page((unsigned long) buf); +	return 0; +} +  static int ext4_fill_super(struct super_block *sb, void *data, int silent)  {  	char *orig_data = kstrdup(data, GFP_KERNEL); @@ -3735,6 +3843,18 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)  no_journal:  	/* +	 * Get the # of file system overhead blocks from the +	 * superblock if present. +	 */ +	if (es->s_overhead_clusters) +		sbi->s_overhead = le32_to_cpu(es->s_overhead_clusters); +	else { +		ret = ext4_calculate_overhead(sb); +		if (ret) +			goto failed_mount_wq; +	} + +	/*  	 * The maximum number of concurrent works can be high and  	 * concurrency isn't really necessary.  Limit it to 1.  	 */ @@ -4600,67 +4720,21 @@ restore_opts:  	return err;  } -/* - * Note: calculating the overhead so we can be compatible with - * historical BSD practice is quite difficult in the face of - * clusters/bigalloc.  This is because multiple metadata blocks from - * different block group can end up in the same allocation cluster. - * Calculating the exact overhead in the face of clustered allocation - * requires either O(all block bitmaps) in memory or O(number of block - * groups**2) in time.  We will still calculate the superblock for - * older file systems --- and if we come across with a bigalloc file - * system with zero in s_overhead_clusters the estimate will be close to - * correct especially for very large cluster sizes --- but for newer - * file systems, it's better to calculate this figure once at mkfs - * time, and store it in the superblock.  If the superblock value is - * present (even for non-bigalloc file systems), we will use it. - */  static int ext4_statfs(struct dentry *dentry, struct kstatfs *buf)  {  	struct super_block *sb = dentry->d_sb;  	struct ext4_sb_info *sbi = EXT4_SB(sb);  	struct ext4_super_block *es = sbi->s_es; -	struct ext4_group_desc *gdp; +	ext4_fsblk_t overhead = 0;  	u64 fsid;  	s64 bfree; -	if (test_opt(sb, MINIX_DF)) { -		sbi->s_overhead_last = 0; -	} else if (es->s_overhead_clusters) { -		sbi->s_overhead_last = le32_to_cpu(es->s_overhead_clusters); -	} else if (sbi->s_blocks_last != ext4_blocks_count(es)) { -		ext4_group_t i, ngroups = ext4_get_groups_count(sb); -		ext4_fsblk_t overhead = 0; - -		/* -		 * Compute the overhead (FS structures).  This is constant -		 * for a given filesystem unless the number of block groups -		 * changes so we cache the previous value until it does. -		 */ - -		/* -		 * All of the blocks before first_data_block are -		 * overhead -		 */ -		overhead = EXT4_B2C(sbi, le32_to_cpu(es->s_first_data_block)); - -		/* -		 * Add the overhead found in each block group -		 */ -		for (i = 0; i < ngroups; i++) { -			gdp = ext4_get_group_desc(sb, i, NULL); -			overhead += ext4_num_overhead_clusters(sb, i, gdp); -			cond_resched(); -		} -		sbi->s_overhead_last = overhead; -		smp_wmb(); -		sbi->s_blocks_last = ext4_blocks_count(es); -	} +	if (!test_opt(sb, MINIX_DF)) +		overhead = sbi->s_overhead;  	buf->f_type = EXT4_SUPER_MAGIC;  	buf->f_bsize = sb->s_blocksize; -	buf->f_blocks = (ext4_blocks_count(es) - -			 EXT4_C2B(sbi, sbi->s_overhead_last)); +	buf->f_blocks = ext4_blocks_count(es) - EXT4_C2B(sbi, sbi->s_overhead);  	bfree = percpu_counter_sum_positive(&sbi->s_freeclusters_counter) -  		percpu_counter_sum_positive(&sbi->s_dirtyclusters_counter);  	/* prevent underflow in case that few free space is available */  |