diff options
Diffstat (limited to 'fs/ext4/file.c')
| -rw-r--r-- | fs/ext4/file.c | 220 | 
1 files changed, 114 insertions, 106 deletions
diff --git a/fs/ext4/file.c b/fs/ext4/file.c index 513c12cf444c..8131be8c0af3 100644 --- a/fs/ext4/file.c +++ b/fs/ext4/file.c @@ -273,19 +273,24 @@ static int ext4_file_open(struct inode * inode, struct file * filp)   * we determine this extent as a data or a hole according to whether the   * page cache has data or not.   */ -static int ext4_find_unwritten_pgoff(struct inode *inode, int whence, -				     loff_t endoff, loff_t *offset) +static int ext4_find_unwritten_pgoff(struct inode *inode, +				     int whence, +				     struct ext4_map_blocks *map, +				     loff_t *offset)  {  	struct pagevec pvec; +	unsigned int blkbits;  	pgoff_t index;  	pgoff_t end; +	loff_t endoff;  	loff_t startoff;  	loff_t lastoff;  	int found = 0; +	blkbits = inode->i_sb->s_blocksize_bits;  	startoff = *offset;  	lastoff = startoff; - +	endoff = (loff_t)(map->m_lblk + map->m_len) << blkbits;  	index = startoff >> PAGE_CACHE_SHIFT;  	end = endoff >> PAGE_CACHE_SHIFT; @@ -403,144 +408,147 @@ out:  static loff_t ext4_seek_data(struct file *file, loff_t offset, loff_t maxsize)  {  	struct inode *inode = file->f_mapping->host; -	struct fiemap_extent_info fie; -	struct fiemap_extent ext[2]; -	loff_t next; -	int i, ret = 0; +	struct ext4_map_blocks map; +	struct extent_status es; +	ext4_lblk_t start, last, end; +	loff_t dataoff, isize; +	int blkbits; +	int ret = 0;  	mutex_lock(&inode->i_mutex); -	if (offset >= inode->i_size) { + +	isize = i_size_read(inode); +	if (offset >= isize) {  		mutex_unlock(&inode->i_mutex);  		return -ENXIO;  	} -	fie.fi_flags = 0; -	fie.fi_extents_max = 2; -	fie.fi_extents_start = (struct fiemap_extent __user *) &ext; -	while (1) { -		mm_segment_t old_fs = get_fs(); - -		fie.fi_extents_mapped = 0; -		memset(ext, 0, sizeof(*ext) * fie.fi_extents_max); - -		set_fs(get_ds()); -		ret = ext4_fiemap(inode, &fie, offset, maxsize - offset); -		set_fs(old_fs); -		if (ret) + +	blkbits = inode->i_sb->s_blocksize_bits; +	start = offset >> blkbits; +	last = start; +	end = isize >> blkbits; +	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; +		} -		/* No extents found, EOF */ -		if (!fie.fi_extents_mapped) { -			ret = -ENXIO; +		/* +		 * 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;  			break;  		} -		for (i = 0; i < fie.fi_extents_mapped; i++) { -			next = (loff_t)(ext[i].fe_length + ext[i].fe_logical); -			if (offset < (loff_t)ext[i].fe_logical) -				offset = (loff_t)ext[i].fe_logical; -			/* -			 * If extent is not unwritten, then it contains valid -			 * data, mapped or delayed. -			 */ -			if (!(ext[i].fe_flags & FIEMAP_EXTENT_UNWRITTEN)) -				goto out; +		/* +		 * 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; +		} -			/* -			 * 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 (ext4_find_unwritten_pgoff(inode, SEEK_DATA, -						      next, &offset)) -				goto out; +		last++; +		dataoff = (loff_t)last << blkbits; +	} while (last <= end); -			if (ext[i].fe_flags & FIEMAP_EXTENT_LAST) { -				ret = -ENXIO; -				goto out; -			} -			offset = next; -		} -	} -	if (offset > inode->i_size) -		offset = inode->i_size; -out:  	mutex_unlock(&inode->i_mutex); -	if (ret) -		return ret; -	return vfs_setpos(file, offset, maxsize); +	if (dataoff > isize) +		return -ENXIO; + +	return vfs_setpos(file, dataoff, maxsize);  }  /* - * ext4_seek_hole() retrieves the offset for SEEK_HOLE + * ext4_seek_hole() retrieves the offset for SEEK_HOLE.   */  static loff_t ext4_seek_hole(struct file *file, loff_t offset, loff_t maxsize)  {  	struct inode *inode = file->f_mapping->host; -	struct fiemap_extent_info fie; -	struct fiemap_extent ext[2]; -	loff_t next; -	int i, ret = 0; +	struct ext4_map_blocks map; +	struct extent_status es; +	ext4_lblk_t start, last, end; +	loff_t holeoff, isize; +	int blkbits; +	int ret = 0;  	mutex_lock(&inode->i_mutex); -	if (offset >= inode->i_size) { + +	isize = i_size_read(inode); +	if (offset >= isize) {  		mutex_unlock(&inode->i_mutex);  		return -ENXIO;  	} -	fie.fi_flags = 0; -	fie.fi_extents_max = 2; -	fie.fi_extents_start = (struct fiemap_extent __user *)&ext; -	while (1) { -		mm_segment_t old_fs = get_fs(); - -		fie.fi_extents_mapped = 0; -		memset(ext, 0, sizeof(*ext)); +	blkbits = inode->i_sb->s_blocksize_bits; +	start = offset >> blkbits; +	last = start; +	end = isize >> blkbits; +	holeoff = offset; -		set_fs(get_ds()); -		ret = ext4_fiemap(inode, &fie, offset, maxsize - offset); -		set_fs(old_fs); -		if (ret) -			break; +	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; +		} -		/* No extents found */ -		if (!fie.fi_extents_mapped) -			break; +		/* +		 * 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; +		} -		for (i = 0; i < fie.fi_extents_mapped; i++) { -			next = (loff_t)(ext[i].fe_logical + ext[i].fe_length); -			/* -			 * If extent is not unwritten, then it contains valid -			 * data, mapped or delayed. -			 */ -			if (!(ext[i].fe_flags & FIEMAP_EXTENT_UNWRITTEN)) { -				if (offset < (loff_t)ext[i].fe_logical) -					goto out; -				offset = next; +		/* +		 * 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 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 (ext4_find_unwritten_pgoff(inode, SEEK_HOLE, -						      next, &offset)) -				goto out; - -			offset = next; -			if (ext[i].fe_flags & FIEMAP_EXTENT_LAST) -				goto out;  		} -	} -	if (offset > inode->i_size) -		offset = inode->i_size; -out: + +		/* find a hole */ +		break; +	} while (last <= end); +  	mutex_unlock(&inode->i_mutex); -	if (ret) -		return ret; -	return vfs_setpos(file, offset, maxsize); +	if (holeoff > isize) +		holeoff = isize; + +	return vfs_setpos(file, holeoff, maxsize);  }  /*  |