Skip to content

Commit 11ef08c

Browse files
committed
Merge branch 'delalloc-buffer-write' into dev
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 <[email protected]>
2 parents 1fd95c0 + cc88323 commit 11ef08c

File tree

3 files changed

+105
-179
lines changed

3 files changed

+105
-179
lines changed

fs/ext4/ext4.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3603,9 +3603,6 @@ extern int ext4_da_write_inline_data_begin(struct address_space *mapping,
36033603
unsigned flags,
36043604
struct page **pagep,
36053605
void **fsdata);
3606-
extern int ext4_da_write_inline_data_end(struct inode *inode, loff_t pos,
3607-
unsigned len, unsigned copied,
3608-
struct page *page);
36093606
extern int ext4_try_add_inline_entry(handle_t *handle,
36103607
struct ext4_filename *fname,
36113608
struct inode *dir, struct inode *inode);

fs/ext4/inline.c

Lines changed: 66 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -733,45 +733,83 @@ int ext4_try_to_write_inline_data(struct address_space *mapping,
733733
int ext4_write_inline_data_end(struct inode *inode, loff_t pos, unsigned len,
734734
unsigned copied, struct page *page)
735735
{
736-
int ret, no_expand;
736+
handle_t *handle = ext4_journal_current_handle();
737+
int no_expand;
737738
void *kaddr;
738739
struct ext4_iloc iloc;
740+
int ret = 0, ret2;
741+
742+
if (unlikely(copied < len) && !PageUptodate(page))
743+
copied = 0;
739744

740-
if (unlikely(copied < len)) {
741-
if (!PageUptodate(page)) {
742-
copied = 0;
745+
if (likely(copied)) {
746+
ret = ext4_get_inode_loc(inode, &iloc);
747+
if (ret) {
748+
unlock_page(page);
749+
put_page(page);
750+
ext4_std_error(inode->i_sb, ret);
743751
goto out;
744752
}
745-
}
753+
ext4_write_lock_xattr(inode, &no_expand);
754+
BUG_ON(!ext4_has_inline_data(inode));
746755

747-
ret = ext4_get_inode_loc(inode, &iloc);
748-
if (ret) {
749-
ext4_std_error(inode->i_sb, ret);
750-
copied = 0;
751-
goto out;
752-
}
756+
/*
757+
* ei->i_inline_off may have changed since
758+
* ext4_write_begin() called
759+
* ext4_try_to_write_inline_data()
760+
*/
761+
(void) ext4_find_inline_data_nolock(inode);
753762

754-
ext4_write_lock_xattr(inode, &no_expand);
755-
BUG_ON(!ext4_has_inline_data(inode));
763+
kaddr = kmap_atomic(page);
764+
ext4_write_inline_data(inode, &iloc, kaddr, pos, copied);
765+
kunmap_atomic(kaddr);
766+
SetPageUptodate(page);
767+
/* clear page dirty so that writepages wouldn't work for us. */
768+
ClearPageDirty(page);
756769

757-
/*
758-
* ei->i_inline_off may have changed since ext4_write_begin()
759-
* called ext4_try_to_write_inline_data()
760-
*/
761-
(void) ext4_find_inline_data_nolock(inode);
770+
ext4_write_unlock_xattr(inode, &no_expand);
771+
brelse(iloc.bh);
762772

763-
kaddr = kmap_atomic(page);
764-
ext4_write_inline_data(inode, &iloc, kaddr, pos, len);
765-
kunmap_atomic(kaddr);
766-
SetPageUptodate(page);
767-
/* clear page dirty so that writepages wouldn't work for us. */
768-
ClearPageDirty(page);
773+
/*
774+
* It's important to update i_size while still holding page
775+
* lock: page writeout could otherwise come in and zero
776+
* beyond i_size.
777+
*/
778+
ext4_update_inode_size(inode, pos + copied);
779+
}
780+
unlock_page(page);
781+
put_page(page);
769782

770-
ext4_write_unlock_xattr(inode, &no_expand);
771-
brelse(iloc.bh);
772-
mark_inode_dirty(inode);
783+
/*
784+
* Don't mark the inode dirty under page lock. First, it unnecessarily
785+
* makes the holding time of page lock longer. Second, it forces lock
786+
* ordering of page lock and transaction start for journaling
787+
* filesystems.
788+
*/
789+
if (likely(copied))
790+
mark_inode_dirty(inode);
773791
out:
774-
return copied;
792+
/*
793+
* If we didn't copy as much data as expected, we need to trim back
794+
* size of xattr containing inline data.
795+
*/
796+
if (pos + len > inode->i_size && ext4_can_truncate(inode))
797+
ext4_orphan_add(handle, inode);
798+
799+
ret2 = ext4_journal_stop(handle);
800+
if (!ret)
801+
ret = ret2;
802+
if (pos + len > inode->i_size) {
803+
ext4_truncate_failed_write(inode);
804+
/*
805+
* If truncate failed early the inode might still be
806+
* on the orphan list; we need to make sure the inode
807+
* is removed from the orphan list in that case.
808+
*/
809+
if (inode->i_nlink)
810+
ext4_orphan_del(NULL, inode);
811+
}
812+
return ret ? ret : copied;
775813
}
776814

777815
struct buffer_head *
@@ -953,43 +991,6 @@ int ext4_da_write_inline_data_begin(struct address_space *mapping,
953991
return ret;
954992
}
955993

956-
int ext4_da_write_inline_data_end(struct inode *inode, loff_t pos,
957-
unsigned len, unsigned copied,
958-
struct page *page)
959-
{
960-
int ret;
961-
962-
ret = ext4_write_inline_data_end(inode, pos, len, copied, page);
963-
if (ret < 0) {
964-
unlock_page(page);
965-
put_page(page);
966-
return ret;
967-
}
968-
copied = ret;
969-
970-
/*
971-
* No need to use i_size_read() here, the i_size
972-
* cannot change under us because we hold i_mutex.
973-
*
974-
* But it's important to update i_size while still holding page lock:
975-
* page writeout could otherwise come in and zero beyond i_size.
976-
*/
977-
if (pos+copied > inode->i_size)
978-
i_size_write(inode, pos+copied);
979-
unlock_page(page);
980-
put_page(page);
981-
982-
/*
983-
* Don't mark the inode dirty under page lock. First, it unnecessarily
984-
* makes the holding time of page lock longer. Second, it forces lock
985-
* ordering of page lock and transaction start for journaling
986-
* filesystems.
987-
*/
988-
mark_inode_dirty(inode);
989-
990-
return copied;
991-
}
992-
993994
#ifdef INLINE_DIR_DEBUG
994995
void ext4_show_inline_dir(struct inode *dir, struct buffer_head *bh,
995996
void *inline_start, int inline_size)

0 commit comments

Comments
 (0)