diff options
-rw-r--r-- | fs/ext4/ext4.h | 1 | ||||
-rw-r--r-- | fs/ext4/extents.c | 17 | ||||
-rw-r--r-- | fs/ext4/inode.c | 46 | ||||
-rw-r--r-- | fs/ext4/truncate.h | 4 |
4 files changed, 68 insertions, 0 deletions
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 6d7dec48372b..1fc013f3d944 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h | |||
@@ -2459,6 +2459,7 @@ extern int ext4_get_inode_loc(struct inode *, struct ext4_iloc *); | |||
2459 | extern int ext4_inode_attach_jinode(struct inode *inode); | 2459 | extern int ext4_inode_attach_jinode(struct inode *inode); |
2460 | extern int ext4_can_truncate(struct inode *inode); | 2460 | extern int ext4_can_truncate(struct inode *inode); |
2461 | extern int ext4_truncate(struct inode *); | 2461 | extern int ext4_truncate(struct inode *); |
2462 | extern int ext4_break_layouts(struct inode *); | ||
2462 | extern int ext4_punch_hole(struct inode *inode, loff_t offset, loff_t length); | 2463 | extern int ext4_punch_hole(struct inode *inode, loff_t offset, loff_t length); |
2463 | extern int ext4_truncate_restart_trans(handle_t *, struct inode *, int nblocks); | 2464 | extern int ext4_truncate_restart_trans(handle_t *, struct inode *, int nblocks); |
2464 | extern void ext4_set_inode_flags(struct inode *); | 2465 | extern void ext4_set_inode_flags(struct inode *); |
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index 8ce6fd5b10dd..72a361d5ef74 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c | |||
@@ -4826,6 +4826,13 @@ static long ext4_zero_range(struct file *file, loff_t offset, | |||
4826 | * released from page cache. | 4826 | * released from page cache. |
4827 | */ | 4827 | */ |
4828 | down_write(&EXT4_I(inode)->i_mmap_sem); | 4828 | down_write(&EXT4_I(inode)->i_mmap_sem); |
4829 | |||
4830 | ret = ext4_break_layouts(inode); | ||
4831 | if (ret) { | ||
4832 | up_write(&EXT4_I(inode)->i_mmap_sem); | ||
4833 | goto out_mutex; | ||
4834 | } | ||
4835 | |||
4829 | ret = ext4_update_disksize_before_punch(inode, offset, len); | 4836 | ret = ext4_update_disksize_before_punch(inode, offset, len); |
4830 | if (ret) { | 4837 | if (ret) { |
4831 | up_write(&EXT4_I(inode)->i_mmap_sem); | 4838 | up_write(&EXT4_I(inode)->i_mmap_sem); |
@@ -5499,6 +5506,11 @@ int ext4_collapse_range(struct inode *inode, loff_t offset, loff_t len) | |||
5499 | * page cache. | 5506 | * page cache. |
5500 | */ | 5507 | */ |
5501 | down_write(&EXT4_I(inode)->i_mmap_sem); | 5508 | down_write(&EXT4_I(inode)->i_mmap_sem); |
5509 | |||
5510 | ret = ext4_break_layouts(inode); | ||
5511 | if (ret) | ||
5512 | goto out_mmap; | ||
5513 | |||
5502 | /* | 5514 | /* |
5503 | * Need to round down offset to be aligned with page size boundary | 5515 | * Need to round down offset to be aligned with page size boundary |
5504 | * for page size > block size. | 5516 | * for page size > block size. |
@@ -5647,6 +5659,11 @@ int ext4_insert_range(struct inode *inode, loff_t offset, loff_t len) | |||
5647 | * page cache. | 5659 | * page cache. |
5648 | */ | 5660 | */ |
5649 | down_write(&EXT4_I(inode)->i_mmap_sem); | 5661 | down_write(&EXT4_I(inode)->i_mmap_sem); |
5662 | |||
5663 | ret = ext4_break_layouts(inode); | ||
5664 | if (ret) | ||
5665 | goto out_mmap; | ||
5666 | |||
5650 | /* | 5667 | /* |
5651 | * Need to round down to align start offset to page size boundary | 5668 | * Need to round down to align start offset to page size boundary |
5652 | * for page size > block size. | 5669 | * for page size > block size. |
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index ba0de19fb1ad..60432498acfb 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c | |||
@@ -4191,6 +4191,39 @@ int ext4_update_disksize_before_punch(struct inode *inode, loff_t offset, | |||
4191 | return 0; | 4191 | return 0; |
4192 | } | 4192 | } |
4193 | 4193 | ||
4194 | static void ext4_wait_dax_page(struct ext4_inode_info *ei, bool *did_unlock) | ||
4195 | { | ||
4196 | *did_unlock = true; | ||
4197 | up_write(&ei->i_mmap_sem); | ||
4198 | schedule(); | ||
4199 | down_write(&ei->i_mmap_sem); | ||
4200 | } | ||
4201 | |||
4202 | int ext4_break_layouts(struct inode *inode) | ||
4203 | { | ||
4204 | struct ext4_inode_info *ei = EXT4_I(inode); | ||
4205 | struct page *page; | ||
4206 | bool retry; | ||
4207 | int error; | ||
4208 | |||
4209 | if (WARN_ON_ONCE(!rwsem_is_locked(&ei->i_mmap_sem))) | ||
4210 | return -EINVAL; | ||
4211 | |||
4212 | do { | ||
4213 | retry = false; | ||
4214 | page = dax_layout_busy_page(inode->i_mapping); | ||
4215 | if (!page) | ||
4216 | return 0; | ||
4217 | |||
4218 | error = ___wait_var_event(&page->_refcount, | ||
4219 | atomic_read(&page->_refcount) == 1, | ||
4220 | TASK_INTERRUPTIBLE, 0, 0, | ||
4221 | ext4_wait_dax_page(ei, &retry)); | ||
4222 | } while (error == 0 && retry); | ||
4223 | |||
4224 | return error; | ||
4225 | } | ||
4226 | |||
4194 | /* | 4227 | /* |
4195 | * ext4_punch_hole: punches a hole in a file by releasing the blocks | 4228 | * ext4_punch_hole: punches a hole in a file by releasing the blocks |
4196 | * associated with the given offset and length | 4229 | * associated with the given offset and length |
@@ -4264,6 +4297,11 @@ int ext4_punch_hole(struct inode *inode, loff_t offset, loff_t length) | |||
4264 | * page cache. | 4297 | * page cache. |
4265 | */ | 4298 | */ |
4266 | down_write(&EXT4_I(inode)->i_mmap_sem); | 4299 | down_write(&EXT4_I(inode)->i_mmap_sem); |
4300 | |||
4301 | ret = ext4_break_layouts(inode); | ||
4302 | if (ret) | ||
4303 | goto out_dio; | ||
4304 | |||
4267 | first_block_offset = round_up(offset, sb->s_blocksize); | 4305 | first_block_offset = round_up(offset, sb->s_blocksize); |
4268 | last_block_offset = round_down((offset + length), sb->s_blocksize) - 1; | 4306 | last_block_offset = round_down((offset + length), sb->s_blocksize) - 1; |
4269 | 4307 | ||
@@ -5553,6 +5591,14 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr) | |||
5553 | ext4_wait_for_tail_page_commit(inode); | 5591 | ext4_wait_for_tail_page_commit(inode); |
5554 | } | 5592 | } |
5555 | down_write(&EXT4_I(inode)->i_mmap_sem); | 5593 | down_write(&EXT4_I(inode)->i_mmap_sem); |
5594 | |||
5595 | rc = ext4_break_layouts(inode); | ||
5596 | if (rc) { | ||
5597 | up_write(&EXT4_I(inode)->i_mmap_sem); | ||
5598 | error = rc; | ||
5599 | goto err_out; | ||
5600 | } | ||
5601 | |||
5556 | /* | 5602 | /* |
5557 | * Truncate pagecache after we've waited for commit | 5603 | * Truncate pagecache after we've waited for commit |
5558 | * in data=journal mode to make pages freeable. | 5604 | * in data=journal mode to make pages freeable. |
diff --git a/fs/ext4/truncate.h b/fs/ext4/truncate.h index 0cb13badf473..bcbe3668c1d4 100644 --- a/fs/ext4/truncate.h +++ b/fs/ext4/truncate.h | |||
@@ -11,6 +11,10 @@ | |||
11 | */ | 11 | */ |
12 | static inline void ext4_truncate_failed_write(struct inode *inode) | 12 | static inline void ext4_truncate_failed_write(struct inode *inode) |
13 | { | 13 | { |
14 | /* | ||
15 | * We don't need to call ext4_break_layouts() because the blocks we | ||
16 | * are truncating were never visible to userspace. | ||
17 | */ | ||
14 | down_write(&EXT4_I(inode)->i_mmap_sem); | 18 | down_write(&EXT4_I(inode)->i_mmap_sem); |
15 | truncate_inode_pages(inode->i_mapping, inode->i_size); | 19 | truncate_inode_pages(inode->i_mapping, inode->i_size); |
16 | ext4_truncate(inode); | 20 | ext4_truncate(inode); |