On Mon, Dec 16, 2024 at 09:39:15AM +0800, Zhang Yi wrote: > From: Zhang Yi <yi.zhang@xxxxxxxxxx> > > Currently, all zeroing ranges, punch holes, collapse ranges, and insert > ranges first wait for all existing direct I/O workers to complete, and > then they acquire the mapping's invalidate lock before performing the > actual work. These common components are nearly identical, so we can > simplify the code by factoring them out into the ext4_fallocate(). > I really like the long due cleanup of fallocate paths. Thanks for taking it up! Feel free to add: Reviewed-by: Ojaswin Mujoo <ojaswin@xxxxxxxxxxxxx> > Signed-off-by: Zhang Yi <yi.zhang@xxxxxxxxxx> > Reviewed-by: Jan Kara <jack@xxxxxxx> > --- > fs/ext4/extents.c | 124 ++++++++++++++++------------------------------ > fs/ext4/inode.c | 25 ++-------- > 2 files changed, 45 insertions(+), 104 deletions(-) > > diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c > index 85f0de1abe78..1b028be19193 100644 > --- a/fs/ext4/extents.c > +++ b/fs/ext4/extents.c > @@ -4568,7 +4568,6 @@ static long ext4_zero_range(struct file *file, loff_t offset, > loff_t len, int mode) > { > struct inode *inode = file_inode(file); > - struct address_space *mapping = file->f_mapping; > handle_t *handle = NULL; > loff_t new_size = 0; > loff_t end = offset + len; > @@ -4592,23 +4591,6 @@ static long ext4_zero_range(struct file *file, loff_t offset, > return ret; > } > > - /* Wait all existing dio workers, newcomers will block on i_rwsem */ > - inode_dio_wait(inode); > - > - ret = file_modified(file); > - if (ret) > - return ret; > - > - /* > - * Prevent page faults from reinstantiating pages we have released > - * from page cache. > - */ > - filemap_invalidate_lock(mapping); > - > - ret = ext4_break_layouts(inode); > - if (ret) > - goto out_invalidate_lock; > - > flags = EXT4_GET_BLOCKS_CREATE_UNWRIT_EXT; > /* Preallocate the range including the unaligned edges */ > if (!IS_ALIGNED(offset | end, blocksize)) { > @@ -4618,17 +4600,17 @@ static long ext4_zero_range(struct file *file, loff_t offset, > ret = ext4_alloc_file_blocks(file, alloc_lblk, len_lblk, > new_size, flags); > if (ret) > - goto out_invalidate_lock; > + return ret; > } > > ret = ext4_update_disksize_before_punch(inode, offset, len); > if (ret) > - goto out_invalidate_lock; > + return ret; > > /* Now release the pages and zero block aligned part of pages */ > ret = ext4_truncate_page_cache_block_range(inode, offset, end); > if (ret) > - goto out_invalidate_lock; > + return ret; > > /* Zero range excluding the unaligned edges */ > start_lblk = EXT4_B_TO_LBLK(inode, offset); > @@ -4640,11 +4622,11 @@ static long ext4_zero_range(struct file *file, loff_t offset, > ret = ext4_alloc_file_blocks(file, start_lblk, zero_blks, > new_size, flags); > if (ret) > - goto out_invalidate_lock; > + return ret; > } > /* Finish zeroing out if it doesn't contain partial block */ > if (IS_ALIGNED(offset | end, blocksize)) > - goto out_invalidate_lock; > + return ret; > > /* > * In worst case we have to writeout two nonadjacent unwritten > @@ -4657,7 +4639,7 @@ static long ext4_zero_range(struct file *file, loff_t offset, > if (IS_ERR(handle)) { > ret = PTR_ERR(handle); > ext4_std_error(inode->i_sb, ret); > - goto out_invalidate_lock; > + return ret; > } > > /* Zero out partial block at the edges of the range */ > @@ -4677,8 +4659,6 @@ static long ext4_zero_range(struct file *file, loff_t offset, > > out_handle: > ext4_journal_stop(handle); > -out_invalidate_lock: > - filemap_invalidate_unlock(mapping); > return ret; > } > > @@ -4711,13 +4691,6 @@ static long ext4_do_fallocate(struct file *file, loff_t offset, > goto out; > } > > - /* Wait all existing dio workers, newcomers will block on i_rwsem */ > - inode_dio_wait(inode); > - > - ret = file_modified(file); > - if (ret) > - goto out; > - > ret = ext4_alloc_file_blocks(file, start_lblk, len_lblk, new_size, > EXT4_GET_BLOCKS_CREATE_UNWRIT_EXT); > if (ret) > @@ -4742,6 +4715,7 @@ static long ext4_do_fallocate(struct file *file, loff_t offset, > long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len) > { > struct inode *inode = file_inode(file); > + struct address_space *mapping = file->f_mapping; > int ret; > > /* > @@ -4765,6 +4739,29 @@ long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len) > if (ret) > goto out_inode_lock; > > + /* Wait all existing dio workers, newcomers will block on i_rwsem */ > + inode_dio_wait(inode); > + > + ret = file_modified(file); > + if (ret) > + return ret; > + > + if ((mode & FALLOC_FL_MODE_MASK) == FALLOC_FL_ALLOCATE_RANGE) { > + ret = ext4_do_fallocate(file, offset, len, mode); > + goto out_inode_lock; > + } > + > + /* > + * Follow-up operations will drop page cache, hold invalidate lock > + * to prevent page faults from reinstantiating pages we have > + * released from page cache. > + */ > + filemap_invalidate_lock(mapping); > + > + ret = ext4_break_layouts(inode); > + if (ret) > + goto out_invalidate_lock; > + > if (mode & FALLOC_FL_PUNCH_HOLE) > ret = ext4_punch_hole(file, offset, len); > else if (mode & FALLOC_FL_COLLAPSE_RANGE) > @@ -4774,7 +4771,10 @@ long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len) > else if (mode & FALLOC_FL_ZERO_RANGE) > ret = ext4_zero_range(file, offset, len, mode); > else > - ret = ext4_do_fallocate(file, offset, len, mode); > + ret = -EOPNOTSUPP; > + > +out_invalidate_lock: > + filemap_invalidate_unlock(mapping); > out_inode_lock: > inode_unlock(inode); > return ret; > @@ -5301,23 +5301,6 @@ static int ext4_collapse_range(struct file *file, loff_t offset, loff_t len) > if (end >= inode->i_size) > return -EINVAL; > > - /* Wait for existing dio to complete */ > - inode_dio_wait(inode); > - > - ret = file_modified(file); > - if (ret) > - return ret; > - > - /* > - * Prevent page faults from reinstantiating pages we have released from > - * page cache. > - */ > - filemap_invalidate_lock(mapping); > - > - ret = ext4_break_layouts(inode); > - if (ret) > - goto out_invalidate_lock; > - > /* > * Write tail of the last page before removed range and data that > * will be shifted since they will get removed from the page cache > @@ -5331,16 +5314,15 @@ static int ext4_collapse_range(struct file *file, loff_t offset, loff_t len) > if (!ret) > ret = filemap_write_and_wait_range(mapping, end, LLONG_MAX); > if (ret) > - goto out_invalidate_lock; > + return ret; > > truncate_pagecache(inode, start); > > credits = ext4_writepage_trans_blocks(inode); > handle = ext4_journal_start(inode, EXT4_HT_TRUNCATE, credits); > - if (IS_ERR(handle)) { > - ret = PTR_ERR(handle); > - goto out_invalidate_lock; > - } > + if (IS_ERR(handle)) > + return PTR_ERR(handle); > + > ext4_fc_mark_ineligible(sb, EXT4_FC_REASON_FALLOC_RANGE, handle); > > start_lblk = offset >> inode->i_blkbits; > @@ -5379,8 +5361,6 @@ static int ext4_collapse_range(struct file *file, loff_t offset, loff_t len) > > out_handle: > ext4_journal_stop(handle); > -out_invalidate_lock: > - filemap_invalidate_unlock(mapping); > return ret; > } > > @@ -5421,23 +5401,6 @@ static int ext4_insert_range(struct file *file, loff_t offset, loff_t len) > if (len > inode->i_sb->s_maxbytes - inode->i_size) > return -EFBIG; > > - /* Wait for existing dio to complete */ > - inode_dio_wait(inode); > - > - ret = file_modified(file); > - if (ret) > - return ret; > - > - /* > - * Prevent page faults from reinstantiating pages we have released from > - * page cache. > - */ > - filemap_invalidate_lock(mapping); > - > - ret = ext4_break_layouts(inode); > - if (ret) > - goto out_invalidate_lock; > - > /* > * Write out all dirty pages. Need to round down to align start offset > * to page size boundary for page size > block size. > @@ -5445,16 +5408,15 @@ static int ext4_insert_range(struct file *file, loff_t offset, loff_t len) > start = round_down(offset, PAGE_SIZE); > ret = filemap_write_and_wait_range(mapping, start, LLONG_MAX); > if (ret) > - goto out_invalidate_lock; > + return ret; > > truncate_pagecache(inode, start); > > credits = ext4_writepage_trans_blocks(inode); > handle = ext4_journal_start(inode, EXT4_HT_TRUNCATE, credits); > - if (IS_ERR(handle)) { > - ret = PTR_ERR(handle); > - goto out_invalidate_lock; > - } > + if (IS_ERR(handle)) > + return PTR_ERR(handle); > + > ext4_fc_mark_ineligible(sb, EXT4_FC_REASON_FALLOC_RANGE, handle); > > /* Expand file to avoid data loss if there is error while shifting */ > @@ -5525,8 +5487,6 @@ static int ext4_insert_range(struct file *file, loff_t offset, loff_t len) > > out_handle: > ext4_journal_stop(handle); > -out_invalidate_lock: > - filemap_invalidate_unlock(mapping); > return ret; > } > > diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c > index 2e1f070ab449..2677a2cace58 100644 > --- a/fs/ext4/inode.c > +++ b/fs/ext4/inode.c > @@ -4009,7 +4009,6 @@ int ext4_punch_hole(struct file *file, loff_t offset, loff_t length) > struct inode *inode = file_inode(file); > struct super_block *sb = inode->i_sb; > ext4_lblk_t start_lblk, end_lblk; > - struct address_space *mapping = inode->i_mapping; > loff_t max_end = EXT4_SB(sb)->s_bitmap_maxbytes - sb->s_blocksize; > loff_t end = offset + length; > handle_t *handle; > @@ -4044,31 +4043,15 @@ int ext4_punch_hole(struct file *file, loff_t offset, loff_t length) > return ret; > } > > - /* Wait all existing dio workers, newcomers will block on i_rwsem */ > - inode_dio_wait(inode); > - > - ret = file_modified(file); > - if (ret) > - return ret; > - > - /* > - * Prevent page faults from reinstantiating pages we have released from > - * page cache. > - */ > - filemap_invalidate_lock(mapping); > - > - ret = ext4_break_layouts(inode); > - if (ret) > - goto out_invalidate_lock; > > ret = ext4_update_disksize_before_punch(inode, offset, length); > if (ret) > - goto out_invalidate_lock; > + return ret; > > /* Now release the pages and zero block aligned part of pages*/ > ret = ext4_truncate_page_cache_block_range(inode, offset, end); > if (ret) > - goto out_invalidate_lock; > + return ret; > > if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) > credits = ext4_writepage_trans_blocks(inode); > @@ -4078,7 +4061,7 @@ int ext4_punch_hole(struct file *file, loff_t offset, loff_t length) > if (IS_ERR(handle)) { > ret = PTR_ERR(handle); > ext4_std_error(sb, ret); > - goto out_invalidate_lock; > + return ret; > } > > ret = ext4_zero_partial_blocks(handle, inode, offset, length); > @@ -4123,8 +4106,6 @@ int ext4_punch_hole(struct file *file, loff_t offset, loff_t length) > ext4_handle_sync(handle); > out_handle: > ext4_journal_stop(handle); > -out_invalidate_lock: > - filemap_invalidate_unlock(mapping); > return ret; > } > > -- > 2.46.1 >