diff options
Diffstat (limited to 'fs/ext4')
| -rw-r--r-- | fs/ext4/balloc.c | 8 | ||||
| -rw-r--r-- | fs/ext4/bitmap.c | 6 | ||||
| -rw-r--r-- | fs/ext4/ext4.h | 7 | ||||
| -rw-r--r-- | fs/ext4/ext4_jbd2.c | 8 | ||||
| -rw-r--r-- | fs/ext4/extents.c | 60 | ||||
| -rw-r--r-- | fs/ext4/ialloc.c | 23 | ||||
| -rw-r--r-- | fs/ext4/mballoc.c | 14 | ||||
| -rw-r--r-- | fs/ext4/resize.c | 3 | ||||
| -rw-r--r-- | fs/ext4/super.c | 9 | 
9 files changed, 83 insertions, 55 deletions
diff --git a/fs/ext4/balloc.c b/fs/ext4/balloc.c index 1b5089067d0..cf1821784a1 100644 --- a/fs/ext4/balloc.c +++ b/fs/ext4/balloc.c @@ -174,8 +174,7 @@ void ext4_init_block_bitmap(struct super_block *sb, struct buffer_head *bh,  		ext4_free_inodes_set(sb, gdp, 0);  		ext4_itable_unused_set(sb, gdp, 0);  		memset(bh->b_data, 0xff, sb->s_blocksize); -		ext4_block_bitmap_csum_set(sb, block_group, gdp, bh, -					   EXT4_BLOCKS_PER_GROUP(sb) / 8); +		ext4_block_bitmap_csum_set(sb, block_group, gdp, bh);  		return;  	}  	memset(bh->b_data, 0, sb->s_blocksize); @@ -212,8 +211,7 @@ void ext4_init_block_bitmap(struct super_block *sb, struct buffer_head *bh,  	 */  	ext4_mark_bitmap_end(num_clusters_in_group(sb, block_group),  			     sb->s_blocksize * 8, bh->b_data); -	ext4_block_bitmap_csum_set(sb, block_group, gdp, bh, -				   EXT4_BLOCKS_PER_GROUP(sb) / 8); +	ext4_block_bitmap_csum_set(sb, block_group, gdp, bh);  	ext4_group_desc_csum_set(sb, block_group, gdp);  } @@ -350,7 +348,7 @@ void ext4_validate_block_bitmap(struct super_block *sb,  		return;  	}  	if (unlikely(!ext4_block_bitmap_csum_verify(sb, block_group, -			desc, bh, EXT4_BLOCKS_PER_GROUP(sb) / 8))) { +			desc, bh))) {  		ext4_unlock_group(sb, block_group);  		ext4_error(sb, "bg %u: bad block bitmap checksum", block_group);  		return; diff --git a/fs/ext4/bitmap.c b/fs/ext4/bitmap.c index 5c2d1813ebe..3285aa5a706 100644 --- a/fs/ext4/bitmap.c +++ b/fs/ext4/bitmap.c @@ -58,11 +58,12 @@ void ext4_inode_bitmap_csum_set(struct super_block *sb, ext4_group_t group,  int ext4_block_bitmap_csum_verify(struct super_block *sb, ext4_group_t group,  				  struct ext4_group_desc *gdp, -				  struct buffer_head *bh, int sz) +				  struct buffer_head *bh)  {  	__u32 hi;  	__u32 provided, calculated;  	struct ext4_sb_info *sbi = EXT4_SB(sb); +	int sz = EXT4_CLUSTERS_PER_GROUP(sb) / 8;  	if (!EXT4_HAS_RO_COMPAT_FEATURE(sb,  					EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) @@ -84,8 +85,9 @@ int ext4_block_bitmap_csum_verify(struct super_block *sb, ext4_group_t group,  void ext4_block_bitmap_csum_set(struct super_block *sb, ext4_group_t group,  				struct ext4_group_desc *gdp, -				struct buffer_head *bh, int sz) +				struct buffer_head *bh)  { +	int sz = EXT4_CLUSTERS_PER_GROUP(sb) / 8;  	__u32 csum;  	struct ext4_sb_info *sbi = EXT4_SB(sb); diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 3ab2539b7b2..3c20de1d59d 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -1882,10 +1882,10 @@ int ext4_inode_bitmap_csum_verify(struct super_block *sb, ext4_group_t group,  				  struct buffer_head *bh, int sz);  void ext4_block_bitmap_csum_set(struct super_block *sb, ext4_group_t group,  				struct ext4_group_desc *gdp, -				struct buffer_head *bh, int sz); +				struct buffer_head *bh);  int ext4_block_bitmap_csum_verify(struct super_block *sb, ext4_group_t group,  				  struct ext4_group_desc *gdp, -				  struct buffer_head *bh, int sz); +				  struct buffer_head *bh);  /* balloc.c */  extern void ext4_validate_block_bitmap(struct super_block *sb, @@ -2063,8 +2063,7 @@ extern int ext4_resize_fs(struct super_block *sb, ext4_fsblk_t n_blocks_count);  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, -				     struct ext4_super_block *es); +extern void ext4_superblock_csum_set(struct super_block *sb);  extern void *ext4_kvmalloc(size_t size, gfp_t flags);  extern void *ext4_kvzalloc(size_t size, gfp_t flags);  extern void ext4_kvfree(void *ptr); diff --git a/fs/ext4/ext4_jbd2.c b/fs/ext4/ext4_jbd2.c index bfa65b49d42..b4323ba846b 100644 --- a/fs/ext4/ext4_jbd2.c +++ b/fs/ext4/ext4_jbd2.c @@ -143,17 +143,13 @@ int __ext4_handle_dirty_super(const char *where, unsigned int line,  	struct buffer_head *bh = EXT4_SB(sb)->s_sbh;  	int err = 0; +	ext4_superblock_csum_set(sb);  	if (ext4_handle_valid(handle)) { -		ext4_superblock_csum_set(sb, -				(struct ext4_super_block *)bh->b_data);  		err = jbd2_journal_dirty_metadata(handle, bh);  		if (err)  			ext4_journal_abort_handle(where, line, __func__,  						  bh, handle, err); -	} else { -		ext4_superblock_csum_set(sb, -				(struct ext4_super_block *)bh->b_data); +	} else  		mark_buffer_dirty(bh); -	}  	return err;  } diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index 1c94cca35ed..7011ac96720 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -52,6 +52,9 @@  #define EXT4_EXT_MARK_UNINIT1	0x2  /* mark first half uninitialized */  #define EXT4_EXT_MARK_UNINIT2	0x4  /* mark second half uninitialized */ +#define EXT4_EXT_DATA_VALID1	0x8  /* first half contains valid data */ +#define EXT4_EXT_DATA_VALID2	0x10 /* second half contains valid data */ +  static __le32 ext4_extent_block_csum(struct inode *inode,  				     struct ext4_extent_header *eh)  { @@ -2914,6 +2917,9 @@ static int ext4_split_extent_at(handle_t *handle,  	unsigned int ee_len, depth;  	int err = 0; +	BUG_ON((split_flag & (EXT4_EXT_DATA_VALID1 | EXT4_EXT_DATA_VALID2)) == +	       (EXT4_EXT_DATA_VALID1 | EXT4_EXT_DATA_VALID2)); +  	ext_debug("ext4_split_extents_at: inode %lu, logical"  		"block %llu\n", inode->i_ino, (unsigned long long)split); @@ -2972,7 +2978,14 @@ static int ext4_split_extent_at(handle_t *handle,  	err = ext4_ext_insert_extent(handle, inode, path, &newex, flags);  	if (err == -ENOSPC && (EXT4_EXT_MAY_ZEROOUT & split_flag)) { -		err = ext4_ext_zeroout(inode, &orig_ex); +		if (split_flag & (EXT4_EXT_DATA_VALID1|EXT4_EXT_DATA_VALID2)) { +			if (split_flag & EXT4_EXT_DATA_VALID1) +				err = ext4_ext_zeroout(inode, ex2); +			else +				err = ext4_ext_zeroout(inode, ex); +		} else +			err = ext4_ext_zeroout(inode, &orig_ex); +  		if (err)  			goto fix_extent_len;  		/* update the extent length and mark as initialized */ @@ -3025,12 +3038,13 @@ static int ext4_split_extent(handle_t *handle,  	uninitialized = ext4_ext_is_uninitialized(ex);  	if (map->m_lblk + map->m_len < ee_block + ee_len) { -		split_flag1 = split_flag & EXT4_EXT_MAY_ZEROOUT ? -			      EXT4_EXT_MAY_ZEROOUT : 0; +		split_flag1 = split_flag & EXT4_EXT_MAY_ZEROOUT;  		flags1 = flags | EXT4_GET_BLOCKS_PRE_IO;  		if (uninitialized)  			split_flag1 |= EXT4_EXT_MARK_UNINIT1 |  				       EXT4_EXT_MARK_UNINIT2; +		if (split_flag & EXT4_EXT_DATA_VALID2) +			split_flag1 |= EXT4_EXT_DATA_VALID1;  		err = ext4_split_extent_at(handle, inode, path,  				map->m_lblk + map->m_len, split_flag1, flags1);  		if (err) @@ -3043,8 +3057,8 @@ static int ext4_split_extent(handle_t *handle,  		return PTR_ERR(path);  	if (map->m_lblk >= ee_block) { -		split_flag1 = split_flag & EXT4_EXT_MAY_ZEROOUT ? -			      EXT4_EXT_MAY_ZEROOUT : 0; +		split_flag1 = split_flag & (EXT4_EXT_MAY_ZEROOUT | +					    EXT4_EXT_DATA_VALID2);  		if (uninitialized)  			split_flag1 |= EXT4_EXT_MARK_UNINIT1;  		if (split_flag & EXT4_EXT_MARK_UNINIT2) @@ -3323,26 +3337,47 @@ static int ext4_split_unwritten_extents(handle_t *handle,  	split_flag |= ee_block + ee_len <= eof_block ? EXT4_EXT_MAY_ZEROOUT : 0;  	split_flag |= EXT4_EXT_MARK_UNINIT2; - +	if (flags & EXT4_GET_BLOCKS_CONVERT) +		split_flag |= EXT4_EXT_DATA_VALID2;  	flags |= EXT4_GET_BLOCKS_PRE_IO;  	return ext4_split_extent(handle, inode, path, map, split_flag, flags);  }  static int ext4_convert_unwritten_extents_endio(handle_t *handle, -					      struct inode *inode, -					      struct ext4_ext_path *path) +						struct inode *inode, +						struct ext4_map_blocks *map, +						struct ext4_ext_path *path)  {  	struct ext4_extent *ex; +	ext4_lblk_t ee_block; +	unsigned int ee_len;  	int depth;  	int err = 0;  	depth = ext_depth(inode);  	ex = path[depth].p_ext; +	ee_block = le32_to_cpu(ex->ee_block); +	ee_len = ext4_ext_get_actual_len(ex);  	ext_debug("ext4_convert_unwritten_extents_endio: inode %lu, logical"  		"block %llu, max_blocks %u\n", inode->i_ino, -		(unsigned long long)le32_to_cpu(ex->ee_block), -		ext4_ext_get_actual_len(ex)); +		  (unsigned long long)ee_block, ee_len); + +	/* If extent is larger than requested then split is required */ +	if (ee_block != map->m_lblk || ee_len > map->m_len) { +		err = ext4_split_unwritten_extents(handle, inode, map, path, +						   EXT4_GET_BLOCKS_CONVERT); +		if (err < 0) +			goto out; +		ext4_ext_drop_refs(path); +		path = ext4_ext_find_extent(inode, map->m_lblk, path); +		if (IS_ERR(path)) { +			err = PTR_ERR(path); +			goto out; +		} +		depth = ext_depth(inode); +		ex = path[depth].p_ext; +	}  	err = ext4_ext_get_access(handle, inode, path + depth);  	if (err) @@ -3652,7 +3687,7 @@ ext4_ext_handle_uninitialized_extents(handle_t *handle, struct inode *inode,  	}  	/* IO end_io complete, convert the filled extent to written */  	if ((flags & EXT4_GET_BLOCKS_CONVERT)) { -		ret = ext4_convert_unwritten_extents_endio(handle, inode, +		ret = ext4_convert_unwritten_extents_endio(handle, inode, map,  							path);  		if (ret >= 0) {  			ext4_update_inode_fsync_trans(handle, inode, 1); @@ -4428,6 +4463,9 @@ long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len)  	 */  	if (len <= EXT_UNINIT_MAX_LEN << blkbits)  		flags |= EXT4_GET_BLOCKS_NO_NORMALIZE; + +	/* Prevent race condition between unwritten */ +	ext4_flush_unwritten_io(inode);  retry:  	while (ret >= 0 && ret < max_blocks) {  		map.m_lblk = map.m_lblk + ret; diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c index fa36372f3fd..3a100e7a62a 100644 --- a/fs/ext4/ialloc.c +++ b/fs/ext4/ialloc.c @@ -725,6 +725,10 @@ repeat_in_this_group:  				   "inode=%lu", ino + 1);  			continue;  		} +		BUFFER_TRACE(inode_bitmap_bh, "get_write_access"); +		err = ext4_journal_get_write_access(handle, inode_bitmap_bh); +		if (err) +			goto fail;  		ext4_lock_group(sb, group);  		ret2 = ext4_test_and_set_bit(ino, inode_bitmap_bh->b_data);  		ext4_unlock_group(sb, group); @@ -738,6 +742,11 @@ repeat_in_this_group:  	goto out;  got: +	BUFFER_TRACE(inode_bitmap_bh, "call ext4_handle_dirty_metadata"); +	err = ext4_handle_dirty_metadata(handle, NULL, inode_bitmap_bh); +	if (err) +		goto fail; +  	/* We may have to initialize the block bitmap if it isn't already */  	if (ext4_has_group_desc_csum(sb) &&  	    gdp->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT)) { @@ -762,9 +771,7 @@ got:  			ext4_free_group_clusters_set(sb, gdp,  				ext4_free_clusters_after_init(sb, group, gdp));  			ext4_block_bitmap_csum_set(sb, group, gdp, -						   block_bitmap_bh, -						   EXT4_BLOCKS_PER_GROUP(sb) / -						   8); +						   block_bitmap_bh);  			ext4_group_desc_csum_set(sb, group, gdp);  		}  		ext4_unlock_group(sb, group); @@ -773,11 +780,6 @@ got:  			goto fail;  	} -	BUFFER_TRACE(inode_bitmap_bh, "get_write_access"); -	err = ext4_journal_get_write_access(handle, inode_bitmap_bh); -	if (err) -		goto fail; -  	BUFFER_TRACE(group_desc_bh, "get_write_access");  	err = ext4_journal_get_write_access(handle, group_desc_bh);  	if (err) @@ -825,11 +827,6 @@ got:  	}  	ext4_unlock_group(sb, group); -	BUFFER_TRACE(inode_bitmap_bh, "call ext4_handle_dirty_metadata"); -	err = ext4_handle_dirty_metadata(handle, NULL, inode_bitmap_bh); -	if (err) -		goto fail; -  	BUFFER_TRACE(group_desc_bh, "call ext4_handle_dirty_metadata");  	err = ext4_handle_dirty_metadata(handle, NULL, group_desc_bh);  	if (err) diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index f8b27bf80ac..526e5535860 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c @@ -2805,8 +2805,7 @@ ext4_mb_mark_diskspace_used(struct ext4_allocation_context *ac,  	}  	len = ext4_free_group_clusters(sb, gdp) - ac->ac_b_ex.fe_len;  	ext4_free_group_clusters_set(sb, gdp, len); -	ext4_block_bitmap_csum_set(sb, ac->ac_b_ex.fe_group, gdp, bitmap_bh, -				   EXT4_BLOCKS_PER_GROUP(sb) / 8); +	ext4_block_bitmap_csum_set(sb, ac->ac_b_ex.fe_group, gdp, bitmap_bh);  	ext4_group_desc_csum_set(sb, ac->ac_b_ex.fe_group, gdp);  	ext4_unlock_group(sb, ac->ac_b_ex.fe_group); @@ -4666,8 +4665,7 @@ do_more:  	ret = ext4_free_group_clusters(sb, gdp) + count_clusters;  	ext4_free_group_clusters_set(sb, gdp, ret); -	ext4_block_bitmap_csum_set(sb, block_group, gdp, bitmap_bh, -				   EXT4_BLOCKS_PER_GROUP(sb) / 8); +	ext4_block_bitmap_csum_set(sb, block_group, gdp, bitmap_bh);  	ext4_group_desc_csum_set(sb, block_group, gdp);  	ext4_unlock_group(sb, block_group);  	percpu_counter_add(&sbi->s_freeclusters_counter, count_clusters); @@ -4811,8 +4809,7 @@ int ext4_group_add_blocks(handle_t *handle, struct super_block *sb,  	mb_free_blocks(NULL, &e4b, bit, count);  	blk_free_count = blocks_freed + ext4_free_group_clusters(sb, desc);  	ext4_free_group_clusters_set(sb, desc, blk_free_count); -	ext4_block_bitmap_csum_set(sb, block_group, desc, bitmap_bh, -				   EXT4_BLOCKS_PER_GROUP(sb) / 8); +	ext4_block_bitmap_csum_set(sb, block_group, desc, bitmap_bh);  	ext4_group_desc_csum_set(sb, block_group, desc);  	ext4_unlock_group(sb, block_group);  	percpu_counter_add(&sbi->s_freeclusters_counter, @@ -4993,8 +4990,9 @@ int ext4_trim_fs(struct super_block *sb, struct fstrim_range *range)  	minlen = EXT4_NUM_B2C(EXT4_SB(sb),  			      range->minlen >> sb->s_blocksize_bits); -	if (unlikely(minlen > EXT4_CLUSTERS_PER_GROUP(sb)) || -	    unlikely(start >= max_blks)) +	if (minlen > EXT4_CLUSTERS_PER_GROUP(sb) || +	    start >= max_blks || +	    range->len < sb->s_blocksize)  		return -EINVAL;  	if (end >= max_blks)  		end = max_blks - 1; diff --git a/fs/ext4/resize.c b/fs/ext4/resize.c index 7a75e108696..47bf06a2765 100644 --- a/fs/ext4/resize.c +++ b/fs/ext4/resize.c @@ -1212,8 +1212,7 @@ static int ext4_set_bitmap_checksums(struct super_block *sb,  	bh = ext4_get_bitmap(sb, group_data->block_bitmap);  	if (!bh)  		return -EIO; -	ext4_block_bitmap_csum_set(sb, group, gdp, bh, -				   EXT4_BLOCKS_PER_GROUP(sb) / 8); +	ext4_block_bitmap_csum_set(sb, group, gdp, bh);  	brelse(bh);  	return 0; diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 7265a036747..80928f71685 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -143,9 +143,10 @@ int ext4_superblock_csum_verify(struct super_block *sb,  	return es->s_checksum == ext4_superblock_csum(sb, es);  } -void ext4_superblock_csum_set(struct super_block *sb, -			      struct ext4_super_block *es) +void ext4_superblock_csum_set(struct super_block *sb)  { +	struct ext4_super_block *es = EXT4_SB(sb)->s_es; +  	if (!EXT4_HAS_RO_COMPAT_FEATURE(sb,  		EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))  		return; @@ -1963,7 +1964,7 @@ static int ext4_fill_flex_info(struct super_block *sb)  		sbi->s_log_groups_per_flex = 0;  		return 1;  	} -	groups_per_flex = 1 << sbi->s_log_groups_per_flex; +	groups_per_flex = 1U << sbi->s_log_groups_per_flex;  	err = ext4_alloc_flex_bg_array(sb, sbi->s_groups_count);  	if (err) @@ -4381,7 +4382,7 @@ static int ext4_commit_super(struct super_block *sb, int sync)  		cpu_to_le32(percpu_counter_sum_positive(  				&EXT4_SB(sb)->s_freeinodes_counter));  	BUFFER_TRACE(sbh, "marking dirty"); -	ext4_superblock_csum_set(sb, es); +	ext4_superblock_csum_set(sb);  	mark_buffer_dirty(sbh);  	if (sync) {  		error = sync_dirty_buffer(sbh);  |