diff options
Diffstat (limited to 'fs/ext4/file.c')
| -rw-r--r-- | fs/ext4/file.c | 129 | 
1 files changed, 49 insertions, 80 deletions
diff --git a/fs/ext4/file.c b/fs/ext4/file.c index 4cd318f31cbe..6659e216385e 100644 --- a/fs/ext4/file.c +++ b/fs/ext4/file.c @@ -93,31 +93,29 @@ ext4_file_write_iter(struct kiocb *iocb, struct iov_iter *from)  {  	struct file *file = iocb->ki_filp;  	struct inode *inode = file_inode(iocb->ki_filp); -	struct mutex *aio_mutex = NULL;  	struct blk_plug plug;  	int o_direct = iocb->ki_flags & IOCB_DIRECT; +	int unaligned_aio = 0;  	int overwrite = 0;  	ssize_t ret; +	inode_lock(inode); +	ret = generic_write_checks(iocb, from); +	if (ret <= 0) +		goto out; +  	/* -	 * Unaligned direct AIO must be serialized; see comment above -	 * In the case of O_APPEND, assume that we must always serialize +	 * Unaligned direct AIO must be serialized among each other as zeroing +	 * of partial blocks of two competing unaligned AIOs can result in data +	 * corruption.  	 */ -	if (o_direct && -	    ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS) && +	if (o_direct && ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS) &&  	    !is_sync_kiocb(iocb) && -	    (iocb->ki_flags & IOCB_APPEND || -	     ext4_unaligned_aio(inode, from, iocb->ki_pos))) { -		aio_mutex = ext4_aio_mutex(inode); -		mutex_lock(aio_mutex); +	    ext4_unaligned_aio(inode, from, iocb->ki_pos)) { +		unaligned_aio = 1;  		ext4_unwritten_wait(inode);  	} -	inode_lock(inode); -	ret = generic_write_checks(iocb, from); -	if (ret <= 0) -		goto out; -  	/*  	 * If we have encountered a bitmap-format file, the size limit  	 * is smaller than s_maxbytes, which is for extent-mapped files. @@ -139,7 +137,7 @@ ext4_file_write_iter(struct kiocb *iocb, struct iov_iter *from)  		blk_start_plug(&plug);  		/* check whether we do a DIO overwrite or not */ -		if (ext4_should_dioread_nolock(inode) && !aio_mutex && +		if (ext4_should_dioread_nolock(inode) && !unaligned_aio &&  		    !file->f_mapping->nrpages && pos + length <= i_size_read(inode)) {  			struct ext4_map_blocks map;  			unsigned int blkbits = inode->i_blkbits; @@ -181,14 +179,10 @@ ext4_file_write_iter(struct kiocb *iocb, struct iov_iter *from)  	if (o_direct)  		blk_finish_plug(&plug); -	if (aio_mutex) -		mutex_unlock(aio_mutex);  	return ret;  out:  	inode_unlock(inode); -	if (aio_mutex) -		mutex_unlock(aio_mutex);  	return ret;  } @@ -417,7 +411,7 @@ static int ext4_file_open(struct inode * inode, struct file * filp)   */  static int ext4_find_unwritten_pgoff(struct inode *inode,  				     int whence, -				     struct ext4_map_blocks *map, +				     ext4_lblk_t end_blk,  				     loff_t *offset)  {  	struct pagevec pvec; @@ -432,7 +426,7 @@ static int ext4_find_unwritten_pgoff(struct inode *inode,  	blkbits = inode->i_sb->s_blocksize_bits;  	startoff = *offset;  	lastoff = startoff; -	endoff = (loff_t)(map->m_lblk + map->m_len) << blkbits; +	endoff = (loff_t)end_blk << blkbits;  	index = startoff >> PAGE_CACHE_SHIFT;  	end = endoff >> PAGE_CACHE_SHIFT; @@ -550,12 +544,11 @@ out:  static loff_t ext4_seek_data(struct file *file, loff_t offset, loff_t maxsize)  {  	struct inode *inode = file->f_mapping->host; -	struct ext4_map_blocks map;  	struct extent_status es;  	ext4_lblk_t start, last, end;  	loff_t dataoff, isize;  	int blkbits; -	int ret = 0; +	int ret;  	inode_lock(inode); @@ -572,41 +565,32 @@ static loff_t ext4_seek_data(struct file *file, loff_t offset, loff_t maxsize)  	dataoff = offset;  	do { -		map.m_lblk = last; -		map.m_len = end - last + 1; -		ret = ext4_map_blocks(NULL, inode, &map, 0); -		if (ret > 0 && !(map.m_flags & EXT4_MAP_UNWRITTEN)) { -			if (last != start) -				dataoff = (loff_t)last << blkbits; -			break; +		ret = ext4_get_next_extent(inode, last, end - last + 1, &es); +		if (ret <= 0) { +			/* No extent found -> no data */ +			if (ret == 0) +				ret = -ENXIO; +			inode_unlock(inode); +			return ret;  		} -		/* -		 * If there is a delay extent at this offset, -		 * it will be as a data. -		 */ -		ext4_es_find_delayed_extent_range(inode, last, last, &es); -		if (es.es_len != 0 && in_range(last, es.es_lblk, es.es_len)) { -			if (last != start) -				dataoff = (loff_t)last << blkbits; +		last = es.es_lblk; +		if (last != start) +			dataoff = (loff_t)last << blkbits; +		if (!ext4_es_is_unwritten(&es))  			break; -		}  		/*  		 * If there is a unwritten extent at this offset,  		 * it will be as a data or a hole according to page  		 * cache that has data or not.  		 */ -		if (map.m_flags & EXT4_MAP_UNWRITTEN) { -			int unwritten; -			unwritten = ext4_find_unwritten_pgoff(inode, SEEK_DATA, -							      &map, &dataoff); -			if (unwritten) -				break; -		} - -		last++; +		if (ext4_find_unwritten_pgoff(inode, SEEK_DATA, +					      es.es_lblk + es.es_len, &dataoff)) +			break; +		last += es.es_len;  		dataoff = (loff_t)last << blkbits; +		cond_resched();  	} while (last <= end);  	inode_unlock(inode); @@ -623,12 +607,11 @@ static loff_t ext4_seek_data(struct file *file, loff_t offset, loff_t maxsize)  static loff_t ext4_seek_hole(struct file *file, loff_t offset, loff_t maxsize)  {  	struct inode *inode = file->f_mapping->host; -	struct ext4_map_blocks map;  	struct extent_status es;  	ext4_lblk_t start, last, end;  	loff_t holeoff, isize;  	int blkbits; -	int ret = 0; +	int ret;  	inode_lock(inode); @@ -645,44 +628,30 @@ static loff_t ext4_seek_hole(struct file *file, loff_t offset, loff_t maxsize)  	holeoff = offset;  	do { -		map.m_lblk = last; -		map.m_len = end - last + 1; -		ret = ext4_map_blocks(NULL, inode, &map, 0); -		if (ret > 0 && !(map.m_flags & EXT4_MAP_UNWRITTEN)) { -			last += ret; -			holeoff = (loff_t)last << blkbits; -			continue; +		ret = ext4_get_next_extent(inode, last, end - last + 1, &es); +		if (ret < 0) { +			inode_unlock(inode); +			return ret;  		} - -		/* -		 * If there is a delay extent at this offset, -		 * we will skip this extent. -		 */ -		ext4_es_find_delayed_extent_range(inode, last, last, &es); -		if (es.es_len != 0 && in_range(last, es.es_lblk, es.es_len)) { -			last = es.es_lblk + es.es_len; -			holeoff = (loff_t)last << blkbits; -			continue; +		/* Found a hole? */ +		if (ret == 0 || es.es_lblk > last) { +			if (last != start) +				holeoff = (loff_t)last << blkbits; +			break;  		} -  		/*  		 * If there is a unwritten extent at this offset,  		 * it will be as a data or a hole according to page  		 * cache that has data or not.  		 */ -		if (map.m_flags & EXT4_MAP_UNWRITTEN) { -			int unwritten; -			unwritten = ext4_find_unwritten_pgoff(inode, SEEK_HOLE, -							      &map, &holeoff); -			if (!unwritten) { -				last += ret; -				holeoff = (loff_t)last << blkbits; -				continue; -			} -		} +		if (ext4_es_is_unwritten(&es) && +		    ext4_find_unwritten_pgoff(inode, SEEK_HOLE, +					      last + es.es_len, &holeoff)) +			break; -		/* find a hole */ -		break; +		last += es.es_len; +		holeoff = (loff_t)last << blkbits; +		cond_resched();  	} while (last <= end);  	inode_unlock(inode);  | 
