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:
parent
2dabb32484
commit
9703fefe0b
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue