Skip to content

Commit

Permalink
Merge branch 'delalloc-buffer-write' into dev
Browse files Browse the repository at this point in the history
Fix a bug in how we update i_disksize, and the error path in
inline_data_end.  Finally, drop an unnecessary creation of a journal
handle which was only needed for inline data, which can give us a
large performance gain in delayed allocation writes.

Signed-off-by: Theodore Ts'o <tytso@mit.edu>
  • Loading branch information
Theodore Ts'o committed Sep 9, 2021
2 parents 1fd95c0 + cc88323 commit 11ef08c
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 179 deletions.
3 changes: 0 additions & 3 deletions fs/ext4/ext4.h
Original file line number Diff line number Diff line change
Expand Up @@ -3603,9 +3603,6 @@ extern int ext4_da_write_inline_data_begin(struct address_space *mapping,
unsigned flags,
struct page **pagep,
void **fsdata);
extern int ext4_da_write_inline_data_end(struct inode *inode, loff_t pos,
unsigned len, unsigned copied,
struct page *page);
extern int ext4_try_add_inline_entry(handle_t *handle,
struct ext4_filename *fname,
struct inode *dir, struct inode *inode);
Expand Down
131 changes: 66 additions & 65 deletions fs/ext4/inline.c
Original file line number Diff line number Diff line change
Expand Up @@ -733,45 +733,83 @@ int ext4_try_to_write_inline_data(struct address_space *mapping,
int ext4_write_inline_data_end(struct inode *inode, loff_t pos, unsigned len,
unsigned copied, struct page *page)
{
int ret, no_expand;
handle_t *handle = ext4_journal_current_handle();
int no_expand;
void *kaddr;
struct ext4_iloc iloc;
int ret = 0, ret2;

if (unlikely(copied < len) && !PageUptodate(page))
copied = 0;

if (unlikely(copied < len)) {
if (!PageUptodate(page)) {
copied = 0;
if (likely(copied)) {
ret = ext4_get_inode_loc(inode, &iloc);
if (ret) {
unlock_page(page);
put_page(page);
ext4_std_error(inode->i_sb, ret);
goto out;
}
}
ext4_write_lock_xattr(inode, &no_expand);
BUG_ON(!ext4_has_inline_data(inode));

ret = ext4_get_inode_loc(inode, &iloc);
if (ret) {
ext4_std_error(inode->i_sb, ret);
copied = 0;
goto out;
}
/*
* ei->i_inline_off may have changed since
* ext4_write_begin() called
* ext4_try_to_write_inline_data()
*/
(void) ext4_find_inline_data_nolock(inode);

ext4_write_lock_xattr(inode, &no_expand);
BUG_ON(!ext4_has_inline_data(inode));
kaddr = kmap_atomic(page);
ext4_write_inline_data(inode, &iloc, kaddr, pos, copied);
kunmap_atomic(kaddr);
SetPageUptodate(page);
/* clear page dirty so that writepages wouldn't work for us. */
ClearPageDirty(page);

/*
* ei->i_inline_off may have changed since ext4_write_begin()
* called ext4_try_to_write_inline_data()
*/
(void) ext4_find_inline_data_nolock(inode);
ext4_write_unlock_xattr(inode, &no_expand);
brelse(iloc.bh);

kaddr = kmap_atomic(page);
ext4_write_inline_data(inode, &iloc, kaddr, pos, len);
kunmap_atomic(kaddr);
SetPageUptodate(page);
/* clear page dirty so that writepages wouldn't work for us. */
ClearPageDirty(page);
/*
* It's important to update i_size while still holding page
* lock: page writeout could otherwise come in and zero
* beyond i_size.
*/
ext4_update_inode_size(inode, pos + copied);
}
unlock_page(page);
put_page(page);

ext4_write_unlock_xattr(inode, &no_expand);
brelse(iloc.bh);
mark_inode_dirty(inode);
/*
* Don't mark the inode dirty under page lock. First, it unnecessarily
* makes the holding time of page lock longer. Second, it forces lock
* ordering of page lock and transaction start for journaling
* filesystems.
*/
if (likely(copied))
mark_inode_dirty(inode);
out:
return copied;
/*
* If we didn't copy as much data as expected, we need to trim back
* size of xattr containing inline data.
*/
if (pos + len > inode->i_size && ext4_can_truncate(inode))
ext4_orphan_add(handle, inode);

ret2 = ext4_journal_stop(handle);
if (!ret)
ret = ret2;
if (pos + len > inode->i_size) {
ext4_truncate_failed_write(inode);
/*
* If truncate failed early the inode might still be
* on the orphan list; we need to make sure the inode
* is removed from the orphan list in that case.
*/
if (inode->i_nlink)
ext4_orphan_del(NULL, inode);
}
return ret ? ret : copied;
}

struct buffer_head *
Expand Down Expand Up @@ -953,43 +991,6 @@ int ext4_da_write_inline_data_begin(struct address_space *mapping,
return ret;
}

int ext4_da_write_inline_data_end(struct inode *inode, loff_t pos,
unsigned len, unsigned copied,
struct page *page)
{
int ret;

ret = ext4_write_inline_data_end(inode, pos, len, copied, page);
if (ret < 0) {
unlock_page(page);
put_page(page);
return ret;
}
copied = ret;

/*
* No need to use i_size_read() here, the i_size
* cannot change under us because we hold i_mutex.
*
* But it's important to update i_size while still holding page lock:
* page writeout could otherwise come in and zero beyond i_size.
*/
if (pos+copied > inode->i_size)
i_size_write(inode, pos+copied);
unlock_page(page);
put_page(page);

/*
* Don't mark the inode dirty under page lock. First, it unnecessarily
* makes the holding time of page lock longer. Second, it forces lock
* ordering of page lock and transaction start for journaling
* filesystems.
*/
mark_inode_dirty(inode);

return copied;
}

#ifdef INLINE_DIR_DEBUG
void ext4_show_inline_dir(struct inode *dir, struct buffer_head *bh,
void *inline_start, int inline_size)
Expand Down
Loading

0 comments on commit 11ef08c

Please sign in to comment.