diff options
Diffstat (limited to 'fs/ext4/inode.c')
| -rw-r--r-- | fs/ext4/inode.c | 109 | 
1 files changed, 83 insertions, 26 deletions
| diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 54bdd4884fe6..89aade6f45f6 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -483,7 +483,7 @@ static int ext4_map_query_blocks(handle_t *handle, struct inode *inode,  	status = map->m_flags & EXT4_MAP_UNWRITTEN ?  			EXTENT_STATUS_UNWRITTEN : EXTENT_STATUS_WRITTEN;  	ext4_es_insert_extent(inode, map->m_lblk, map->m_len, -			      map->m_pblk, status, 0); +			      map->m_pblk, status, false);  	return retval;  } @@ -563,8 +563,8 @@ static int ext4_map_create_blocks(handle_t *handle, struct inode *inode,  	status = map->m_flags & EXT4_MAP_UNWRITTEN ?  			EXTENT_STATUS_UNWRITTEN : EXTENT_STATUS_WRITTEN; -	ext4_es_insert_extent(inode, map->m_lblk, map->m_len, -			      map->m_pblk, status, flags); +	ext4_es_insert_extent(inode, map->m_lblk, map->m_len, map->m_pblk, +			      status, flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE);  	return retval;  } @@ -856,7 +856,14 @@ struct buffer_head *ext4_getblk(handle_t *handle, struct inode *inode,  	if (nowait)  		return sb_find_get_block(inode->i_sb, map.m_pblk); -	bh = sb_getblk(inode->i_sb, map.m_pblk); +	/* +	 * Since bh could introduce extra ref count such as referred by +	 * journal_head etc. Try to avoid using __GFP_MOVABLE here +	 * as it may fail the migration when journal_head remains. +	 */ +	bh = getblk_unmovable(inode->i_sb->s_bdev, map.m_pblk, +				inode->i_sb->s_blocksize); +  	if (unlikely(!bh))  		return ERR_PTR(-ENOMEM);  	if (map.m_flags & EXT4_MAP_NEW) { @@ -1307,8 +1314,10 @@ static int ext4_write_end(struct file *file,  	folio_unlock(folio);  	folio_put(folio); -	if (old_size < pos && !verity) +	if (old_size < pos && !verity) {  		pagecache_isize_extended(inode, old_size, pos); +		ext4_zero_partial_blocks(handle, inode, old_size, pos - old_size); +	}  	/*  	 * Don't mark the inode dirty under folio lock. First, it unnecessarily  	 * makes the holding time of folio lock longer. Second, it forces lock @@ -1423,8 +1432,10 @@ static int ext4_journalled_write_end(struct file *file,  	folio_unlock(folio);  	folio_put(folio); -	if (old_size < pos && !verity) +	if (old_size < pos && !verity) {  		pagecache_isize_extended(inode, old_size, pos); +		ext4_zero_partial_blocks(handle, inode, old_size, pos - old_size); +	}  	if (size_changed) {  		ret2 = ext4_mark_inode_dirty(handle, inode); @@ -2985,7 +2996,8 @@ static int ext4_da_do_write_end(struct address_space *mapping,  	struct inode *inode = mapping->host;  	loff_t old_size = inode->i_size;  	bool disksize_changed = false; -	loff_t new_i_size; +	loff_t new_i_size, zero_len = 0; +	handle_t *handle;  	if (unlikely(!folio_buffers(folio))) {  		folio_unlock(folio); @@ -3029,18 +3041,21 @@ static int ext4_da_do_write_end(struct address_space *mapping,  	folio_unlock(folio);  	folio_put(folio); -	if (old_size < pos) +	if (pos > old_size) {  		pagecache_isize_extended(inode, old_size, pos); +		zero_len = pos - old_size; +	} -	if (disksize_changed) { -		handle_t *handle; +	if (!disksize_changed && !zero_len) +		return copied; -		handle = ext4_journal_start(inode, EXT4_HT_INODE, 2); -		if (IS_ERR(handle)) -			return PTR_ERR(handle); -		ext4_mark_inode_dirty(handle, inode); -		ext4_journal_stop(handle); -	} +	handle = ext4_journal_start(inode, EXT4_HT_INODE, 2); +	if (IS_ERR(handle)) +		return PTR_ERR(handle); +	if (zero_len) +		ext4_zero_partial_blocks(handle, inode, old_size, zero_len); +	ext4_mark_inode_dirty(handle, inode); +	ext4_journal_stop(handle);  	return copied;  } @@ -3444,17 +3459,34 @@ static int ext4_iomap_overwrite_begin(struct inode *inode, loff_t offset,  	return ret;  } +static inline bool ext4_want_directio_fallback(unsigned flags, ssize_t written) +{ +	/* must be a directio to fall back to buffered */ +	if ((flags & (IOMAP_WRITE | IOMAP_DIRECT)) != +		    (IOMAP_WRITE | IOMAP_DIRECT)) +		return false; + +	/* atomic writes are all-or-nothing */ +	if (flags & IOMAP_ATOMIC) +		return false; + +	/* can only try again if we wrote nothing */ +	return written == 0; +} +  static int ext4_iomap_end(struct inode *inode, loff_t offset, loff_t length,  			  ssize_t written, unsigned flags, struct iomap *iomap)  {  	/*  	 * Check to see whether an error occurred while writing out the data to -	 * the allocated blocks. If so, return the magic error code so that we -	 * fallback to buffered I/O and attempt to complete the remainder of -	 * the I/O. Any blocks that may have been allocated in preparation for -	 * the direct I/O will be reused during buffered I/O. +	 * the allocated blocks. If so, return the magic error code for +	 * non-atomic write so that we fallback to buffered I/O and attempt to +	 * complete the remainder of the I/O. +	 * For non-atomic writes, any blocks that may have been +	 * allocated in preparation for the direct I/O will be reused during +	 * buffered I/O. For atomic write, we never fallback to buffered-io.  	 */ -	if (flags & (IOMAP_WRITE | IOMAP_DIRECT) && written == 0) +	if (ext4_want_directio_fallback(flags, written))  		return -ENOTBLK;  	return 0; @@ -4497,10 +4529,10 @@ make_io:  	 * Read the block from disk.  	 */  	trace_ext4_load_inode(sb, ino); -	ext4_read_bh_nowait(bh, REQ_META | REQ_PRIO, NULL); +	ext4_read_bh_nowait(bh, REQ_META | REQ_PRIO, NULL, +			    ext4_simulate_fail(sb, EXT4_SIM_INODE_EIO));  	blk_finish_plug(&plug);  	wait_on_buffer(bh); -	ext4_simulate_fail_bh(sb, bh, EXT4_SIM_INODE_EIO);  	if (!buffer_uptodate(bh)) {  		if (ret_block)  			*ret_block = block; @@ -5426,6 +5458,14 @@ int ext4_setattr(struct mnt_idmap *idmap, struct dentry *dentry,  		}  		if (attr->ia_size != inode->i_size) { +			/* attach jbd2 jinode for EOF folio tail zeroing */ +			if (attr->ia_size & (inode->i_sb->s_blocksize - 1) || +			    oldsize & (inode->i_sb->s_blocksize - 1)) { +				error = ext4_inode_attach_jinode(inode); +				if (error) +					goto err_out; +			} +  			handle = ext4_journal_start(inode, EXT4_HT_INODE, 3);  			if (IS_ERR(handle)) {  				error = PTR_ERR(handle); @@ -5436,12 +5476,17 @@ int ext4_setattr(struct mnt_idmap *idmap, struct dentry *dentry,  				orphan = 1;  			}  			/* -			 * Update c/mtime on truncate up, ext4_truncate() will -			 * update c/mtime in shrink case below +			 * Update c/mtime and tail zero the EOF folio on +			 * truncate up. ext4_truncate() handles the shrink case +			 * below.  			 */ -			if (!shrink) +			if (!shrink) {  				inode_set_mtime_to_ts(inode,  						      inode_set_ctime_current(inode)); +				if (oldsize & (inode->i_sb->s_blocksize - 1)) +					ext4_block_truncate_page(handle, +							inode->i_mapping, oldsize); +			}  			if (shrink)  				ext4_fc_track_range(handle, inode, @@ -5578,6 +5623,18 @@ int ext4_getattr(struct mnt_idmap *idmap, const struct path *path,  		}  	} +	if ((request_mask & STATX_WRITE_ATOMIC) && S_ISREG(inode->i_mode)) { +		struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); +		unsigned int awu_min = 0, awu_max = 0; + +		if (ext4_inode_can_atomic_write(inode)) { +			awu_min = sbi->s_awu_min; +			awu_max = sbi->s_awu_max; +		} + +		generic_fill_statx_atomic_writes(stat, awu_min, awu_max); +	} +  	flags = ei->i_flags & EXT4_FL_USER_VISIBLE;  	if (flags & EXT4_APPEND_FL)  		stat->attributes |= STATX_ATTR_APPEND; | 
