Btrfs: fallocate: Work with sectorsized blocks

While at it, this commit changes btrfs_truncate_page() to truncate sectorsized
blocks instead of pages. Hence the function has been renamed to
btrfs_truncate_block().

Signed-off-by: Chandan Rajendra <chandan@linux.vnet.ibm.com>
Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
Chandan Rajendra 2016-01-21 15:55:56 +05:30 committed by David Sterba
parent 2dabb32484
commit 9703fefe0b
3 changed files with 55 additions and 51 deletions

View File

@ -4030,7 +4030,7 @@ int btrfs_unlink_subvol(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct btrfs_root *root,
struct inode *dir, u64 objectid, struct inode *dir, u64 objectid,
const char *name, int name_len); const char *name, int name_len);
int btrfs_truncate_page(struct inode *inode, loff_t from, loff_t len, int btrfs_truncate_block(struct inode *inode, loff_t from, loff_t len,
int front); int front);
int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans, int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct btrfs_root *root,

View File

@ -2310,10 +2310,10 @@ static int btrfs_punch_hole(struct inode *inode, loff_t offset, loff_t len)
int ret = 0; int ret = 0;
int err = 0; int err = 0;
unsigned int rsv_count; unsigned int rsv_count;
bool same_page; bool same_block;
bool no_holes = btrfs_fs_incompat(root->fs_info, NO_HOLES); bool no_holes = btrfs_fs_incompat(root->fs_info, NO_HOLES);
u64 ino_size; u64 ino_size;
bool truncated_page = false; bool truncated_block = false;
bool updated_inode = false; bool updated_inode = false;
ret = btrfs_wait_ordered_range(inode, offset, len); ret = btrfs_wait_ordered_range(inode, offset, len);
@ -2321,7 +2321,7 @@ static int btrfs_punch_hole(struct inode *inode, loff_t offset, loff_t len)
return ret; return ret;
mutex_lock(&inode->i_mutex); mutex_lock(&inode->i_mutex);
ino_size = round_up(inode->i_size, PAGE_CACHE_SIZE); ino_size = round_up(inode->i_size, root->sectorsize);
ret = find_first_non_hole(inode, &offset, &len); ret = find_first_non_hole(inode, &offset, &len);
if (ret < 0) if (ret < 0)
goto out_only_mutex; goto out_only_mutex;
@ -2334,31 +2334,30 @@ static int btrfs_punch_hole(struct inode *inode, loff_t offset, loff_t len)
lockstart = round_up(offset, BTRFS_I(inode)->root->sectorsize); lockstart = round_up(offset, BTRFS_I(inode)->root->sectorsize);
lockend = round_down(offset + len, lockend = round_down(offset + len,
BTRFS_I(inode)->root->sectorsize) - 1; BTRFS_I(inode)->root->sectorsize) - 1;
same_page = ((offset >> PAGE_CACHE_SHIFT) == same_block = (BTRFS_BYTES_TO_BLKS(root->fs_info, offset))
((offset + len - 1) >> PAGE_CACHE_SHIFT)); == (BTRFS_BYTES_TO_BLKS(root->fs_info, offset + len - 1));
/* /*
* We needn't truncate any page which is beyond the end of the file * We needn't truncate any block which is beyond the end of the file
* because we are sure there is no data there. * because we are sure there is no data there.
*/ */
/* /*
* Only do this if we are in the same page and we aren't doing the * Only do this if we are in the same block and we aren't doing the
* entire page. * entire block.
*/ */
if (same_page && len < PAGE_CACHE_SIZE) { if (same_block && len < root->sectorsize) {
if (offset < ino_size) { if (offset < ino_size) {
truncated_page = true; truncated_block = true;
ret = btrfs_truncate_page(inode, offset, len, 0); ret = btrfs_truncate_block(inode, offset, len, 0);
} else { } else {
ret = 0; ret = 0;
} }
goto out_only_mutex; goto out_only_mutex;
} }
/* zero back part of the first page */ /* zero back part of the first block */
if (offset < ino_size) { if (offset < ino_size) {
truncated_page = true; truncated_block = true;
ret = btrfs_truncate_page(inode, offset, 0, 0); ret = btrfs_truncate_block(inode, offset, 0, 0);
if (ret) { if (ret) {
mutex_unlock(&inode->i_mutex); mutex_unlock(&inode->i_mutex);
return ret; return ret;
@ -2393,9 +2392,10 @@ static int btrfs_punch_hole(struct inode *inode, loff_t offset, loff_t len)
if (!ret) { if (!ret) {
/* zero the front end of the last page */ /* zero the front end of the last page */
if (tail_start + tail_len < ino_size) { if (tail_start + tail_len < ino_size) {
truncated_page = true; truncated_block = true;
ret = btrfs_truncate_page(inode, ret = btrfs_truncate_block(inode,
tail_start + tail_len, 0, 1); tail_start + tail_len,
0, 1);
if (ret) if (ret)
goto out_only_mutex; goto out_only_mutex;
} }
@ -2575,7 +2575,7 @@ static int btrfs_punch_hole(struct inode *inode, loff_t offset, loff_t len)
unlock_extent_cached(&BTRFS_I(inode)->io_tree, lockstart, lockend, unlock_extent_cached(&BTRFS_I(inode)->io_tree, lockstart, lockend,
&cached_state, GFP_NOFS); &cached_state, GFP_NOFS);
out_only_mutex: out_only_mutex:
if (!updated_inode && truncated_page && !ret && !err) { if (!updated_inode && truncated_block && !ret && !err) {
/* /*
* If we only end up zeroing part of a page, we still need to * If we only end up zeroing part of a page, we still need to
* update the inode item, so that all the time fields are * update the inode item, so that all the time fields are
@ -2695,10 +2695,10 @@ static long btrfs_fallocate(struct file *file, int mode,
} else if (offset + len > inode->i_size) { } else if (offset + len > inode->i_size) {
/* /*
* If we are fallocating from the end of the file onward we * If we are fallocating from the end of the file onward we
* need to zero out the end of the page if i_size lands in the * need to zero out the end of the block if i_size lands in the
* middle of a page. * middle of a block.
*/ */
ret = btrfs_truncate_page(inode, inode->i_size, 0, 0); ret = btrfs_truncate_block(inode, inode->i_size, 0, 0);
if (ret) if (ret)
goto out; goto out;
} }

View File

@ -4247,7 +4247,8 @@ static int truncate_inline_extent(struct inode *inode,
* read the extent item from disk (data not in the page cache). * read the extent item from disk (data not in the page cache).
*/ */
btrfs_release_path(path); btrfs_release_path(path);
return btrfs_truncate_page(inode, offset, page_end - offset, 0); return btrfs_truncate_block(inode, offset, page_end - offset,
0);
} }
btrfs_set_file_extent_ram_bytes(leaf, fi, size); btrfs_set_file_extent_ram_bytes(leaf, fi, size);
@ -4600,17 +4601,17 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans,
} }
/* /*
* btrfs_truncate_page - read, zero a chunk and write a page * btrfs_truncate_block - read, zero a chunk and write a block
* @inode - inode that we're zeroing * @inode - inode that we're zeroing
* @from - the offset to start zeroing * @from - the offset to start zeroing
* @len - the length to zero, 0 to zero the entire range respective to the * @len - the length to zero, 0 to zero the entire range respective to the
* offset * offset
* @front - zero up to the offset instead of from the offset on * @front - zero up to the offset instead of from the offset on
* *
* This will find the page for the "from" offset and cow the page and zero the * This will find the block for the "from" offset and cow the block and zero the
* part we want to zero. This is used with truncate and hole punching. * part we want to zero. This is used with truncate and hole punching.
*/ */
int btrfs_truncate_page(struct inode *inode, loff_t from, loff_t len, int btrfs_truncate_block(struct inode *inode, loff_t from, loff_t len,
int front) int front)
{ {
struct address_space *mapping = inode->i_mapping; struct address_space *mapping = inode->i_mapping;
@ -4621,18 +4622,19 @@ int btrfs_truncate_page(struct inode *inode, loff_t from, loff_t len,
char *kaddr; char *kaddr;
u32 blocksize = root->sectorsize; u32 blocksize = root->sectorsize;
pgoff_t index = from >> PAGE_CACHE_SHIFT; pgoff_t index = from >> PAGE_CACHE_SHIFT;
unsigned offset = from & (PAGE_CACHE_SIZE-1); unsigned offset = from & (blocksize - 1);
struct page *page; struct page *page;
gfp_t mask = btrfs_alloc_write_mask(mapping); gfp_t mask = btrfs_alloc_write_mask(mapping);
int ret = 0; int ret = 0;
u64 page_start; u64 block_start;
u64 page_end; u64 block_end;
if ((offset & (blocksize - 1)) == 0 && if ((offset & (blocksize - 1)) == 0 &&
(!len || ((len & (blocksize - 1)) == 0))) (!len || ((len & (blocksize - 1)) == 0)))
goto out; goto out;
ret = btrfs_delalloc_reserve_space(inode, ret = btrfs_delalloc_reserve_space(inode,
round_down(from, PAGE_CACHE_SIZE), PAGE_CACHE_SIZE); round_down(from, blocksize), blocksize);
if (ret) if (ret)
goto out; goto out;
@ -4640,14 +4642,14 @@ int btrfs_truncate_page(struct inode *inode, loff_t from, loff_t len,
page = find_or_create_page(mapping, index, mask); page = find_or_create_page(mapping, index, mask);
if (!page) { if (!page) {
btrfs_delalloc_release_space(inode, btrfs_delalloc_release_space(inode,
round_down(from, PAGE_CACHE_SIZE), round_down(from, blocksize),
PAGE_CACHE_SIZE); blocksize);
ret = -ENOMEM; ret = -ENOMEM;
goto out; goto out;
} }
page_start = page_offset(page); block_start = round_down(from, blocksize);
page_end = page_start + PAGE_CACHE_SIZE - 1; block_end = block_start + blocksize - 1;
if (!PageUptodate(page)) { if (!PageUptodate(page)) {
ret = btrfs_readpage(NULL, page); ret = btrfs_readpage(NULL, page);
@ -4664,12 +4666,12 @@ int btrfs_truncate_page(struct inode *inode, loff_t from, loff_t len,
} }
wait_on_page_writeback(page); wait_on_page_writeback(page);
lock_extent_bits(io_tree, page_start, page_end, &cached_state); lock_extent_bits(io_tree, block_start, block_end, &cached_state);
set_page_extent_mapped(page); set_page_extent_mapped(page);
ordered = btrfs_lookup_ordered_extent(inode, page_start); ordered = btrfs_lookup_ordered_extent(inode, block_start);
if (ordered) { if (ordered) {
unlock_extent_cached(io_tree, page_start, page_end, unlock_extent_cached(io_tree, block_start, block_end,
&cached_state, GFP_NOFS); &cached_state, GFP_NOFS);
unlock_page(page); unlock_page(page);
page_cache_release(page); page_cache_release(page);
@ -4678,39 +4680,41 @@ int btrfs_truncate_page(struct inode *inode, loff_t from, loff_t len,
goto again; goto again;
} }
clear_extent_bit(&BTRFS_I(inode)->io_tree, page_start, page_end, clear_extent_bit(&BTRFS_I(inode)->io_tree, block_start, block_end,
EXTENT_DIRTY | EXTENT_DELALLOC | EXTENT_DIRTY | EXTENT_DELALLOC |
EXTENT_DO_ACCOUNTING | EXTENT_DEFRAG, EXTENT_DO_ACCOUNTING | EXTENT_DEFRAG,
0, 0, &cached_state, GFP_NOFS); 0, 0, &cached_state, GFP_NOFS);
ret = btrfs_set_extent_delalloc(inode, page_start, page_end, ret = btrfs_set_extent_delalloc(inode, block_start, block_end,
&cached_state); &cached_state);
if (ret) { if (ret) {
unlock_extent_cached(io_tree, page_start, page_end, unlock_extent_cached(io_tree, block_start, block_end,
&cached_state, GFP_NOFS); &cached_state, GFP_NOFS);
goto out_unlock; goto out_unlock;
} }
if (offset != PAGE_CACHE_SIZE) { if (offset != blocksize) {
if (!len) if (!len)
len = PAGE_CACHE_SIZE - offset; len = blocksize - offset;
kaddr = kmap(page); kaddr = kmap(page);
if (front) if (front)
memset(kaddr, 0, offset); memset(kaddr + (block_start - page_offset(page)),
0, offset);
else else
memset(kaddr + offset, 0, len); memset(kaddr + (block_start - page_offset(page)) + offset,
0, len);
flush_dcache_page(page); flush_dcache_page(page);
kunmap(page); kunmap(page);
} }
ClearPageChecked(page); ClearPageChecked(page);
set_page_dirty(page); set_page_dirty(page);
unlock_extent_cached(io_tree, page_start, page_end, &cached_state, unlock_extent_cached(io_tree, block_start, block_end, &cached_state,
GFP_NOFS); GFP_NOFS);
out_unlock: out_unlock:
if (ret) if (ret)
btrfs_delalloc_release_space(inode, page_start, btrfs_delalloc_release_space(inode, block_start,
PAGE_CACHE_SIZE); blocksize);
unlock_page(page); unlock_page(page);
page_cache_release(page); page_cache_release(page);
out: out:
@ -4781,11 +4785,11 @@ int btrfs_cont_expand(struct inode *inode, loff_t oldsize, loff_t size)
int err = 0; int err = 0;
/* /*
* If our size started in the middle of a page we need to zero out the * If our size started in the middle of a block we need to zero out the
* rest of the page before we expand the i_size, otherwise we could * rest of the block before we expand the i_size, otherwise we could
* expose stale data. * expose stale data.
*/ */
err = btrfs_truncate_page(inode, oldsize, 0, 0); err = btrfs_truncate_block(inode, oldsize, 0, 0);
if (err) if (err)
return err; return err;