diff options
Diffstat (limited to 'fs/ext2/inode.c')
| -rw-r--r-- | fs/ext2/inode.c | 116 | 
1 files changed, 94 insertions, 22 deletions
| diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index d5c7d09919f3..d831e24dc885 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -32,6 +32,7 @@  #include <linux/buffer_head.h>  #include <linux/mpage.h>  #include <linux/fiemap.h> +#include <linux/iomap.h>  #include <linux/namei.h>  #include <linux/uio.h>  #include "ext2.h" @@ -594,7 +595,7 @@ static void ext2_splice_branch(struct inode *inode,  	if (where->bh)  		mark_buffer_dirty_inode(where->bh, inode); -	inode->i_ctime = CURRENT_TIME_SEC; +	inode->i_ctime = current_time(inode);  	mark_inode_dirty(inode);  } @@ -618,7 +619,7 @@ static void ext2_splice_branch(struct inode *inode,   */  static int ext2_get_blocks(struct inode *inode,  			   sector_t iblock, unsigned long maxblocks, -			   struct buffer_head *bh_result, +			   u32 *bno, bool *new, bool *boundary,  			   int create)  {  	int err = -EIO; @@ -644,7 +645,6 @@ static int ext2_get_blocks(struct inode *inode,  	/* Simplest case - block found, no allocation needed */  	if (!partial) {  		first_block = le32_to_cpu(chain[depth - 1].key); -		clear_buffer_new(bh_result); /* What's this do? */  		count++;  		/*map more blocks*/  		while (count < maxblocks && count <= blocks_to_boundary) { @@ -699,7 +699,6 @@ static int ext2_get_blocks(struct inode *inode,  			mutex_unlock(&ei->truncate_mutex);  			if (err)  				goto cleanup; -			clear_buffer_new(bh_result);  			goto got_it;  		}  	} @@ -733,6 +732,16 @@ static int ext2_get_blocks(struct inode *inode,  	}  	if (IS_DAX(inode)) { +		int i; + +		/* +		 * We must unmap blocks before zeroing so that writeback cannot +		 * overwrite zeros with stale data from block device page cache. +		 */ +		for (i = 0; i < count; i++) { +			unmap_underlying_metadata(inode->i_sb->s_bdev, +					le32_to_cpu(chain[depth-1].key) + i); +		}  		/*  		 * block must be initialised before we put it in the tree  		 * so that it's not found by another thread before it's @@ -745,15 +754,16 @@ static int ext2_get_blocks(struct inode *inode,  			mutex_unlock(&ei->truncate_mutex);  			goto cleanup;  		} -	} else -		set_buffer_new(bh_result); +	} else { +		*new = true; +	}  	ext2_splice_branch(inode, iblock, partial, indirect_blks, count);  	mutex_unlock(&ei->truncate_mutex);  got_it: -	map_bh(bh_result, inode->i_sb, le32_to_cpu(chain[depth-1].key)); +	*bno = le32_to_cpu(chain[depth-1].key);  	if (count > blocks_to_boundary) -		set_buffer_boundary(bh_result); +		*boundary = true;  	err = count;  	/* Clean up and exit */  	partial = chain + depth - 1;	/* the whole chain */ @@ -765,19 +775,82 @@ cleanup:  	return err;  } -int ext2_get_block(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create) +int ext2_get_block(struct inode *inode, sector_t iblock, +		struct buffer_head *bh_result, int create)  {  	unsigned max_blocks = bh_result->b_size >> inode->i_blkbits; -	int ret = ext2_get_blocks(inode, iblock, max_blocks, -			      bh_result, create); -	if (ret > 0) { -		bh_result->b_size = (ret << inode->i_blkbits); -		ret = 0; +	bool new = false, boundary = false; +	u32 bno; +	int ret; + +	ret = ext2_get_blocks(inode, iblock, max_blocks, &bno, &new, &boundary, +			create); +	if (ret <= 0) +		return ret; + +	map_bh(bh_result, inode->i_sb, bno); +	bh_result->b_size = (ret << inode->i_blkbits); +	if (new) +		set_buffer_new(bh_result); +	if (boundary) +		set_buffer_boundary(bh_result); +	return 0; + +} + +#ifdef CONFIG_FS_DAX +static int ext2_iomap_begin(struct inode *inode, loff_t offset, loff_t length, +		unsigned flags, struct iomap *iomap) +{ +	unsigned int blkbits = inode->i_blkbits; +	unsigned long first_block = offset >> blkbits; +	unsigned long max_blocks = (length + (1 << blkbits) - 1) >> blkbits; +	bool new = false, boundary = false; +	u32 bno; +	int ret; + +	ret = ext2_get_blocks(inode, first_block, max_blocks, +			&bno, &new, &boundary, flags & IOMAP_WRITE); +	if (ret < 0) +		return ret; + +	iomap->flags = 0; +	iomap->bdev = inode->i_sb->s_bdev; +	iomap->offset = (u64)first_block << blkbits; + +	if (ret == 0) { +		iomap->type = IOMAP_HOLE; +		iomap->blkno = IOMAP_NULL_BLOCK; +		iomap->length = 1 << blkbits; +	} else { +		iomap->type = IOMAP_MAPPED; +		iomap->blkno = (sector_t)bno << (blkbits - 9); +		iomap->length = (u64)ret << blkbits; +		iomap->flags |= IOMAP_F_MERGED;  	} -	return ret; +	if (new) +		iomap->flags |= IOMAP_F_NEW; +	return 0;  } +static int +ext2_iomap_end(struct inode *inode, loff_t offset, loff_t length, +		ssize_t written, unsigned flags, struct iomap *iomap) +{ +	if (iomap->type == IOMAP_MAPPED && +	    written < length && +	    (flags & IOMAP_WRITE)) +		ext2_write_failed(inode->i_mapping, offset + length); +	return 0; +} + +struct iomap_ops ext2_iomap_ops = { +	.iomap_begin		= ext2_iomap_begin, +	.iomap_end		= ext2_iomap_end, +}; +#endif /* CONFIG_FS_DAX */ +  int ext2_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,  		u64 start, u64 len)  { @@ -863,11 +936,10 @@ ext2_direct_IO(struct kiocb *iocb, struct iov_iter *iter)  	loff_t offset = iocb->ki_pos;  	ssize_t ret; -	if (IS_DAX(inode)) -		ret = dax_do_io(iocb, inode, iter, ext2_get_block, NULL, -				DIO_LOCKING); -	else -		ret = blockdev_direct_IO(iocb, inode, iter, ext2_get_block); +	if (WARN_ON_ONCE(IS_DAX(inode))) +		return -EIO; + +	ret = blockdev_direct_IO(iocb, inode, iter, ext2_get_block);  	if (ret < 0 && iov_iter_rw(iter) == WRITE)  		ext2_write_failed(mapping, offset + count);  	return ret; @@ -1236,7 +1308,7 @@ static int ext2_setsize(struct inode *inode, loff_t newsize)  	__ext2_truncate_blocks(inode, newsize);  	dax_sem_up_write(EXT2_I(inode)); -	inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC; +	inode->i_mtime = inode->i_ctime = current_time(inode);  	if (inode_needs_sync(inode)) {  		sync_mapping_buffers(inode->i_mapping);  		sync_inode_metadata(inode, 1); @@ -1580,7 +1652,7 @@ int ext2_setattr(struct dentry *dentry, struct iattr *iattr)  	struct inode *inode = d_inode(dentry);  	int error; -	error = inode_change_ok(inode, iattr); +	error = setattr_prepare(dentry, iattr);  	if (error)  		return error; |