Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 377569
b: refs/heads/master
c: a87dd18
h: refs/heads/master
i:
  377567: 262c9fd
v: v3
  • Loading branch information
Lukas Czerner authored and Theodore Ts'o committed May 28, 2013
1 parent 030fcf1 commit c087bb8
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 73 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 55f252c9f50e998f6bc3aadc7806f049f7443d21
refs/heads/master: a87dd18ce24dee5da1e9eb44bf8d8d48e0957efd
2 changes: 2 additions & 0 deletions trunk/fs/ext4/ext4.h
Original file line number Diff line number Diff line change
Expand Up @@ -2100,6 +2100,8 @@ extern int ext4_block_truncate_page(handle_t *handle,
struct address_space *mapping, loff_t from);
extern int ext4_block_zero_page_range(handle_t *handle,
struct address_space *mapping, loff_t from, loff_t length);
extern int ext4_zero_partial_blocks(handle_t *handle, struct inode *inode,
loff_t lstart, loff_t lend);
extern int ext4_discard_partial_page_buffers(handle_t *handle,
struct address_space *mapping, loff_t from,
loff_t length, int flags);
Expand Down
118 changes: 46 additions & 72 deletions trunk/fs/ext4/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -3693,6 +3693,41 @@ int ext4_block_zero_page_range(handle_t *handle,
return err;
}

int ext4_zero_partial_blocks(handle_t *handle, struct inode *inode,
loff_t lstart, loff_t length)
{
struct super_block *sb = inode->i_sb;
struct address_space *mapping = inode->i_mapping;
unsigned partial = lstart & (sb->s_blocksize - 1);
ext4_fsblk_t start, end;
loff_t byte_end = (lstart + length - 1);
int err = 0;

start = lstart >> sb->s_blocksize_bits;
end = byte_end >> sb->s_blocksize_bits;

/* Handle partial zero within the single block */
if (start == end) {
err = ext4_block_zero_page_range(handle, mapping,
lstart, length);
return err;
}
/* Handle partial zero out on the start of the range */
if (partial) {
err = ext4_block_zero_page_range(handle, mapping,
lstart, sb->s_blocksize);
if (err)
return err;
}
/* Handle partial zero out on the end of the range */
partial = byte_end & (sb->s_blocksize - 1);
if (partial != sb->s_blocksize - 1)
err = ext4_block_zero_page_range(handle, mapping,
byte_end - partial,
partial + 1);
return err;
}

int ext4_can_truncate(struct inode *inode)
{
if (S_ISREG(inode->i_mode))
Expand Down Expand Up @@ -3721,8 +3756,7 @@ int ext4_punch_hole(struct file *file, loff_t offset, loff_t length)
struct super_block *sb = inode->i_sb;
ext4_lblk_t first_block, stop_block;
struct address_space *mapping = inode->i_mapping;
loff_t first_page, last_page, page_len;
loff_t first_page_offset, last_page_offset;
loff_t first_block_offset, last_block_offset;
handle_t *handle;
unsigned int credits;
int ret = 0;
Expand Down Expand Up @@ -3773,17 +3807,13 @@ int ext4_punch_hole(struct file *file, loff_t offset, loff_t length)
offset;
}

first_page = (offset + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
last_page = (offset + length) >> PAGE_CACHE_SHIFT;
first_block_offset = round_up(offset, sb->s_blocksize);
last_block_offset = round_down((offset + length), sb->s_blocksize) - 1;

first_page_offset = first_page << PAGE_CACHE_SHIFT;
last_page_offset = last_page << PAGE_CACHE_SHIFT;

/* Now release the pages */
if (last_page_offset > first_page_offset) {
truncate_pagecache_range(inode, first_page_offset,
last_page_offset - 1);
}
/* Now release the pages and zero block aligned part of pages*/
if (last_block_offset > first_block_offset)
truncate_pagecache_range(inode, first_block_offset,
last_block_offset);

/* Wait all existing dio workers, newcomers will block on i_mutex */
ext4_inode_block_unlocked_dio(inode);
Expand All @@ -3803,66 +3833,10 @@ int ext4_punch_hole(struct file *file, loff_t offset, loff_t length)
goto out_dio;
}

/*
* Now we need to zero out the non-page-aligned data in the
* pages at the start and tail of the hole, and unmap the
* buffer heads for the block aligned regions of the page that
* were completely zeroed.
*/
if (first_page > last_page) {
/*
* If the file space being truncated is contained
* within a page just zero out and unmap the middle of
* that page
*/
ret = ext4_discard_partial_page_buffers(handle,
mapping, offset, length, 0);

if (ret)
goto out_stop;
} else {
/*
* zero out and unmap the partial page that contains
* the start of the hole
*/
page_len = first_page_offset - offset;
if (page_len > 0) {
ret = ext4_discard_partial_page_buffers(handle, mapping,
offset, page_len, 0);
if (ret)
goto out_stop;
}

/*
* zero out and unmap the partial page that contains
* the end of the hole
*/
page_len = offset + length - last_page_offset;
if (page_len > 0) {
ret = ext4_discard_partial_page_buffers(handle, mapping,
last_page_offset, page_len, 0);
if (ret)
goto out_stop;
}
}

/*
* If i_size is contained in the last page, we need to
* unmap and zero the partial page after i_size
*/
if (inode->i_size >> PAGE_CACHE_SHIFT == last_page &&
inode->i_size % PAGE_CACHE_SIZE != 0) {
page_len = PAGE_CACHE_SIZE -
(inode->i_size & (PAGE_CACHE_SIZE - 1));

if (page_len > 0) {
ret = ext4_discard_partial_page_buffers(handle,
mapping, inode->i_size, page_len, 0);

if (ret)
goto out_stop;
}
}
ret = ext4_zero_partial_blocks(handle, inode, offset,
length);
if (ret)
goto out_stop;

first_block = (offset + sb->s_blocksize - 1) >>
EXT4_BLOCK_SIZE_BITS(sb);
Expand Down

0 comments on commit c087bb8

Please sign in to comment.