diff options
Diffstat (limited to 'fs')
| -rw-r--r-- | fs/ext4/bitmap.c | 4 | ||||
| -rw-r--r-- | fs/ext4/ext4.h | 4 | ||||
| -rw-r--r-- | fs/ext4/resize.c | 7 | ||||
| -rw-r--r-- | fs/ext4/super.c | 174 | 
4 files changed, 132 insertions, 57 deletions
diff --git a/fs/ext4/bitmap.c b/fs/ext4/bitmap.c index 7e86a6d28c6..a94b9c63ee5 100644 --- a/fs/ext4/bitmap.c +++ b/fs/ext4/bitmap.c @@ -11,8 +11,6 @@  #include <linux/jbd2.h>  #include "ext4.h" -#ifdef EXT4FS_DEBUG -  static const int nibblemap[] = {4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 0};  unsigned int ext4_count_free(char *bitmap, unsigned int numchars) @@ -25,8 +23,6 @@ unsigned int ext4_count_free(char *bitmap, unsigned int numchars)  	return sum;  } -#endif  /*  EXT4FS_DEBUG  */ -  int ext4_inode_bitmap_csum_verify(struct super_block *sb, ext4_group_t group,  				  struct ext4_group_desc *gdp,  				  struct buffer_head *bh, int sz) diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 293fa1ced21..01434f25917 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -1161,8 +1161,7 @@ struct ext4_sb_info {  	unsigned long s_desc_per_block;	/* Number of group descriptors per block */  	ext4_group_t s_groups_count;	/* Number of groups in the fs */  	ext4_group_t s_blockfile_groups;/* Groups acceptable for non-extent files */ -	unsigned long s_overhead_last;  /* Last calculated overhead */ -	unsigned long s_blocks_last;    /* Last seen block count */ +	unsigned long s_overhead;  /* # of fs overhead clusters */  	unsigned int s_cluster_ratio;	/* Number of blocks per cluster */  	unsigned int s_cluster_bits;	/* log2 of s_cluster_ratio */  	loff_t s_bitmap_maxbytes;	/* max bytes for bitmap files */ @@ -2037,6 +2036,7 @@ extern int ext4_group_extend(struct super_block *sb,  extern int ext4_resize_fs(struct super_block *sb, ext4_fsblk_t n_blocks_count);  /* super.c */ +extern int ext4_calculate_overhead(struct super_block *sb);  extern int ext4_superblock_csum_verify(struct super_block *sb,  				       struct ext4_super_block *es);  extern void ext4_superblock_csum_set(struct super_block *sb, diff --git a/fs/ext4/resize.c b/fs/ext4/resize.c index 7ea6cbb4412..17d38de4068 100644 --- a/fs/ext4/resize.c +++ b/fs/ext4/resize.c @@ -1197,7 +1197,7 @@ static void ext4_update_super(struct super_block *sb,  	struct ext4_new_group_data *group_data = flex_gd->groups;  	struct ext4_sb_info *sbi = EXT4_SB(sb);  	struct ext4_super_block *es = sbi->s_es; -	int i; +	int i, ret;  	BUG_ON(flex_gd->count == 0 || group_data == NULL);  	/* @@ -1272,6 +1272,11 @@ static void ext4_update_super(struct super_block *sb,  			   &sbi->s_flex_groups[flex_group].free_inodes);  	} +	/* +	 * Update the fs overhead information +	 */ +	ext4_calculate_overhead(sb); +  	if (test_opt(sb, DEBUG))  		printk(KERN_DEBUG "EXT4-fs: added group %u:"  		       "%llu blocks(%llu free %llu reserved)\n", flex_gd->count, 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 */  |