diff options
Diffstat (limited to 'fs/ext4/file.c')
| -rw-r--r-- | fs/ext4/file.c | 36 | 
1 files changed, 23 insertions, 13 deletions
diff --git a/fs/ext4/file.c b/fs/ext4/file.c index 7ac0a81bd371..0b8b4499e5ca 100644 --- a/fs/ext4/file.c +++ b/fs/ext4/file.c @@ -202,8 +202,9 @@ ext4_extending_io(struct inode *inode, loff_t offset, size_t len)  	return false;  } -/* Is IO overwriting allocated and initialized blocks? */ -static bool ext4_overwrite_io(struct inode *inode, loff_t pos, loff_t len) +/* Is IO overwriting allocated or initialized blocks? */ +static bool ext4_overwrite_io(struct inode *inode, +			      loff_t pos, loff_t len, bool *unwritten)  {  	struct ext4_map_blocks map;  	unsigned int blkbits = inode->i_blkbits; @@ -217,12 +218,15 @@ static bool ext4_overwrite_io(struct inode *inode, loff_t pos, loff_t len)  	blklen = map.m_len;  	err = ext4_map_blocks(NULL, inode, &map, 0); +	if (err != blklen) +		return false;  	/*  	 * 'err==len' means that all of the blocks have been preallocated, -	 * regardless of whether they have been initialized or not. To exclude -	 * unwritten extents, we need to check m_flags. +	 * regardless of whether they have been initialized or not. We need to +	 * check m_flags to distinguish the unwritten extents.  	 */ -	return err == blklen && (map.m_flags & EXT4_MAP_MAPPED); +	*unwritten = !(map.m_flags & EXT4_MAP_MAPPED); +	return true;  }  static ssize_t ext4_generic_write_checks(struct kiocb *iocb, @@ -431,11 +435,16 @@ static const struct iomap_dio_ops ext4_dio_write_ops = {   * - For extending writes case we don't take the shared lock, since it requires   *   updating inode i_disksize and/or orphan handling with exclusive lock.   * - * - shared locking will only be true mostly with overwrites. Otherwise we will - *   switch to exclusive i_rwsem lock. + * - shared locking will only be true mostly with overwrites, including + *   initialized blocks and unwritten blocks. For overwrite unwritten blocks + *   we protect splitting extents by i_data_sem in ext4_inode_info, so we can + *   also release exclusive i_rwsem lock. + * + * - Otherwise we will switch to exclusive i_rwsem lock.   */  static ssize_t ext4_dio_write_checks(struct kiocb *iocb, struct iov_iter *from, -				     bool *ilock_shared, bool *extend) +				     bool *ilock_shared, bool *extend, +				     bool *unwritten)  {  	struct file *file = iocb->ki_filp;  	struct inode *inode = file_inode(file); @@ -459,7 +468,7 @@ restart:  	 * in file_modified().  	 */  	if (*ilock_shared && (!IS_NOSEC(inode) || *extend || -	     !ext4_overwrite_io(inode, offset, count))) { +	     !ext4_overwrite_io(inode, offset, count, unwritten))) {  		if (iocb->ki_flags & IOCB_NOWAIT) {  			ret = -EAGAIN;  			goto out; @@ -491,7 +500,7 @@ static ssize_t ext4_dio_write_iter(struct kiocb *iocb, struct iov_iter *from)  	loff_t offset = iocb->ki_pos;  	size_t count = iov_iter_count(from);  	const struct iomap_ops *iomap_ops = &ext4_iomap_ops; -	bool extend = false, unaligned_io = false; +	bool extend = false, unaligned_io = false, unwritten = false;  	bool ilock_shared = true;  	/* @@ -534,7 +543,8 @@ static ssize_t ext4_dio_write_iter(struct kiocb *iocb, struct iov_iter *from)  		return ext4_buffered_write_iter(iocb, from);  	} -	ret = ext4_dio_write_checks(iocb, from, &ilock_shared, &extend); +	ret = ext4_dio_write_checks(iocb, from, +				    &ilock_shared, &extend, &unwritten);  	if (ret <= 0)  		return ret; @@ -582,7 +592,7 @@ static ssize_t ext4_dio_write_iter(struct kiocb *iocb, struct iov_iter *from)  		ext4_journal_stop(handle);  	} -	if (ilock_shared) +	if (ilock_shared && !unwritten)  		iomap_ops = &ext4_iomap_overwrite_ops;  	ret = iomap_dio_rw(iocb, from, iomap_ops, &ext4_dio_write_ops,  			   (unaligned_io || extend) ? IOMAP_DIO_FORCE_WAIT : 0, @@ -801,7 +811,7 @@ static int ext4_file_mmap(struct file *file, struct vm_area_struct *vma)  	file_accessed(file);  	if (IS_DAX(file_inode(file))) {  		vma->vm_ops = &ext4_dax_vm_ops; -		vma->vm_flags |= VM_HUGEPAGE; +		vm_flags_set(vma, VM_HUGEPAGE);  	} else {  		vma->vm_ops = &ext4_file_vm_ops;  	}  |