ext4: Speedup orphan file handling
Jan Kara
reviewerv5 cover ext4: Speedup orphan file handling
Hello,
Here is a fourth version of my series to speed up orphan inode handling in ext4.
Orphan inode handling in ext4 is a bottleneck for workloads which heavily excercise truncate / unlink of small files as they contend on global s_orphan_mutex (when you have fast enough storage). This patch set implements new way of handling orphan inodes - instead of using a linked list, we store inode numbers of orphaned inodes in a file which is possible to implement in a more scalable manner than linked list manipulations. See description of patch 3/5 for more details.
The patch set achieves significant gains both for a micro benchmark stressing orphan inode handling (truncating file byte-by-byte, several threads in parallel) and for reaim creat_clo workload. I'm happy for any review, thoughts, ideas about the patches. I have also implemented full support in e2fsprogs which I'll send separately.
Honza
[1] https://lore.kernel.org/lkml/20210227120804.GB22871@xsang-OptiPlex-9020/
Changes since v4:
* Rebased on top of v5.14-rc5
* Updated commit message of patch 1/5
* Added Reviewed-by tags from Ted
Changes since v3:
* Added documentation about on-disk format changes
* Add physical block number into orphan block checksum
* Improve some sanity checks, handling of corrupted orphan file
* Improved some changelogs
Changes since v2:
* Updated some comments
* Rebased onto 5.13-rc5
* Change orphan file inode from a fixed inode number to inode number stored in the superblock
Changes since v1:
* orphan blocks have now magic numbers
* split out orphan handling to a separate source file
* some smaller updates according to review
Previous versions:
Jan Kara
reviewerPATCH v5 1/5 ext4: Support for checksumming from journal triggers
JBD2 layer support triggers which are called when journaling layer moves buffer to a certain state. We can use the frozen trigger, which gets called when buffer data is frozen and about to be written out to the journal, to compute block checksums for some buffer types (similarly as does ocfs2). This avoids unnecessary repeated recomputation of the checksum (at the cost of larger window where memory corruption won't be caught by checksumming) and is even necessary when there are unsynchronized updaters of the checksummed data.
So add superblock and journal trigger type arguments to ext4_journal_get_write_access() and ext4_journal_get_create_access() so that frozen triggers can be set accordingly. Also add inode argument to ext4_walk_page_buffers() and all the callbacks used with that function for the same purpose. This patch is mostly only a change of prototype of the above mentioned functions and a few small helpers. Real checksumming will come later.
--- fs/ext4/ext4.h | 26 ++++++++++++-- fs/ext4/ext4_jbd2.c | 43 +++++++++++++++------- fs/ext4/ext4_jbd2.h | 18 ++++++---- fs/ext4/extents.c | 12 ++++--- fs/ext4/file.c | 3 +- fs/ext4/ialloc.c | 19 ++++++---- fs/ext4/indirect.c | 15 +++++--- fs/ext4/inline.c | 26 +++++++++----- fs/ext4/inode.c | 84 +++++++++++++++++++++++++------------------ fs/ext4/ioctl.c | 4 ++- fs/ext4/mballoc.c | 15 ++++---- fs/ext4/namei.c | 40 +++++++++++++-------- fs/ext4/resize.c | 38 ++++++++++++-------- fs/ext4/super.c | 16 ++++++++- fs/ext4/xattr.c | 26 +++++++++----- fs/jbd2/transaction.c | 2 +- 16 files changed, 259 insertions(+), 128 deletions(-)
fs/ext4/ext4.h +24 -2
@@ -1447,6 +1447,24 @@ struct ext4_super_block {#define EXT4_ENC_UTF8_12_1 1 +/* Types of ext4 journal triggers */ +enum ext4_journal_trigger_type { +EXT4_JTR_NONE /* This must be the last entry for indexing to work! */ +}; + +#define EXT4_JOURNAL_TRIGGER_COUNT EXT4_JTR_NONE + +struct ext4_journal_trigger { +struct jbd2_buffer_trigger_type tr_triggers; +struct super_block *sb; +}; + +static inline struct ext4_journal_trigger *EXT4_TRIGGER ( +struct jbd2_buffer_trigger_type *trigger) +{ + return container_of(trigger, struct ext4_journal_trigger, tr_triggers); +} +/* * fourth extended-fs super-block data in memory */
@@ -1625,6 +1643,9 @@ struct ext4_sb_info {struct mb_cache *s_ea_inode_cache;spinlock_t s_es_lock____cacheline_aligned_in_smp ; +/* Journal triggers for checksum computation */ +struct ext4_journal_trigger s_journal_triggers[EXT4_JOURNAL_TRIGGER_COUNT ]; +/* Ratelimit ext4 messages. */ struct ratelimit_state s_err_ratelimit_state;struct ratelimit_state s_warning_ratelimit_state;
@@ -2920,13 +2941,14 @@ int ext4_get_block(struct inode *inode, sector_t iblock,int ext4_da_get_block_prep (struct inode *inode,sector_t iblock,struct buffer_head *bh,int create);int ext4_walk_page_buffers (handle_t *handle, +struct inode *inode,struct buffer_head *head,unsigned from,unsigned to,int *partial, -int (*fn)(handle_t *handle, +int (*fn)(handle_t *handle,struct inode *inode,struct buffer_head *bh)); -int do_journal_get_write_access (handle_t *handle, +int do_journal_get_write_access (handle_t *handle,struct inode *inode,struct buffer_head *bh);#define FALL_BACK_TO_NONDELALLOC 1#define CONVERT_INLINE_DATA 2
fs/ext4/ext4_jbd2.c +31 -12
68 lines not on this page · raw message
fs/ext4/ext4_jbd2.h +12 -6
@@ -231,26 +231,32 @@ int ext4_expand_extra_isize(struct inode *inode,* Wrapper functions with which ext4 calls into JBD. */ int __ext4_journal_get_write_access (const char *where,unsigned int line, -handle_t *handle,struct buffer_head *bh); +handle_t *handle,struct super_block *sb, +struct buffer_head *bh, +enum ext4_journal_trigger_type trigger_type);int __ext4_forget (const char *where,unsigned int line,handle_t *handle,int is_metadata,struct inode *inode,struct buffer_head *bh,ext4_fsblk_t blocknr);int __ext4_journal_get_create_access (const char *where,unsigned int line, -handle_t *handle,struct buffer_head *bh); +handle_t *handle,struct super_block *sb, +struct buffer_head *bh, +enum ext4_journal_trigger_type trigger_type);int __ext4_handle_dirty_metadata (const char *where,unsigned int line,handle_t *handle,struct inode *inode,struct buffer_head *bh); -#define ext4_journal_get_write_access (handle, bh) \ - __ext4_journal_get_write_access(__func__, __LINE__, (handle), (bh)) +#define ext4_journal_get_write_access (handle, sb, bh, trigger_type) \ + __ext4_journal_get_write_access(__func__, __LINE__, (handle), (sb), \ + (bh), (trigger_type))#define ext4_forget (handle, is_metadata, inode, bh, block_nr) \ __ext4_forget(__func__, __LINE__, (handle), (is_metadata), (inode), \ (bh), (block_nr)) -#define ext4_journal_get_create_access (handle, bh) \ - __ext4_journal_get_create_access(__func__, __LINE__, (handle), (bh)) +#define ext4_journal_get_create_access (handle, sb, bh, trigger_type) \ + __ext4_journal_get_create_access(__func__, __LINE__, (handle), (sb), \ + (bh), (trigger_type))#define ext4_handle_dirty_metadata (handle, inode, bh) \ __ext4_handle_dirty_metadata(__func__, __LINE__, (handle), (inode), \ (bh))
fs/ext4/extents.c +8 -4
@@ -139,7 +139,8 @@ static int ext4_ext_get_access(handle_t *handle, struct inode *inode,if (path->p_bh) {/* path points to block */ BUFFER_TRACE (path->p_bh,"get_write_access" ); -return ext4_journal_get_write_access (handle, path->p_bh); +return ext4_journal_get_write_access (handle, inode->i_sb, + path->p_bh,EXT4_JTR_NONE ); }/* path points to leaf/index in inode body */ /* we use in-core data, no need to protect them */
@@ -1082,7 +1083,8 @@ static int ext4_ext_split(handle_t *handle, struct inode *inode, }lock_buffer (bh); - err =ext4_journal_get_create_access (handle, bh); + err =ext4_journal_get_create_access (handle, inode->i_sb, bh, +EXT4_JTR_NONE );if (err) goto cleanup;
@@ -1160,7 +1162,8 @@ static int ext4_ext_split(handle_t *handle, struct inode *inode, }lock_buffer (bh); - err =ext4_journal_get_create_access (handle, bh); + err =ext4_journal_get_create_access (handle, inode->i_sb, bh, +EXT4_JTR_NONE );if (err) goto cleanup;
@@ -1286,7 +1289,8 @@ static int ext4_ext_grow_indepth(handle_t *handle, struct inode *inode,return -ENOMEM ;lock_buffer (bh); - err =ext4_journal_get_create_access (handle, bh); + err =ext4_journal_get_create_access (handle, inode->i_sb, bh, +EXT4_JTR_NONE );if (err) {unlock_buffer (bh); goto out;
fs/ext4/file.c +2 -1
@@ -822,7 +822,8 @@ static int ext4_sample_last_mounted(struct super_block *sb,if (IS_ERR (handle)) goto out;BUFFER_TRACE (sbi->s_sbh,"get_write_access" ); - err =ext4_journal_get_write_access (handle, sbi->s_sbh); + err =ext4_journal_get_write_access (handle, sb, sbi->s_sbh, +EXT4_JTR_NONE );if (err) goto out_journal;lock_buffer (sbi->s_sbh);
fs/ext4/ialloc.c +12 -7
@@ -300,7 +300,8 @@ void ext4_free_inode(handle_t *handle, struct inode *inode) }BUFFER_TRACE (bitmap_bh,"get_write_access" ); - fatal =ext4_journal_get_write_access (handle, bitmap_bh); + fatal =ext4_journal_get_write_access (handle, sb, bitmap_bh, +EXT4_JTR_NONE );if (fatal) goto error_return;
@@ -308,7 +309,8 @@ void ext4_free_inode(handle_t *handle, struct inode *inode) gdp =ext4_get_group_desc (sb, block_group, &bh2);if (gdp) {BUFFER_TRACE (bh2,"get_write_access" ); - fatal =ext4_journal_get_write_access (handle, bh2); + fatal =ext4_journal_get_write_access (handle, sb, bh2, +EXT4_JTR_NONE ); }ext4_lock_group (sb, block_group); cleared =ext4_test_and_clear_bit (bit, bitmap_bh->b_data);
@@ -1085,7 +1087,8 @@ struct inode *__ext4_new_inode(struct user_namespace *mnt_userns, } }BUFFER_TRACE (inode_bitmap_bh,"get_write_access" ); - err =ext4_journal_get_write_access (handle, inode_bitmap_bh); + err =ext4_journal_get_write_access (handle, sb, inode_bitmap_bh, +EXT4_JTR_NONE );if (err) {ext4_std_error (sb, err); goto out;
@@ -1127,7 +1130,8 @@ struct inode *__ext4_new_inode(struct user_namespace *mnt_userns, }BUFFER_TRACE (group_desc_bh,"get_write_access" ); - err =ext4_journal_get_write_access (handle, group_desc_bh); + err =ext4_journal_get_write_access (handle, sb, group_desc_bh, +EXT4_JTR_NONE );if (err) {ext4_std_error (sb, err); goto out;
@@ -1144,7 +1148,8 @@ struct inode *__ext4_new_inode(struct user_namespace *mnt_userns, goto out; }BUFFER_TRACE (block_bitmap_bh,"get block bitmap access" ); - err =ext4_journal_get_write_access (handle, block_bitmap_bh); + err =ext4_journal_get_write_access (handle, sb, block_bitmap_bh, +EXT4_JTR_NONE );if (err) {brelse (block_bitmap_bh);ext4_std_error (sb, err);
@@ -1583,8 +1588,8 @@ int ext4_init_inode_table(struct super_block *sb, ext4_group_t group, num = sbi->s_itb_per_group - used_blks;BUFFER_TRACE (group_desc_bh,"get_write_access" ); - ret =ext4_journal_get_write_access (handle, - group_desc_bh); + ret =ext4_journal_get_write_access (handle, sb, group_desc_bh, +EXT4_JTR_NONE );if (ret) goto err_out;
fs/ext4/indirect.c +10 -5
@@ -354,7 +354,8 @@ static int ext4_alloc_branch(handle_t *handle, }lock_buffer (bh);BUFFER_TRACE (bh,"call get_create_access" ); - err =ext4_journal_get_create_access (handle, bh); + err =ext4_journal_get_create_access (handle, ar->inode->i_sb, + bh,EXT4_JTR_NONE );if (err) {unlock_buffer (bh); goto failed;
@@ -429,7 +430,8 @@ static int ext4_splice_branch(handle_t *handle,*/ if (where->bh) {BUFFER_TRACE (where->bh,"get_write_access" ); - err =ext4_journal_get_write_access (handle, where->bh); + err =ext4_journal_get_write_access (handle, ar->inode->i_sb, + where->bh,EXT4_JTR_NONE );if (err) goto err_out; }
@@ -728,7 +730,8 @@ static int ext4_ind_truncate_ensure_credits(handle_t *handle,return ret;if (bh) {BUFFER_TRACE (bh,"retaking write access" ); - ret =ext4_journal_get_write_access (handle, bh); + ret =ext4_journal_get_write_access (handle, inode->i_sb, bh, +EXT4_JTR_NONE );if (unlikely (ret))return ret; }
@@ -916,7 +919,8 @@ static void ext4_free_data(handle_t *handle, struct inode *inode,if (this_bh) {/* For indirect block */ BUFFER_TRACE (this_bh,"get_write_access" ); - err =ext4_journal_get_write_access (handle, this_bh); + err =ext4_journal_get_write_access (handle, inode->i_sb, + this_bh,EXT4_JTR_NONE );/* Important: if we can't update the indirect pointers * to the blocks, we can't free them. */ if (err)
@@ -1079,7 +1083,8 @@ static void ext4_free_branches(handle_t *handle, struct inode *inode,*/ BUFFER_TRACE (parent_bh,"get_write_access" );if (!ext4_journal_get_write_access (handle, - parent_bh)){ + inode->i_sb, parent_bh, +EXT4_JTR_NONE )) { *p =0 ;BUFFER_TRACE (parent_bh,"call ext4_handle_dirty_metadata" );
fs/ext4/inline.c +17 -9
@@ -264,7 +264,8 @@ static int ext4_create_inline_data(handle_t *handle,return error;BUFFER_TRACE (is.iloc.bh,"get_write_access" ); - error =ext4_journal_get_write_access (handle, is.iloc.bh); + error =ext4_journal_get_write_access (handle, inode->i_sb, is.iloc.bh, +EXT4_JTR_NONE );if (error) goto out;
@@ -350,7 +351,8 @@ static int ext4_update_inline_data(handle_t *handle, struct inode *inode, goto out;BUFFER_TRACE (is.iloc.bh,"get_write_access" ); - error =ext4_journal_get_write_access (handle, is.iloc.bh); + error =ext4_journal_get_write_access (handle, inode->i_sb, is.iloc.bh, +EXT4_JTR_NONE );if (error) goto out;
@@ -427,7 +429,8 @@ static int ext4_destroy_inline_data_nolock(handle_t *handle, goto out;BUFFER_TRACE (is.iloc.bh,"get_write_access" ); - error =ext4_journal_get_write_access (handle, is.iloc.bh); + error =ext4_journal_get_write_access (handle, inode->i_sb, is.iloc.bh, +EXT4_JTR_NONE );if (error) goto out;
@@ -593,7 +596,7 @@ static int ext4_convert_inline_data_to_extent(struct address_space *mapping, ret =__block_write_begin (page, from, to, ext4_get_block);if (!ret &&ext4_should_journal_data (inode)) { - ret =ext4_walk_page_buffers (handle,page_buffers (page), + ret =ext4_walk_page_buffers (handle, inode,page_buffers (page), from, to,NULL , do_journal_get_write_access); }
@@ -682,7 +685,8 @@ int ext4_try_to_write_inline_data(struct address_space *mapping, goto convert; } - ret =ext4_journal_get_write_access (handle, iloc.bh); + ret =ext4_journal_get_write_access (handle, inode->i_sb, iloc.bh, +EXT4_JTR_NONE );if (ret) goto out;
@@ -923,7 +927,8 @@ int ext4_da_write_inline_data_begin(struct address_space *mapping,if (ret <0 ) goto out_release_page; } - ret =ext4_journal_get_write_access (handle, iloc.bh); + ret =ext4_journal_get_write_access (handle, inode->i_sb, iloc.bh, +EXT4_JTR_NONE );if (ret) goto out_release_page;
@@ -1028,7 +1033,8 @@ static int ext4_add_dirent_to_inline(handle_t *handle,return err;BUFFER_TRACE (iloc->bh,"get_write_access" ); - err =ext4_journal_get_write_access (handle, iloc->bh); + err =ext4_journal_get_write_access (handle, dir->i_sb, iloc->bh, +EXT4_JTR_NONE );if (err)return err;ext4_insert_dentry (dir, inode, de, inline_size, fname);
@@ -1223,7 +1229,8 @@ static int ext4_convert_inline_data_nolock(handle_t *handle, }lock_buffer (data_bh); - error =ext4_journal_get_create_access (handle, data_bh); + error =ext4_journal_get_create_access (handle, inode->i_sb, data_bh, +EXT4_JTR_NONE );if (error) {unlock_buffer (data_bh); error = -EIO ;
@@ -1707,7 +1714,8 @@ int ext4_delete_inline_entry(handle_t *handle, }BUFFER_TRACE (bh,"get_write_access" ); - err =ext4_journal_get_write_access (handle, bh); + err =ext4_journal_get_write_access (handle, dir->i_sb, bh, +EXT4_JTR_NONE );if (err) goto out;
fs/ext4/inode.c +49 -35
242 lines not on this page · raw message
fs/ext4/ioctl.c +3 -1
@@ -1154,7 +1154,9 @@ static long __ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) err =PTR_ERR (handle); goto pwsalt_err_exit; } - err =ext4_journal_get_write_access (handle, sbi->s_sbh); + err =ext4_journal_get_write_access (handle, sb, + sbi->s_sbh, +EXT4_JTR_NONE );if (err) goto pwsalt_err_journal;lock_buffer (sbi->s_sbh);
fs/ext4/mballoc.c +9 -6
@@ -3726,7 +3726,8 @@ ext4_mb_mark_diskspace_used(struct ext4_allocation_context *ac, }BUFFER_TRACE (bitmap_bh,"getting write access" ); - err =ext4_journal_get_write_access (handle, bitmap_bh); + err =ext4_journal_get_write_access (handle, sb, bitmap_bh, +EXT4_JTR_NONE );if (err) goto out_err;
@@ -3739,7 +3740,7 @@ ext4_mb_mark_diskspace_used(struct ext4_allocation_context *ac, ext4_free_group_clusters(sb, gdp));BUFFER_TRACE (gdp_bh,"get_write_access" ); - err =ext4_journal_get_write_access (handle, gdp_bh); + err =ext4_journal_get_write_access (handle, sb, gdp_bh,EXT4_JTR_NONE );if (err) goto out_err;
@@ -5916,7 +5917,8 @@ void ext4_free_blocks(handle_t *handle, struct inode *inode, }BUFFER_TRACE (bitmap_bh,"getting write access" ); - err =ext4_journal_get_write_access (handle, bitmap_bh); + err =ext4_journal_get_write_access (handle, sb, bitmap_bh, +EXT4_JTR_NONE );if (err) goto error_return;
@@ -5926,7 +5928,7 @@ void ext4_free_blocks(handle_t *handle, struct inode *inode,* using it */ BUFFER_TRACE (gd_bh,"get_write_access" ); - err =ext4_journal_get_write_access (handle, gd_bh); + err =ext4_journal_get_write_access (handle, sb, gd_bh,EXT4_JTR_NONE );if (err) goto error_return;#ifdef AGGRESSIVE_CHECK
@@ -6107,7 +6109,8 @@ int ext4_group_add_blocks(handle_t *handle, struct super_block *sb, }BUFFER_TRACE (bitmap_bh,"getting write access" ); - err =ext4_journal_get_write_access (handle, bitmap_bh); + err =ext4_journal_get_write_access (handle, sb, bitmap_bh, +EXT4_JTR_NONE );if (err) goto error_return;
@@ -6117,7 +6120,7 @@ int ext4_group_add_blocks(handle_t *handle, struct super_block *sb,* using it */ BUFFER_TRACE (gd_bh,"get_write_access" ); - err =ext4_journal_get_write_access (handle, gd_bh); + err =ext4_journal_get_write_access (handle, sb, gd_bh,EXT4_JTR_NONE );if (err) goto error_return;
fs/ext4/namei.c +26 -14
@@ -70,7 +70,8 @@ static struct buffer_head *ext4_append(handle_t *handle, inode->i_size += inode->i_sb->s_blocksize;EXT4_I (inode)->i_disksize = inode->i_size;BUFFER_TRACE (bh,"get_write_access" ); - err =ext4_journal_get_write_access (handle, bh); + err =ext4_journal_get_write_access (handle, inode->i_sb, bh, +EXT4_JTR_NONE );if (err) {brelse (bh);ext4_std_error (inode->i_sb, err);
@@ -1927,12 +1928,14 @@ static struct ext4_dir_entry_2 *do_split(handle_t *handle, struct inode *dir, }BUFFER_TRACE (*bh,"get_write_access" ); - err =ext4_journal_get_write_access (handle, *bh); + err =ext4_journal_get_write_access (handle, dir->i_sb, *bh, +EXT4_JTR_NONE );if (err) goto journal_error;BUFFER_TRACE (frame->bh,"get_write_access" ); - err =ext4_journal_get_write_access (handle, frame->bh); + err =ext4_journal_get_write_access (handle, dir->i_sb, frame->bh, +EXT4_JTR_NONE );if (err) goto journal_error;
@@ -2109,7 +2112,8 @@ static int add_dirent_to_buf(handle_t *handle, struct ext4_filename *fname,return err; }BUFFER_TRACE (bh,"get_write_access" ); - err =ext4_journal_get_write_access (handle, bh); + err =ext4_journal_get_write_access (handle, dir->i_sb, bh, +EXT4_JTR_NONE );if (err) {ext4_std_error (dir->i_sb, err);return err;
@@ -2167,7 +2171,8 @@ static int make_indexed_dir(handle_t *handle, struct ext4_filename *fname, blocksize = dir->i_sb->s_blocksize;dxtrace (printk (KERN_DEBUG "Creating index: inode %lu\n" , dir->i_ino));BUFFER_TRACE (bh,"get_write_access" ); - retval =ext4_journal_get_write_access (handle, bh); + retval =ext4_journal_get_write_access (handle, dir->i_sb, bh, +EXT4_JTR_NONE );if (retval) {ext4_std_error (dir->i_sb, retval);brelse (bh);
@@ -2419,7 +2424,7 @@ static int ext4_dx_add_entry(handle_t *handle, struct ext4_filename *fname, }BUFFER_TRACE (bh,"get_write_access" ); - err =ext4_journal_get_write_access (handle, bh); + err =ext4_journal_get_write_access (handle, sb, bh,EXT4_JTR_NONE );if (err) goto journal_error;
@@ -2476,7 +2481,8 @@ static int ext4_dx_add_entry(handle_t *handle, struct ext4_filename *fname, node2->fake.rec_len =ext4_rec_len_to_disk (sb->s_blocksize, sb->s_blocksize);BUFFER_TRACE (frame->bh,"get_write_access" ); - err =ext4_journal_get_write_access (handle, frame->bh); + err =ext4_journal_get_write_access (handle, sb, frame->bh, +EXT4_JTR_NONE );if (err) goto journal_error;if (!add_level) {
@@ -2486,8 +2492,9 @@ static int ext4_dx_add_entry(handle_t *handle, struct ext4_filename *fname, icount1, icount2));BUFFER_TRACE (frame->bh,"get_write_access" );/* index root */ - err =ext4_journal_get_write_access (handle, - (frame -1 )->bh); + err =ext4_journal_get_write_access (handle, sb, + (frame -1 )->bh, +EXT4_JTR_NONE );if (err) goto journal_error;
@@ -2636,7 +2643,8 @@ static int ext4_delete_entry(handle_t *handle, csum_size =sizeof (struct ext4_dir_entry_tail );BUFFER_TRACE (bh,"get_write_access" ); - err =ext4_journal_get_write_access (handle, bh); + err =ext4_journal_get_write_access (handle, dir->i_sb, bh, +EXT4_JTR_NONE );if (unlikely (err)) goto out;
@@ -3088,7 +3096,8 @@ int ext4_orphan_add(handle_t *handle, struct inode *inode) S_ISLNK(inode->i_mode)) || inode->i_nlink == 0);BUFFER_TRACE (sbi->s_sbh,"get_write_access" ); - err =ext4_journal_get_write_access (handle, sbi->s_sbh); + err =ext4_journal_get_write_access (handle, sb, sbi->s_sbh, +EXT4_JTR_NONE );if (err) goto out;
@@ -3186,7 +3195,8 @@ int ext4_orphan_del(handle_t *handle, struct inode *inode)if (prev == &sbi->s_orphan) {jbd_debug (4 ,"superblock will point to %u\n" , ino_next);BUFFER_TRACE (sbi->s_sbh,"get_write_access" ); - err =ext4_journal_get_write_access (handle, sbi->s_sbh); + err =ext4_journal_get_write_access (handle, inode->i_sb, + sbi->s_sbh,EXT4_JTR_NONE );if (err) {mutex_unlock (&sbi->s_orphan_lock); goto out_brelse;
@@ -3675,7 +3685,8 @@ static int ext4_rename_dir_prepare(handle_t *handle, struct ext4_renament *ent)if (le32_to_cpu (ent->parent_de->inode) != ent->dir->i_ino)return -EFSCORRUPTED ;BUFFER_TRACE (ent->dir_bh,"get_write_access" ); -return ext4_journal_get_write_access (handle, ent->dir_bh); +return ext4_journal_get_write_access (handle, ent->dir->i_sb, + ent->dir_bh,EXT4_JTR_NONE ); } static int ext4_rename_dir_finish(handle_t *handle, struct ext4_renament *ent,
@@ -3710,7 +3721,8 @@ static int ext4_setent(handle_t *handle, struct ext4_renament *ent,int retval, retval2;BUFFER_TRACE (ent->bh,"get write access" ); - retval =ext4_journal_get_write_access (handle, ent->bh); + retval =ext4_journal_get_write_access (handle, ent->dir->i_sb, ent->bh, +EXT4_JTR_NONE );if (retval)return retval; ent->de->inode =cpu_to_le32 (ino);
fs/ext4/resize.c +23 -15
@@ -409,7 +409,8 @@ static struct buffer_head *bclean(handle_t *handle, struct super_block *sb,if (unlikely (!bh))return ERR_PTR (-ENOMEM );BUFFER_TRACE (bh,"get_write_access" ); -if ((err =ext4_journal_get_write_access (handle, bh))) { + err =ext4_journal_get_write_access (handle, sb, bh,EXT4_JTR_NONE ); +if (err) {brelse (bh); bh =ERR_PTR (err); }else {
@@ -474,7 +475,8 @@ static int set_flexbg_block_bitmap(struct super_block *sb, handle_t *handle,return -ENOMEM ;BUFFER_TRACE (bh,"get_write_access" ); - err =ext4_journal_get_write_access (handle, bh); + err =ext4_journal_get_write_access (handle, sb, bh, +EXT4_JTR_NONE );if (err) {brelse (bh);return err;
@@ -569,7 +571,8 @@ static int setup_new_flex_group_blocks(struct super_block *sb, }BUFFER_TRACE (gdb,"get_write_access" ); - err =ext4_journal_get_write_access (handle, gdb); + err =ext4_journal_get_write_access (handle, sb, gdb, +EXT4_JTR_NONE );if (err) {brelse (gdb); goto out;
@@ -837,17 +840,18 @@ static int add_new_gdb(handle_t *handle, struct inode *inode, }BUFFER_TRACE (EXT4_SB (sb)->s_sbh,"get_write_access" ); - err =ext4_journal_get_write_access (handle,EXT4_SB (sb)->s_sbh); + err =ext4_journal_get_write_access (handle, sb,EXT4_SB (sb)->s_sbh, +EXT4_JTR_NONE );if (unlikely (err)) goto errout;BUFFER_TRACE (gdb_bh,"get_write_access" ); - err =ext4_journal_get_write_access (handle, gdb_bh); + err =ext4_journal_get_write_access (handle, sb, gdb_bh,EXT4_JTR_NONE );if (unlikely (err)) goto errout;BUFFER_TRACE (dind,"get_write_access" ); - err =ext4_journal_get_write_access (handle, dind); + err =ext4_journal_get_write_access (handle, sb, dind,EXT4_JTR_NONE );if (unlikely (err)) {ext4_std_error (sb, err); goto errout;
@@ -956,7 +960,7 @@ static int add_new_gdb_meta_bg(struct super_block *sb, n_group_desc[gdb_num] = gdb_bh;BUFFER_TRACE (gdb_bh,"get_write_access" ); - err =ext4_journal_get_write_access (handle, gdb_bh); + err =ext4_journal_get_write_access (handle, sb, gdb_bh,EXT4_JTR_NONE );if (err) {kvfree (n_group_desc);brelse (gdb_bh);
@@ -1042,7 +1046,8 @@ static int reserve_backup_gdb(handle_t *handle, struct inode *inode,for (i =0 ; i < reserved_gdb; i++) {BUFFER_TRACE (primary[i],"get_write_access" ); -if ((err =ext4_journal_get_write_access (handle, primary[i]))) +if ((err =ext4_journal_get_write_access (handle, sb, primary[i], +EXT4_JTR_NONE ))) goto exit_bh; }
@@ -1149,10 +1154,9 @@ static void update_backups(struct super_block *sb, sector_t blk_off, char *data, backup_block, backup_block - ext4_group_first_block_no(sb, group));BUFFER_TRACE (bh,"get_write_access" ); -if ((err =ext4_journal_get_write_access (handle, bh))) { -brelse (bh); +if ((err =ext4_journal_get_write_access (handle, sb, bh, +EXT4_JTR_NONE )))break ; - }lock_buffer (bh);memcpy (bh->b_data, data, size);if (rest)
@@ -1232,7 +1236,8 @@ static int ext4_add_new_descs(handle_t *handle, struct super_block *sb, gdb_bh =sbi_array_rcu_deref (sbi, s_group_desc, gdb_num);BUFFER_TRACE (gdb_bh,"get_write_access" ); - err =ext4_journal_get_write_access (handle, gdb_bh); + err =ext4_journal_get_write_access (handle, sb, gdb_bh, +EXT4_JTR_NONE );if (!err && reserved_gdb &&ext4_bg_num_gdb (sb, group)) err =reserve_backup_gdb (handle, resize_inode, group);
@@ -1509,7 +1514,8 @@ static int ext4_flex_group_add(struct super_block *sb, }BUFFER_TRACE (sbi->s_sbh,"get_write_access" ); - err =ext4_journal_get_write_access (handle, sbi->s_sbh); + err =ext4_journal_get_write_access (handle, sb, sbi->s_sbh, +EXT4_JTR_NONE );if (err) goto exit_journal;
@@ -1722,7 +1728,8 @@ static int ext4_group_extend_no_check(struct super_block *sb, }BUFFER_TRACE (EXT4_SB (sb)->s_sbh,"get_write_access" ); - err =ext4_journal_get_write_access (handle,EXT4_SB (sb)->s_sbh); + err =ext4_journal_get_write_access (handle, sb,EXT4_SB (sb)->s_sbh, +EXT4_JTR_NONE );if (err) {ext4_warning (sb,"error %d on journal write access" , err); goto errout;
@@ -1884,7 +1891,8 @@ static int ext4_convert_meta_bg(struct super_block *sb, struct inode *inode)return PTR_ERR (handle);BUFFER_TRACE (sbi->s_sbh,"get_write_access" ); - err =ext4_journal_get_write_access (handle, sbi->s_sbh); + err =ext4_journal_get_write_access (handle, sb, sbi->s_sbh, +EXT4_JTR_NONE );if (err) goto errout;
fs/ext4/super.c +15 -1
@@ -4014,6 +4014,20 @@ static const char *ext4_quota_mode(struct super_block *sb)#endif } +static void ext4_setup_csum_trigger (struct super_block *sb, +enum ext4_journal_trigger_type type, +void (*trigger)( +struct jbd2_buffer_trigger_type *type, +struct buffer_head *bh, +void *mapped_data, +size_t size)) +{ +struct ext4_sb_info *sbi =EXT4_SB (sb); + + sbi->s_journal_triggers[type].sb = sb; + sbi->s_journal_triggers[type].tr_triggers.t_frozen = trigger; +} +static int ext4_fill_super (struct super_block *sb,void *data,int silent) {struct dax_device *dax_dev =fs_dax_get_by_bdev (sb->s_bdev);
@@ -6609,7 +6623,7 @@ static ssize_t ext4_quota_write(struct super_block *sb, int type,if (!bh) goto out;BUFFER_TRACE (bh,"get write access" ); - err =ext4_journal_get_write_access (handle, bh); + err =ext4_journal_get_write_access (handle, sb, bh,EXT4_JTR_NONE );if (err) {brelse (bh);return err;
fs/ext4/xattr.c +17 -9
@@ -791,7 +791,8 @@ static void ext4_xattr_update_super_block(handle_t *handle,return ;BUFFER_TRACE (EXT4_SB (sb)->s_sbh,"get_write_access" ); -if (ext4_journal_get_write_access (handle,EXT4_SB (sb)->s_sbh) ==0 ) { +if (ext4_journal_get_write_access (handle, sb,EXT4_SB (sb)->s_sbh, +EXT4_JTR_NONE ) ==0 ) {lock_buffer (EXT4_SB (sb)->s_sbh);ext4_set_feature_xattr (sb);ext4_superblock_csum_set (sb);
@@ -1169,7 +1170,8 @@ ext4_xattr_inode_dec_ref_all(handle_t *handle, struct inode *parent,continue ; }if (err >0 ) { - err =ext4_journal_get_write_access (handle, bh); + err =ext4_journal_get_write_access (handle, + parent->i_sb, bh,EXT4_JTR_NONE );if (err) { ext4_warning_inode(ea_inode, "Re-get write access err=%d",
@@ -1230,7 +1232,8 @@ ext4_xattr_release_block(handle_t *handle, struct inode *inode,int error =0 ;BUFFER_TRACE (bh,"get_write_access" ); - error =ext4_journal_get_write_access (handle, bh); + error =ext4_journal_get_write_access (handle, inode->i_sb, bh, +EXT4_JTR_NONE );if (error) goto out;
@@ -1371,7 +1374,8 @@ static int ext4_xattr_inode_write(handle_t *handle, struct inode *ea_inode, "ext4_getblk() return bh = NULL");return -EFSCORRUPTED ; } - ret =ext4_journal_get_write_access (handle, bh); + ret =ext4_journal_get_write_access (handle, ea_inode->i_sb, bh, +EXT4_JTR_NONE );if (ret) goto out;
@@ -1855,7 +1859,8 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode,if (s->base) {BUFFER_TRACE (bs->bh,"get_write_access" ); - error =ext4_journal_get_write_access (handle, bs->bh); + error =ext4_journal_get_write_access (handle, sb, bs->bh, +EXT4_JTR_NONE );if (error) goto cleanup;lock_buffer (bs->bh);
@@ -1987,8 +1992,9 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode,if (error) goto cleanup;BUFFER_TRACE (new_bh,"get_write_access" ); - error =ext4_journal_get_write_access (handle, - new_bh); + error =ext4_journal_get_write_access ( + handle, sb, new_bh, +EXT4_JTR_NONE );if (error) goto cleanup_dquot;lock_buffer (new_bh);
@@ -2092,7 +2098,8 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode, }lock_buffer (new_bh); - error =ext4_journal_get_create_access (handle, new_bh); + error =ext4_journal_get_create_access (handle, sb, + new_bh,EXT4_JTR_NONE );if (error) {unlock_buffer (new_bh); error = -EIO ;
@@ -2848,7 +2855,8 @@ int ext4_xattr_delete_inode(handle_t *handle, struct inode *inode, goto cleanup; } - error =ext4_journal_get_write_access (handle, iloc.bh); + error =ext4_journal_get_write_access (handle, inode->i_sb, + iloc.bh,EXT4_JTR_NONE );if (error) {EXT4_ERROR_INODE (inode,"write access (error %d)" , error);
fs/jbd2/transaction.c +1 -1
@@ -1404,7 +1404,7 @@ void jbd2_journal_set_triggers(struct buffer_head *bh, {struct journal_head *jh =jbd2_journal_grab_journal_head (bh); -if (WARN_ON (!jh)) +if (WARN_ON_ONCE (!jh))return ; jh->b_triggers = type;jbd2_journal_put_journal_head (jh);
Signature · 1 line
2.26.2
Jan Kara
reviewerPATCH v5 2/5 ext4: Move orphan inode handling into a separate file
In reply to Jan KaraMove functions for handling orphan inodes into a new file fs/ext4/orphan.c to have them in one place and somewhat reduce size of other files. No code changes.
--- fs/ext4/Makefile | 2 +- fs/ext4/ext4.h | 11 +- fs/ext4/namei.c | 182 ------------------------ fs/ext4/orphan.c | 363 +++++++++++++++++++++++++++++++++++++++++++++++ fs/ext4/super.c | 180 +---------------------- 5 files changed, 375 insertions(+), 363 deletions(-) create mode 100644 fs/ext4/orphan.c
fs/ext4/Makefile +1 -1
@@ -10,7 +10,7 @@ ext4-y := balloc.o bitmap.o block_validity.o dir.o ext4_jbd2.o extents.o \ indirect.o inline.o inode.o ioctl.o mballoc.o migrate.o \ mmp.o move_extent.o namei.o page-io.o readpage.o resize.o \ super.o symlink.o sysfs.o xattr.o xattr_hurd.o xattr_trusted.o \ - xattr_user.o fast_commit.o + xattr_user.o fast_commit.o orphan.o ext4-$(CONFIG_EXT4_FS_POSIX_ACL) += acl.o ext4-$(CONFIG_EXT4_FS_SECURITY) += xattr_security.o
fs/ext4/ext4.h +9 -2
@@ -2168,6 +2168,8 @@ static inline bool ext4_has_incompat_features(struct super_block *sb)return (EXT4_SB (sb)->s_es->s_feature_incompat !=0 ); } +extern int ext4_feature_set_ok (struct super_block *sb,int readonly); +/* * Superblock flags */
@@ -3028,8 +3030,6 @@ extern int ext4_init_new_dir(handle_t *handle, struct inode *dir, struct inode *inode);extern int ext4_dirblock_csum_verify (struct inode *inode,struct buffer_head *bh); -extern int ext4_orphan_add (handle_t *,struct inode *); -extern int ext4_orphan_del (handle_t *,struct inode *);extern int ext4_htree_fill_tree (struct file *dir_file,__u32 start_hash,__u32 start_minor_hash,__u32 *next_hash); extern int ext4_search_dir(struct buffer_head *bh,
@@ -3498,6 +3498,7 @@ static inline bool ext4_is_quota_journalled(struct super_block *sb)return (ext4_has_feature_quota (sb) || sbi->s_qf_names[USRQUOTA] || sbi->s_qf_names[GRPQUOTA]); } +int ext4_enable_quotas (struct super_block *sb);#endif /*
@@ -3759,6 +3760,12 @@ extern void ext4_stop_mmpd(struct ext4_sb_info *sbi);/* verity.c */ extern const struct fsverity_operations ext4_verityops; +/* orphan.c */ +extern int ext4_orphan_add (handle_t *,struct inode *); +extern int ext4_orphan_del (handle_t *,struct inode *); +extern void ext4_orphan_cleanup (struct super_block *sb, +struct ext4_super_block *es); +/* * Add new method to test whether block and inode bitmaps are properly * initialized. With uninit_bg reading the block from disk is not enough
fs/ext4/namei.c +0 -182
188 lines not on this page · raw message
fs/ext4/orphan.c new file +363 -0
363 lines not on this page · raw message
fs/ext4/super.c +2 -178
222 lines not on this page · raw message
Signature · 1 line
2.26.2
Jan Kara
reviewerPATCH v5 3/5 ext4: Speedup ext4 orphan inode handling
In reply to Jan KaraExt4 orphan inode handling is a bottleneck for workloads which heavily truncate / unlink small files since it contends on the global s_orphan_mutex lock (and generally it's difficult to improve scalability of the ondisk linked list of orphaned inodes).
This patch implements new way of handling orphan inodes. Instead of linking orphaned inode into a linked list, we store it's inode number in a new special file which we call "orphan file". Only if there's no more space in the orphan file (too many inodes are currently orphaned) we fall back to using old style linked list. Currently we protect operations in the orphan file with a spinlock for simplicity but even in this setting we can substantially reduce the length of the critical section and thus speedup some workloads. In the next patch we improve this by making orphan handling lockless.
Note that the change is backwards compatible when the filesystem is clean - the existence of the orphan file is a compat feature, we set another ro-compat feature indicating orphan file needs scanning for orphaned inodes when mounting filesystem read-write. This ro-compat feature gets cleared on unmount / remount read-only.
Some performance data from 80 CPU Xeon Server with 512 GB of RAM, filesystem located on SSD, average of 5 runs:
stress-orphan (microbenchmark truncating files byte-by-byte from N processes in parallel)
Threads Time Time
Vanilla Patched
1 1.057200 0.945600 2 1.680400 1.331800 4 2.547000 1.995000 8 7.049400 6.424200 16 14.827800 14.937600 32 40.948200 33.038200 64 87.787400 60.823600
128 206.504000 122.941400
So we can see significant wins all over the board.
--- fs/ext4/ext4.h | 69 +++++++++- fs/ext4/orphan.c | 340 +++++++++++++++++++++++++++++++++++++++++------ fs/ext4/super.c | 34 ++++- 3 files changed, 392 insertions(+), 51 deletions(-)
fs/ext4/ext4.h +62 -7
@@ -1034,7 +1034,14 @@ struct ext4_inode_info {*/ struct rw_semaphore xattr_sem; -struct list_head i_orphan;/* unlinked but open inodes */ +/* +* Inodes with EXT4_STATE_ORPHAN_FILE use i_orphan_idx. Otherwise +* i_orphan is used. +*/ +union { +struct list_head i_orphan;/* unlinked but open inodes */ +unsigned int i_orphan_idx;/* Index in orphan file */ + };/* Fast commit related info */
@@ -1428,7 +1435,8 @@ struct ext4_super_block {__u8 s_last_error_errcode;__le16 s_encoding;/* Filename charset encoding */ __le16 s_encoding_flags;/* Filename charset encoding flags */ -__le32 s_reserved[95 ];/* Padding to the end of the block */ +__le32 s_orphan_file_inum;/* Inode for tracking orphan inodes */ +__le32 s_reserved[94 ];/* Padding to the end of the block */ __le32 s_checksum;/* crc32c(superblock) */ };
@@ -1449,6 +1457,7 @@ struct ext4_super_block {/* Types of ext4 journal triggers */ enum ext4_journal_trigger_type { +EXT4_JTR_ORPHAN_FILE ,EXT4_JTR_NONE /* This must be the last entry for indexing to work! */ };
@@ -1465,6 +1474,36 @@ static inline struct ext4_journal_trigger *EXT4_TRIGGER( return container_of(trigger, struct ext4_journal_trigger, tr_triggers); } +#define EXT4_ORPHAN_BLOCK_MAGIC 0x0b10ca04 + +/* Structure at the tail of orphan block */ +struct ext4_orphan_block_tail { +__le32 ob_magic; +__le32 ob_checksum; +}; + +static inline int ext4_inodes_per_orphan_block (struct super_block *sb) +{ +return (sb->s_blocksize -sizeof (struct ext4_orphan_block_tail )) / +sizeof (u32); +} + +struct ext4_orphan_block { +int ob_free_entries;/* Number of free orphan entries in block */ +struct buffer_head *ob_bh;/* Buffer for orphan block */ +}; + +/* +* Info about orphan file. +*/ +struct ext4_orphan_info { +spinlock_t of_lock; +int of_blocks;/* Number of orphan blocks in a file */ +__u32 of_csum_seed;/* Checksum seed for orphan file */ +struct ext4_orphan_block *of_binfo;/* Array with info about orphan +* file blocks */ +}; +/* * fourth extended-fs super-block data in memory */
@@ -1519,9 +1558,11 @@ struct ext4_sb_info {/* Journaling */ struct journal_s *s_journal; -struct list_head s_orphan; -struct mutex s_orphan_lock;unsigned long s_ext4_flags;/* Ext4 superblock flags */ +struct mutex s_orphan_lock;/* Protects on disk list changes */ +struct list_head s_orphan;/* List of orphaned inodes in on disk +list */ +struct ext4_orphan_info s_orphan_info;unsigned long s_commit_interval;u32 s_max_batch_time;u32 s_min_batch_time;
@@ -1856,6 +1897,7 @@ enum {EXT4_STATE_LUSTRE_EA_INODE ,/* Lustre-style ea_inode */ EXT4_STATE_VERITY_IN_PROGRESS ,/* building fs-verity Merkle tree */ EXT4_STATE_FC_COMMITTING ,/* Fast commit ongoing */ + EXT4_STATE_ORPHAN_FILE, /* Inode orphaned in orphan file */ };#define EXT4_INODE_BIT_FNS (name, field, offset) \
@@ -1957,6 +1999,7 @@ static inline bool ext4_verity_in_progress(struct inode *inode)*/ #define EXT4_FEATURE_COMPAT_FAST_COMMIT 0x0400#define EXT4_FEATURE_COMPAT_STABLE_INODES 0x0800 +#define EXT4_FEATURE_COMPAT_ORPHAN_FILE 0x1000/* Orphan file exists */ #define EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001#define EXT4_FEATURE_RO_COMPAT_LARGE_FILE 0x0002
@@ -1977,6 +2020,8 @@ static inline bool ext4_verity_in_progress(struct inode *inode)#define EXT4_FEATURE_RO_COMPAT_READONLY 0x1000#define EXT4_FEATURE_RO_COMPAT_PROJECT 0x2000#define EXT4_FEATURE_RO_COMPAT_VERITY 0x8000 +#define EXT4_FEATURE_RO_COMPAT_ORPHAN_PRESENT 0x10000/* Orphan file may be +non-empty */ #define EXT4_FEATURE_INCOMPAT_COMPRESSION 0x0001#define EXT4_FEATURE_INCOMPAT_FILETYPE 0x0002
@@ -2060,6 +2105,7 @@ EXT4_FEATURE_COMPAT_FUNCS(dir_index, DIR_INDEX) EXT4_FEATURE_COMPAT_FUNCS(sparse_super2, SPARSE_SUPER2) EXT4_FEATURE_COMPAT_FUNCS(fast_commit, FAST_COMMIT) EXT4_FEATURE_COMPAT_FUNCS(stable_inodes, STABLE_INODES) +EXT4_FEATURE_COMPAT_FUNCS(orphan_file, ORPHAN_FILE) EXT4_FEATURE_RO_COMPAT_FUNCS(sparse_super, SPARSE_SUPER)EXT4_FEATURE_RO_COMPAT_FUNCS (large_file,LARGE_FILE )
@@ -2074,6 +2120,7 @@ EXT4_FEATURE_RO_COMPAT_FUNCS(metadata_csum, METADATA_CSUM) EXT4_FEATURE_RO_COMPAT_FUNCS(readonly, READONLY) EXT4_FEATURE_RO_COMPAT_FUNCS(project, PROJECT) EXT4_FEATURE_RO_COMPAT_FUNCS(verity, VERITY) +EXT4_FEATURE_RO_COMPAT_FUNCS(orphan_present, ORPHAN_PRESENT) EXT4_FEATURE_INCOMPAT_FUNCS(compression, COMPRESSION)EXT4_FEATURE_INCOMPAT_FUNCS (filetype,FILETYPE )
@@ -2107,7 +2154,8 @@ EXT4_FEATURE_INCOMPAT_FUNCS(casefold, CASEFOLD) EXT4_FEATURE_RO_COMPAT_LARGE_FILE| \ EXT4_FEATURE_RO_COMPAT_BTREE_DIR) -#define EXT4_FEATURE_COMPAT_SUPP EXT4_FEATURE_COMPAT_EXT_ATTR +#define EXT4_FEATURE_COMPAT_SUPP (EXT4_FEATURE_COMPAT_EXT_ATTR| \ + EXT4_FEATURE_COMPAT_ORPHAN_FILE)#define EXT4_FEATURE_INCOMPAT_SUPP (EXT4_FEATURE_INCOMPAT_FILETYPE| \ EXT4_FEATURE_INCOMPAT_RECOVER| \ EXT4_FEATURE_INCOMPAT_META_BG| \
@@ -2132,7 +2180,8 @@ EXT4_FEATURE_INCOMPAT_FUNCS(casefold, CASEFOLD) EXT4_FEATURE_RO_COMPAT_METADATA_CSUM|\ EXT4_FEATURE_RO_COMPAT_QUOTA |\ EXT4_FEATURE_RO_COMPAT_PROJECT |\ - EXT4_FEATURE_RO_COMPAT_VERITY) + EXT4_FEATURE_RO_COMPAT_VERITY |\ + EXT4_FEATURE_RO_COMPAT_ORPHAN_PRESENT)#define EXTN_FEATURE_FUNCS (ver) \ static inline bool ext4_has_unknown_ext##ver##_compat_features(struct super_block *sb) \
@@ -2182,7 +2231,6 @@ static inline int ext4_forced_shutdown(struct ext4_sb_info *sbi)return test_bit (EXT4_FLAGS_SHUTDOWN , &sbi->s_ext4_flags); } -/* * Default values for user and/or group using reserved blocks */
@@ -3765,6 +3813,13 @@ extern int ext4_orphan_add(handle_t *, struct inode *);extern int ext4_orphan_del (handle_t *,struct inode *);extern void ext4_orphan_cleanup (struct super_block *sb,struct ext4_super_block *es); +extern void ext4_release_orphan_info (struct super_block *sb); +extern int ext4_init_orphan_info (struct super_block *sb); +extern int ext4_orphan_file_empty (struct super_block *sb); +extern void ext4_orphan_file_block_trigger ( +struct jbd2_buffer_trigger_type *triggers, +struct buffer_head *bh, +void *data,size_t size);/* * Add new method to test whether block and inode bitmaps are properly
fs/ext4/orphan.c +301 -39
402 lines not on this page · raw message
fs/ext4/super.c +29 -5
@@ -1174,6 +1174,7 @@ static void ext4_put_super(struct super_block *sb)flush_work (&sbi->s_error_work);destroy_workqueue (sbi->rsv_conversion_wq); +ext4_release_orphan_info (sb);/* * Unregister sysfs before destroying jbd2 journal.
@@ -1199,6 +1200,7 @@ static void ext4_put_super(struct super_block *sb)if (!sb_rdonly (sb) && !aborted) {ext4_clear_feature_journal_needs_recovery (sb); +ext4_clear_feature_orphan_present (sb); es->s_state =cpu_to_le16 (sbi->s_mount_state); }if (!sb_rdonly (sb))
@@ -2684,8 +2686,11 @@ static int ext4_setup_super(struct super_block *sb, struct ext4_super_block *es, es->s_max_mnt_count =cpu_to_le16 (EXT4_DFL_MAX_MNT_COUNT );le16_add_cpu (&es->s_mnt_count,1 );ext4_update_tstamp (es, s_mtime); -if (sbi->s_journal) +if (sbi->s_journal) {ext4_set_feature_journal_needs_recovery (sb); +if (ext4_has_feature_orphan_file (sb)) +ext4_set_feature_orphan_present (sb); + } err =ext4_commit_super (sb); done:
@@ -3960,6 +3965,8 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) silent =1 ; goto cantfind_ext4; } +ext4_setup_csum_trigger (sb,EXT4_JTR_ORPHAN_FILE , + ext4_orphan_file_block_trigger);/* Load the checksum driver */ sbi->s_chksum_driver =crypto_alloc_shash ("crc32c" ,0 ,0 );
@@ -4624,6 +4631,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) sb->s_root =NULL ; needs_recovery = (es->s_last_orphan !=0 || +ext4_has_feature_orphan_present (sb) ||ext4_has_feature_journal_needs_recovery (sb));if (ext4_has_feature_mmp (sb) && !sb_rdonly (sb))
@@ -4914,12 +4922,15 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)if (err) goto failed_mount7; + err =ext4_init_orphan_info (sb); +if (err) + goto failed_mount8;#ifdef CONFIG_QUOTA /* Enable quota usage during mount. */ if (ext4_has_feature_quota (sb) && !sb_rdonly (sb)) { err =ext4_enable_quotas (sb);if (err) - goto failed_mount8; + goto failed_mount9; }#endif /* CONFIG_QUOTA */
@@ -4938,7 +4949,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)ext4_msg (sb,KERN_INFO ,"recovery complete" ); err =ext4_mark_recovery_complete (sb, es);if (err) - goto failed_mount8; + goto failed_mount9; }if (EXT4_SB (sb)->s_journal) {if (test_opt (sb,DATA_FLAGS ) ==EXT4_MOUNT_JOURNAL_DATA )
@@ -4984,6 +4995,8 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)ext4_msg (sb,KERN_ERR ,"VFS: Can't find ext4 filesystem" ); goto failed_mount; +failed_mount9: +ext4_release_orphan_info (sb); failed_mount8:ext4_unregister_sysfs (sb);kobject_put (&sbi->s_kobj);
@@ -5494,8 +5507,15 @@ static int ext4_mark_recovery_complete(struct super_block *sb,if (err <0 ) goto out; -if (ext4_has_feature_journal_needs_recovery (sb) &&sb_rdonly (sb)) { +if (sb_rdonly (sb) && (ext4_has_feature_journal_needs_recovery (sb) || +ext4_has_feature_orphan_present (sb))) { +if (!ext4_orphan_file_empty (sb)) { +ext4_error (sb,"Orphan file not empty on read-only fs." ); + err = -EFSCORRUPTED ; + goto out; + }ext4_clear_feature_journal_needs_recovery (sb); +ext4_clear_feature_orphan_present (sb);ext4_commit_super (sb); } out:
@@ -5638,6 +5658,8 @@ static int ext4_freeze(struct super_block *sb)/* Journal blocked and flushed, clear needs_recovery flag. */ ext4_clear_feature_journal_needs_recovery (sb); +if (ext4_orphan_file_empty (sb)) +ext4_clear_feature_orphan_present (sb); } error =ext4_commit_super (sb);
@@ -5660,6 +5682,8 @@ static int ext4_unfreeze(struct super_block *sb)if (EXT4_SB (sb)->s_journal) {/* Reset the needs_recovery flag before the fs is unlocked. */ ext4_set_feature_journal_needs_recovery (sb); +if (ext4_has_feature_orphan_file (sb)) +ext4_set_feature_orphan_present (sb); }ext4_commit_super (sb);
@@ -5863,7 +5887,7 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)* around from a previously readonly bdev mount, * require a full umount/remount for now. */ -if (es->s_last_orphan) { +if (es->s_last_orphan || !ext4_orphan_file_empty (sb)) {ext4_msg (sb,KERN_WARNING ,"Couldn't " "remount RDWR because of unprocessed " "orphan inode list. Please "
Signature · 1 line
2.26.2
Theodore Ts'o
maintainerRe: [PATCH 3/5] ext4: Speedup ext4 orphan inode handling
Quoted 9 lines · Jan Kara
fs/ext4/orphan.c +9
-0+static int ext4_orphan_file_del (handle_t *handle,struct inode *inode) +{ +struct ext4_orphan_info *oi = &EXT4_SB (inode->i_sb)->s_orphan_info; +__le32 *bdata; +int blk, off; +int inodes_per_ob =ext4_inodes_per_orphan_block (inode->i_sb); +int ret =0 ; + +if (!handle)
fs/ext4/orphan.c int ext4_orphan_add(handle_t *handle, struct inode *inode) · line 180
Quoted 10 lines
fs/ext4/orphan.c +10
-0+ goto out; + blk =EXT4_I (inode)->i_orphan_idx / inodes_per_ob; + off =EXT4_I (inode)->i_orphan_idx % inodes_per_ob; +if (WARN_ON_ONCE (blk >= oi->of_blocks)) + goto out; + + ret =ext4_journal_get_write_access (handle, inode->i_sb, + oi->of_binfo[blk].ob_bh,EXT4_JTR_ORPHAN_FILE ); +if (ret) + goto out;
If ext4_journal_get_write_access() fails, we effectively drop the inode from the orphan list (as far as the in-memory inode is concerned), although the inode will still be listed in the orphan file. This can be really unfortunate since if the inode gets reallocated for some other purpose, since its inode number is left in the orphan block, on the next remount, this could lead to data loss.
In the orphan list code, we leave the inode on the linked list, which is not great, since that will prevent the inode from being freed, but at least we're keeping the in-memory and on-disk state in sync and we avoid the data loss scenario when the inode gets reused.
I'll also note that all or at least most of the callers of ext4_orphan_del() are doing error checking, which also unfortunate (although what are we supposed to do in case of a failure here?).
I think keeping things consistent with the existing non-optimal "error handle" at least makes things no worse than before, but looking at the error handling, I'm left with a sense of unease. What do you think?
- Ted
Jan Kara
reviewerRe: [PATCH 3/5] ext4: Speedup ext4 orphan inode handling
Quoted 28 lines · Theodore Ts'o
Quoted 9 lines · Jan Kara
fs/ext4/orphan.c +9
-0+static int ext4_orphan_file_del (handle_t *handle,struct inode *inode) +{ +struct ext4_orphan_info *oi = &EXT4_SB (inode->i_sb)->s_orphan_info; +__le32 *bdata; +int blk, off; +int inodes_per_ob =ext4_inodes_per_orphan_block (inode->i_sb); +int ret =0 ; + +if (!handle)fs/ext4/orphan.c int ext4_orphan_add(handle_t *handle, struct inode *inode) · line 180
Quoted 10 lines
fs/ext4/orphan.c +10
-0+ goto out; + blk =EXT4_I (inode)->i_orphan_idx / inodes_per_ob; + off =EXT4_I (inode)->i_orphan_idx % inodes_per_ob; +if (WARN_ON_ONCE (blk >= oi->of_blocks)) + goto out; + + ret =ext4_journal_get_write_access (handle, inode->i_sb, + oi->of_binfo[blk].ob_bh,EXT4_JTR_ORPHAN_FILE ); +if (ret) + goto out;If
ext4_journal_get_write_access()fails, we effectively drop the inode from the orphan list (as far as the in-memory inode is concerned), although the inode will still be listed in the orphan file. This can be really unfortunate since if the inode gets reallocated for some other purpose, since its inode number is left in the orphan block, on the next remount, this could lead to data loss.
Quoted 4 lines
In the orphan list code, we leave the inode on the linked list, which is not great, since that will prevent the inode from being freed, but at least we're keeping the in-memory and on-disk state in sync and we avoid the data loss scenario when the inode gets reused.
Actually, in the orphan list code, we leave the inode in the on-disk list but remove it from the in-memory list - see how list_del_init(&ei->i_orphan) is called very early in ext4_orphan_del(). The reason for this unconditional deletion is that if we do not remove the inode from the in-memory orphan list, the filesystem will complain and corrupt memory on unmount.
Also note that leaving inode in the on-disk orphan list actually does no serious harm. Because the orphan cleanup code just checks i_nlink and i_disksize and truncates inode down to current i_disksize and removes inode completely if i_nlink is 0. So even if an inode on the orphan list gets reused, orphan cleanup will just do nothing for it. So the worst problem that will likely happen is that on-disk orphan linked list becomes corrupted but there's no data loss AFAICT.
Is it clearer now or am I missing something?
Honza
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
Re: [PATCH 3/5] ext4: Speedup ext4 orphan inode handling
Quoted 15 lines · Jan Kara
Actually, in the orphan list code, we leave the inode in the on-disk list but remove it from the in-memory list - see how
list_del_init(&ei->i_orphan)is called very early inext4_orphan_del(). The reason for this unconditional deletion is that if we do not remove the inode from the in-memory orphan list, the filesystem will complain and corrupt memory on unmount.Also note that leaving inode in the on-disk orphan list actually does no serious harm. Because the orphan cleanup code just checks
i_nlinkandi_disksizeand truncates inode down to currenti_disksizeand removes inode completely ifi_nlinkis 0. So even if an inode on the orphan list gets reused, orphan cleanup will just do nothing for it. So the worst problem that will likely happen is that on-disk orphan linked list becomes corrupted but there's no data loss AFAICT.
Quoted 1 line
Is it clearer now or am I missing something?
Yes, you're right, I misread the code. Thanks for clarifying.
Can you send the final spin of this patch set? I think we're all set for this patch series.
- Ted
Jan Kara
reviewerRe: [PATCH 3/5] ext4: Speedup ext4 orphan inode handling
Quoted 16 lines · Theodore Ts'o
Quoted 15 lines · Jan Kara
Actually, in the orphan list code, we leave the inode in the on-disk list but remove it from the in-memory list - see how
list_del_init(&ei->i_orphan)is called very early inext4_orphan_del(). The reason for this unconditional deletion is that if we do not remove the inode from the in-memory orphan list, the filesystem will complain and corrupt memory on unmount.Also note that leaving inode in the on-disk orphan list actually does no serious harm. Because the orphan cleanup code just checks
i_nlinkandi_disksizeand truncates inode down to currenti_disksizeand removes inode completely ifi_nlinkis 0. So even if an inode on the orphan list gets reused, orphan cleanup will just do nothing for it. So the worst problem that will likely happen is that on-disk orphan linked list becomes corrupted but there's no data loss AFAICT.
Quoted 8 lines
Quoted 1 line
Is it clearer now or am I missing something?
Yes, you're right, I misread the code. Thanks for clarifying.
Can you send the final spin of this patch set? I think we're all set for this patch series.
Reviewed-by Theodore Ts'o <[email protected]>
Thanks. I've posted v6 with all the updates.
Honza
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
Jan Kara
reviewerPATCH v5 4/5 ext4: Orphan file documentation
In reply to Jan KaraAdd documentation about the orphan file feature.
--- Documentation/filesystems/ext4/globals.rst | 1 + Documentation/filesystems/ext4/inodes.rst | 10 ++-- Documentation/filesystems/ext4/orphan.rst | 52 +++++++++++++++++++ .../filesystems/ext4/special_inodes.rst | 17 ++++++ Documentation/filesystems/ext4/super.rst | 15 +++++- 5 files changed, 89 insertions(+), 6 deletions(-) create mode 100644 Documentation/filesystems/ext4/orphan.rst
Documentation/filesystems/ext4/globals.rst +1 -0
@@ -11,3 +11,4 @@ have static metadata at fixed locations. .. include:: bitmaps.rst .. include:: mmp.rst .. include:: journal.rst +.. include:: orphan.rst
Documentation/filesystems/ext4/inodes.rst +5 -5
@@ -498,11 +498,11 @@ structure -- inode change time (ctime), access time (atime), data modification time (mtime), and deletion time (dtime). The four fields are 32-bit signed integers that represent seconds since the Unix epoch (1970-01-01 00:00:00 GMT), which means that the fields will overflow in -January 2038. For inodes that are not linked from any directory but are -still open (orphan inodes), the dtime field is overloaded for use with -the orphan list. The superblock field ``s_last_orphan`` points to the -first inode in the orphan list; dtime is then the number of the next -orphaned inode, or zero if there are no more orphans. +January 2038. If the filesystem does not have orphan_file feature, inodes +that are not linked from any directory but are still open (orphan inodes) have +the dtime field overloaded for use with the orphan list. The superblock field +``s_last_orphan`` points to the first inode in the orphan list; dtime is then +the number of the next orphaned inode, or zero if there are no more orphans. If the inode structure size ``sb->s_inode_size`` is larger than 128 bytes and the ``i_inode_extra`` field is large enough to encompass the
Documentation/filesystems/ext4/orphan.rst new file +52 -0
52 lines not on this page · raw message
Documentation/filesystems/ext4/special_inodes.rst +17 -0
@@ -36,3 +36,20 @@ ext4 reserves some inode for special features, as follows: * - 11 - Traditional first non-reserved inode. Usually this is the lost+found directory. See s\_first\_ino in the superblock. +Note that there are also some inodes allocated from non-reserved inode numbers +for other filesystem features which are not referenced from standard directory +hierarchy. These are generally reference from the superblock. They are: + +.. list-table:: + :widths: 20 50 + :header-rows: 1 + + * - Superblock field + - Description + + * - s\_lpf\_ino + - Inode number of lost+found directory. + * - s\_prj\_quota\_inum + - Inode number of quota file tracking project quotas + * - s\_orphan\_file\_inum + - Inode number of file tracking orphan inodes.
Documentation/filesystems/ext4/super.rst +14 -1
@@ -479,7 +479,11 @@ The ext4 superblock is laid out as follows in - Filename charset encoding flags. * - 0x280 - \_\_le32 - - s\_reserved[95] + - s\_orphan\_file\_inum + - Orphan file inode number. + * - 0x284 + - \_\_le32 + - s\_reserved[94] - Padding to the end of the block. * - 0x3FC - \_\_le32
@@ -603,6 +607,11 @@ following: the journal, JBD2 incompat feature (JBD2\_FEATURE\_INCOMPAT\_FAST\_COMMIT) gets set (COMPAT\_FAST\_COMMIT). + * - 0x1000 + - Orphan file allocated. This is the special file for more efficient + tracking of unlinked but still open inodes. When there may be any + entries in the file, we additionally set proper rocompat feature + (RO\_COMPAT\_ORPHAN\_PRESENT). .. _super_incompat:
@@ -713,6 +722,10 @@ the following: - Filesystem tracks project quotas. (RO\_COMPAT\_PROJECT) * - 0x8000 - Verity inodes may be present on the filesystem. (RO\_COMPAT\_VERITY) + * - 0x10000 + - Indicates orphan file may have valid orphan entries and thus we need + to clean them up when mounting the filesystem + (RO\_COMPAT\_ORPHAN\_PRESENT). .. _super_def_hash:
Signature · 1 line
2.26.2
Re: [PATCH 4/5] ext4: Orphan file documentation
Quoted 3 lines · Jan Kara
Add documentation about the orphan file feature.
Signed-off-by Jan Kara <[email protected]>
- Ted
Jan Kara
reviewerPATCH v5 5/5 ext4: Improve scalability of ext4 orphan file handling
In reply to Jan KaraEven though the length of the critical section when adding / removing orphaned inodes was significantly reduced by using orphan file, the contention of lock protecting orphan file still appears high in profiles for truncate / unlink intensive workloads with high number of threads.
This patch makes handling of orphan file completely lockless. Also to reduce conflicts between CPUs different CPUs start searching for empty slot in orphan file in different blocks.
Performance comparison of locked orphan file handling, lockless orphan file handling, and completely disabled orphan inode handling from 80 CPU Xeon Server with 526 GB of RAM, filesystem located on SAS SSD disk, average of 5 runs:
stress-orphan (microbenchmark truncating files byte-by-byte from N processes in parallel)
Threads Time Time Time
Orphan locked Orphan lockless No orphan
1 0.945600 0.939400 0.891200 2 1.331800 1.246600 1.174400 4 1.995000 1.780600 1.713200 8 6.424200 4.900000 4.106000 16 14.937600 8.516400 8.138000 32 33.038200 24.565600 24.002200 64 60.823600 39.844600 38.440200 128 122.941400 70.950400 69.315000
So we can see that with lockless orphan file handling, addition / deletion of orphaned inodes got almost completely out of picture even for a microbenchmark stressing it.
For reaim creat_clo workload on ramdisk there are also noticeable gains (average of 5 runs):
Clients Vanilla (ops/s) Patched (ops/s) creat_clo-1 14705.88 ( 0.00%) 14354.07 * -2.39%* creat_clo-3 27108.43 ( 0.00%) 28301.89 ( 4.40%) creat_clo-5 37406.48 ( 0.00%) 45180.73 * 20.78%* creat_clo-7 41338.58 ( 0.00%) 54687.50 * 32.29%* creat_clo-9 45226.13 ( 0.00%) 62937.07 * 39.16%* creat_clo-11 44000.00 ( 0.00%) 65088.76 * 47.93%* creat_clo-13 36516.85 ( 0.00%) 68661.97 * 88.03%* creat_clo-15 30864.20 ( 0.00%) 69551.78 * 125.35%* creat_clo-17 27478.45 ( 0.00%) 67729.08 * 146.48%* creat_clo-19 25000.00 ( 0.00%) 61621.62 * 146.49%* creat_clo-21 18772.35 ( 0.00%) 63829.79 * 240.02%* creat_clo-23 16698.94 ( 0.00%) 61938.96 * 270.92%* creat_clo-25 14973.05 ( 0.00%) 56947.61 * 280.33%* creat_clo-27 16436.69 ( 0.00%) 65008.03 * 295.51%* creat_clo-29 13949.01 ( 0.00%) 69047.62 * 395.00%* creat_clo-31 14283.52 ( 0.00%) 67982.45 * 375.95%*
--- fs/ext4/ext4.h | 3 +- fs/ext4/orphan.c | 72 ++++++++++++++++++++++++++++++++---------------- 2 files changed, 49 insertions(+), 26 deletions(-)
fs/ext4/ext4.h +1 -2
@@ -1489,7 +1489,7 @@ static inline int ext4_inodes_per_orphan_block(struct super_block *sb) }struct ext4_orphan_block { -int ob_free_entries;/* Number of free orphan entries in block */ +atomic_t ob_free_entries;/* Number of free orphan entries in block */ struct buffer_head *ob_bh;/* Buffer for orphan block */ };
@@ -1497,7 +1497,6 @@ struct ext4_orphan_block {* Info about orphan file. */ struct ext4_orphan_info { -spinlock_t of_lock;int of_blocks;/* Number of orphan blocks in a file */ __u32 of_csum_seed;/* Checksum seed for orphan file */ struct ext4_orphan_block *of_binfo;/* Array with info about orphan
fs/ext4/orphan.c +48 -24
124 lines not on this page · raw message
Signature · 1 line
2.26.2
kernel test robot
build botRe: [PATCH 5/5] ext4: Improve scalability of ext4 orphan file handling
I love your patch! Perhaps something to improve:
Hi Jan,
I love your patch! Perhaps something to improve:
[auto build test WARNING on ext4/dev]
[also build test WARNING on ext3/for_next linus/master v5.14-rc5 next-20210811] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]
url: https://github.com/0day-ci/linux/commits/Jan-Kara/ext4-Speedup-orphan-file-handling/20210811-182113 base: https://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4.git dev config: parisc-randconfig-s032-20210810 (attached as .config) compiler: hppa-linux-gcc (GCC) 10.3.0
reproduce:
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# apt-get install sparse
# sparse version: v0.6.3-348-gf0e6938b-dirty
# https://github.com/0day-ci/linux/commit/77029a42c6e037181b218cbf10a93561e664fb9e
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Jan-Kara/ext4-Speedup-orphan-file-handling/20210811-182113
git checkout 77029a42c6e037181b218cbf10a93561e664fb9e
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-10.3.0 make.cross C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' O=build_dir ARCH=parisc SHELL=/bin/bash fs/ext4/
If you fix the issue, kindly add following tag as appropriate
sparse warnings: (new ones prefixed by >>)
>> fs/ext4/orphan.c:76:18: sparse: sparse: cast from restricted __le32 >> fs/ext4/orphan.c:76:18: sparse: sparse: cast from restricted __le32 >> fs/ext4/orphan.c:76:18: sparse: sparse: cast to restricted __le32
vim +76 fs/ext4/orphan.c
10
11 static int ext4_orphan_file_add(handle_t *handle, struct inode *inode)
12 {
13 int i, j, start;
14 struct ext4_orphan_info *oi = &EXT4_SB(inode->i_sb)->s_orphan_info;
15 int ret = 0;
16 bool found = false;
17 __le32 *bdata;
18 int inodes_per_ob = ext4_inodes_per_orphan_block(inode->i_sb);
19 int looped = 0;
20
21 /*
22 * Find block with free orphan entry. Use CPU number for a naive hash
23 * for a search start in the orphan file
24 */
25 start = raw_smp_processor_id()*13 % oi->of_blocks;
26 i = start;
27 do {
28 if (atomic_dec_if_positive(&oi->of_binfo[i].ob_free_entries)
29 >= 0) {
30 found = true;
31 break;
32 }
33 if (++i >= oi->of_blocks)
34 i = 0;
35 } while (i != start);
36
37 if (!found) {
38 /*
39 * For now we don't grow or shrink orphan file. We just use
40 * whatever was allocated at mke2fs time. The additional
41 * credits we would have to reserve for each orphan inode
42 * operation just don't seem worth it.
43 */
44 return -ENOSPC;
45 }
46
47 ret = ext4_journal_get_write_access(handle, inode->i_sb,
48 oi->of_binfo[i].ob_bh, EXT4_JTR_ORPHAN_FILE);
49 if (ret)
50 return ret;
51
52 bdata = (__le32 *)(oi->of_binfo[i].ob_bh->b_data);
53 /* Find empty slot in a block */
54 j = 0;
55 do {
56 if (looped) {
57 /*
58 * Did we walk through the block several times without
59 * finding free entry? It is theoretically possible
60 * if entries get constantly allocated and freed or
61 * if the block is corrupted. Avoid indefinite looping
62 * and bail. We'll use orphan list instead.
63 */
64 if (looped > 3) {
65 atomic_inc(&oi->of_binfo[i].ob_free_entries);
66 return -ENOSPC;
67 }
68 cond_resched();
69 }
70 while (bdata[j]) {
71 if (++j >= inodes_per_ob) {
72 j = 0;
73 looped++;
74 }
75 }
> 76 } while (cmpxchg(&bdata[j], 0, cpu_to_le32(inode->i_ino)) != 0);
77
78 EXT4_I(inode)->i_orphan_idx = i * inodes_per_ob + j;
79 ext4_set_inode_state(inode, EXT4_STATE_ORPHAN_FILE);
80
81 return ext4_handle_dirty_metadata(handle, NULL, oi->of_binfo[i].ob_bh);
82 }
83 ---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org
Jan Kara
reviewerRe: [PATCH 5/5] ext4: Improve scalability of ext4 orphan file handling
Quoted 34 lines · kernel test robot
Hi Jan,
I love your patch! Perhaps something to improve:
[auto build test WARNING on ext4/dev]
[also build test WARNING on ext3/for_next linus/master v5.14-rc5 next-20210811] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]url: https://github.com/0day-ci/linux/commits/Jan-Kara/ext4-Speedup-orphan-file-handling/20210811-182113 base: https://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4.git dev config: parisc-randconfig-s032-20210810 (attached as .config) compiler: hppa-linux-gcc (GCC) 10.3.0reproduce:
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# apt-get install sparse
# sparse version: v0.6.3-348-gf0e6938b-dirty
# https://github.com/0day-ci/linux/commit/77029a42c6e037181b218cbf10a93561e664fb9e
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Jan-Kara/ext4-Speedup-orphan-file-handling/20210811-182113
git checkout77029a42c6e037181b218cbf10a93561e664fb9e
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-10.3.0 make.cross C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' O=build_dir ARCH=parisc SHELL=/bin/bashfs/ext4/If you fix the issue, kindly add following tag as appropriate
Reported-by kernel test robot <[email protected]>sparse warnings: (new ones prefixed by >>)
Quoted 3 lines
Quoted 3 lines
fs/ext4/orphan.c:76:18: sparse: sparse: cast from restricted__le32fs/ext4/orphan.c:76:18: sparse: sparse: cast from restricted__le32fs/ext4/orphan.c:76:18: sparse: sparse: cast to restricted__le32
...
Quoted 1 line
Quoted 1 line
76 } while (
cmpxchg(&bdata[j], 0, cpu_to_le32(inode->i_ino))!= 0);
Yeah, I didn't bother to convert 0 to little endian which is tripping up sparse. I've now added explicit casts of 0 to __le32 so silence the warning.
Honza
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
Re: [PATCH 5/5] ext4: Improve scalability of ext4 orphan file handling
In reply to Jan KaraQuoted 10 lines · Jan Kara
fs/ext4/orphan.c +0
-2@@ -28,28 +43,40 @@ static int ext4_orphan_file_add(handle_t *handle, struct inode *inode)*/ return -ENOSPC ; } - oi->of_binfo[i].ob_free_entries--; -spin_unlock (&oi->of_lock);
fs/ext4/orphan.c static int ext4_orphan_file_add(handle_t *handle, struct inode *inode) · line 50
Quoted 10 lines
fs/ext4/orphan.c +0
-5-/* -* Get access to orphan block. We have dropped of_lock but since we -* have decremented number of free entries we are guaranteed free entry -* in our block. -*/ ret =ext4_journal_get_write_access (handle, inode->i_sb, oi->of_binfo[i].ob_bh,EXT4_JTR_ORPHAN_FILE );if (ret)return ret;
Shouldn't there be a call to:
atomic_inc(&oi->of_binfo[i].ob_free_entries);
before we return, so the free_entry count stays consistent?
Otherwise, with the test-bot comments also addressed, you can add:
Thanks,
- Ted