Pull ext4 fixes from Ted Ts'o: - Update the MAINTAINERS file to add reviewers for the ext4 file system - Add a test issue an ext4 warning (not a WARN_ON) if there are still dirty pages attached to an evicted inode. - Fix a number of Syzkaller issues - Fix memory leaks on error paths - Replace some BUG and WARN with EFSCORRUPTED reporting - Fix a potential crash when disabling discard via remount followed by an immediate unmount. (Found by Sashiko) - Fix a corner case which could lead to allocating blocks for an indirect-mapped inode block numbers > 2**32 - Fix a race when reallocating a freed inode that could result in a deadlock - Fix a user-after-free in update_super_work when racing with umount - Fix build issues when trying to build ext4's kunit tests as a module - Fix a bug where ext4_split_extent_zeroout() could fail to pass back an error from ext4_ext_dirty() - Avoid allocating blocks from a corrupted block group in ext4_mb_find_by_goal() - Fix a percpu_counters list corruption BUG triggered by an ext4 extents kunit - Fix a potetial crash caused by the fast commit flush path potentially accessing the jinode structure before it is fully initialized - Fix fsync(2) in no-journal mode to make sure the dirtied inode is write to storage - Fix a bug when in no-journal mode, when ext4 tries to avoid using recently deleted inodes, if lazy itable initialization is enabled, can lead to an unitialized inode getting skipped and triggering an e2fsck complaint - Fix journal credit calculation when setting an xattr when both the encryption and ea_inode feeatures are enabled - Fix corner cases which could result in stale xarray tags after writeback - Fix generic/475 failures caused by ENOSPC errors while creating a symlink when the system crashes resulting to a file system inconsistency when replaying the fast commit journal * tag 'ext4_for_linus-7.0-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4: (27 commits) ext4: always drain queued discard work in ext4_mb_release() ext4: handle wraparound when searching for blocks for indirect mapped blocks ext4: skip split extent recovery on corruption ext4: fix iloc.bh leak in ext4_fc_replay_inode() error paths ext4: fix deadlock on inode reallocation ext4: fix use-after-free in update_super_work when racing with umount ext4: fix the might_sleep() warnings in kvfree() ext4: reject mount if bigalloc with s_first_data_block != 0 ext4: fix extents-test.c is not compiled when EXT4_KUNIT_TESTS=M ext4: fix mballoc-test.c is not compiled when EXT4_KUNIT_TESTS=M ext4: introduce EXPORT_SYMBOL_FOR_EXT4_TEST() helper jbd2: gracefully abort on checkpointing state corruptions ext4: avoid infinite loops caused by residual data ext4: validate p_idx bounds in ext4_ext_correct_indexes ext4: test if inode's all dirty pages are submitted to disk ext4: minor fix for ext4_split_extent_zeroout() ext4: avoid allocate block from corrupted group in ext4_mb_find_by_goal() ext4: kunit: extents-test: lix percpu_counters list corruption ext4: publish jinode after initialization ext4: replace BUG_ON with proper error handling in ext4_read_inline_folio ...
Unified · Split
| @@ -9619,7 +9619,12 @@ F: include/linux/ext2* · 9618 unmodified lines | ||
| 9619 | 9619 | |
| 9620 | 9620 | EXT4 FILE SYSTEM |
| 9621 | 9621 | M: "Theodore Ts'o" <tytso@mit.edu> |
| 9622 | −M: Andreas Dilger <adilger.kernel@dilger.ca> | |
| 9622 | +R: Andreas Dilger <adilger.kernel@dilger.ca> | |
| 9623 | +R: Baokun Li <libaokun@linux.alibaba.com> | |
| 9624 | +R: Jan Kara <jack@suse.cz> | |
| 9625 | +R: Ojaswin Mujoo <ojaswin@linux.ibm.com> | |
| 9626 | +R: Ritesh Harjani (IBM) <ritesh.list@gmail.com> | |
| 9627 | +R: Zhang Yi <yi.zhang@huawei.com> | |
| 9623 | 9628 | L: linux-ext4@vger.kernel.org |
| 9624 | 9629 | S: Maintained |
| 9625 | 9630 | W: http://ext4.wiki.kernel.org |
| @@ -14,7 +14,8 @@ ext4-y := balloc.o bitmap.o block_validity.o dir.o ext4_jbd2.o extents.o \ · 13 unmodified lines | ||
| 14 | 14 | |
| 15 | 15 | ext4-$(CONFIG_EXT4_FS_POSIX_ACL) += acl.o |
| 16 | 16 | ext4-$(CONFIG_EXT4_FS_SECURITY) += xattr_security.o |
| 17 | −ext4-inode-test-objs += inode-test.o | |
| 18 | −obj-$(CONFIG_EXT4_KUNIT_TESTS) += ext4-inode-test.o | |
| 17 | +ext4-test-objs += inode-test.o mballoc-test.o \ | |
| 18 | + extents-test.o | |
| 19 | +obj-$(CONFIG_EXT4_KUNIT_TESTS) += ext4-test.o | |
| 19 | 20 | ext4-$(CONFIG_FS_VERITY) += verity.o |
| 20 | 21 | ext4-$(CONFIG_FS_ENCRYPTION) += crypto.o |
| @@ -163,10 +163,17 @@ static int ext4_set_context(struct inode *inode, const void *ctx, size_t len, · 162 unmodified lines | ||
| 163 | 163 | */ |
| 164 | 164 | |
| 165 | 165 | if (handle) { |
| 166 | + /* | |
| 167 | + * Since the inode is new it is ok to pass the | |
| 168 | + * XATTR_CREATE flag. This is necessary to match the | |
| 169 | + * remaining journal credits check in the set_handle | |
| 170 | + * function with the credits allocated for the new | |
| 171 | + * inode. | |
| 172 | + */ | |
| 166 | 173 | res = ext4_xattr_set_handle(handle, inode, |
| 167 | 174 | EXT4_XATTR_INDEX_ENCRYPTION, |
| 168 | 175 | EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, |
| 169 | − ctx, len, 0); | |
| 176 | + ctx, len, XATTR_CREATE); | |
| 170 | 177 | if (!res) { |
| 171 | 178 | ext4_set_inode_flag(inode, EXT4_INODE_ENCRYPT); |
| 172 | 179 | ext4_clear_inode_state(inode, |
| @@ -1570,6 +1570,7 @@ struct ext4_sb_info { · 1569 unmodified lines | ||
| 1570 | 1570 | struct proc_dir_entry *s_proc; |
| 1571 | 1571 | struct kobject s_kobj; |
| 1572 | 1572 | struct completion s_kobj_unregister; |
| 1573 | + struct mutex s_error_notify_mutex; /* protects sysfs_notify vs kobject_del */ | |
| 1573 | 1574 | struct super_block *s_sb; |
| 1574 | 1575 | struct buffer_head *s_mmp_bh; |
| 1575 | 1576 | |
| @@ -3944,6 +3945,11 @@ static inline bool ext4_inode_can_atomic_write(struct inode *inode) · 2368 unmodified lines | ||
| 3944 | 3945 | extern int ext4_block_write_begin(handle_t *handle, struct folio *folio, |
| 3945 | 3946 | loff_t pos, unsigned len, |
| 3946 | 3947 | get_block_t *get_block); |
| 3948 | + | |
| 3949 | +#if IS_ENABLED(CONFIG_EXT4_KUNIT_TESTS) | |
| 3950 | +#define EXPORT_SYMBOL_FOR_EXT4_TEST(sym) \ | |
| 3951 | + EXPORT_SYMBOL_FOR_MODULES(sym, "ext4-test") | |
| 3952 | +#endif | |
| 3947 | 3953 | #endif /* __KERNEL__ */ |
| 3948 | 3954 | |
| 3949 | 3955 | #endif /* _EXT4_H */ |
| @@ -264,5 +264,17 @@ static inline void ext4_idx_store_pblock(struct ext4_extent_idx *ix, · 263 unmodified lines | ||
| 264 | 264 | 0xffff); |
| 265 | 265 | } |
| 266 | 266 | |
| 267 | +extern int __ext4_ext_dirty(const char *where, unsigned int line, | |
| 268 | + handle_t *handle, struct inode *inode, | |
| 269 | + struct ext4_ext_path *path); | |
| 270 | +extern int ext4_ext_zeroout(struct inode *inode, struct ext4_extent *ex); | |
| 271 | +#if IS_ENABLED(CONFIG_EXT4_KUNIT_TESTS) | |
| 272 | +extern int ext4_ext_space_root_idx_test(struct inode *inode, int check); | |
| 273 | +extern struct ext4_ext_path *ext4_split_convert_extents_test( | |
| 274 | + handle_t *handle, struct inode *inode, | |
| 275 | + struct ext4_map_blocks *map, | |
| 276 | + struct ext4_ext_path *path, | |
| 277 | + int flags, unsigned int *allocated); | |
| 278 | +#endif | |
| 267 | 279 | #endif /* _EXT4_EXTENTS */ |
| 268 | 280 | |
| @@ -142,8 +142,10 @@ static struct file_system_type ext_fs_type = { · 141 unmodified lines | ||
| 142 | 142 | |
| 143 | 143 | static void extents_kunit_exit(struct kunit *test) |
| 144 | 144 | { |
| 145 | − struct ext4_sb_info *sbi = k_ctx.k_ei->vfs_inode.i_sb->s_fs_info; | |
| 145 | + struct super_block *sb = k_ctx.k_ei->vfs_inode.i_sb; | |
| 146 | + struct ext4_sb_info *sbi = sb->s_fs_info; | |
| 146 | 147 | |
| 148 | + ext4_es_unregister_shrinker(sbi); | |
| 147 | 149 | kfree(sbi); |
| 148 | 150 | kfree(k_ctx.k_ei); |
| 149 | 151 | kfree(k_ctx.k_data); |
| @@ -280,8 +282,8 @@ static int extents_kunit_init(struct kunit *test) · 130 unmodified lines | ||
| 280 | 282 | eh->eh_depth = 0; |
| 281 | 283 | eh->eh_entries = cpu_to_le16(1); |
| 282 | 284 | eh->eh_magic = EXT4_EXT_MAGIC; |
| 283 | − eh->eh_max = | |
| 284 | − cpu_to_le16(ext4_ext_space_root_idx(&k_ctx.k_ei->vfs_inode, 0)); | |
| 285 | + eh->eh_max = cpu_to_le16(ext4_ext_space_root_idx_test( | |
| 286 | + &k_ctx.k_ei->vfs_inode, 0)); | |
| 285 | 287 | eh->eh_generation = 0; |
| 286 | 288 | |
| 287 | 289 | /* |
| @@ -384,8 +386,8 @@ static void test_split_convert(struct kunit *test) · 96 unmodified lines | ||
| 384 | 386 | |
| 385 | 387 | switch (param->type) { |
| 386 | 388 | case TEST_SPLIT_CONVERT: |
| 387 | − path = ext4_split_convert_extents(NULL, inode, &map, path, | |
| 388 | − param->split_flags, NULL); | |
| 389 | + path = ext4_split_convert_extents_test(NULL, inode, &map, | |
| 390 | + path, param->split_flags, NULL); | |
| 389 | 391 | break; |
| 390 | 392 | case TEST_CREATE_BLOCKS: |
| 391 | 393 | ext4_map_create_blocks_helper(test, inode, &map, param->split_flags); |
| @@ -184,9 +184,9 @@ static int ext4_ext_get_access(handle_t *handle, struct inode *inode, · 183 unmodified lines | ||
| 184 | 184 | * - ENOMEM |
| 185 | 185 | * - EIO |
| 186 | 186 | */ |
| 187 | −static int __ext4_ext_dirty(const char *where, unsigned int line, | |
| 188 | − handle_t *handle, struct inode *inode, | |
| 189 | − struct ext4_ext_path *path) | |
| 187 | +int __ext4_ext_dirty(const char *where, unsigned int line, | |
| 188 | + handle_t *handle, struct inode *inode, | |
| 189 | + struct ext4_ext_path *path) | |
| 190 | 190 | { |
| 191 | 191 | int err; |
| 192 | 192 | |
| @@ -1736,6 +1736,13 @@ static int ext4_ext_correct_indexes(handle_t *handle, struct inode *inode, · 1543 unmodified lines | ||
| 1736 | 1736 | err = ext4_ext_get_access(handle, inode, path + k); |
| 1737 | 1737 | if (err) |
| 1738 | 1738 | return err; |
| 1739 | + if (unlikely(path[k].p_idx > EXT_LAST_INDEX(path[k].p_hdr))) { | |
| 1740 | + EXT4_ERROR_INODE(inode, | |
| 1741 | + "path[%d].p_idx %p > EXT_LAST_INDEX %p", | |
| 1742 | + k, path[k].p_idx, | |
| 1743 | + EXT_LAST_INDEX(path[k].p_hdr)); | |
| 1744 | + return -EFSCORRUPTED; | |
| 1745 | + } | |
| 1739 | 1746 | path[k].p_idx->ei_block = border; |
| 1740 | 1747 | err = ext4_ext_dirty(handle, inode, path + k); |
| 1741 | 1748 | if (err) |
| @@ -1748,6 +1755,14 @@ static int ext4_ext_correct_indexes(handle_t *handle, struct inode *inode, · 6 unmodified lines | ||
| 1748 | 1755 | err = ext4_ext_get_access(handle, inode, path + k); |
| 1749 | 1756 | if (err) |
| 1750 | 1757 | goto clean; |
| 1758 | + if (unlikely(path[k].p_idx > EXT_LAST_INDEX(path[k].p_hdr))) { | |
| 1759 | + EXT4_ERROR_INODE(inode, | |
| 1760 | + "path[%d].p_idx %p > EXT_LAST_INDEX %p", | |
| 1761 | + k, path[k].p_idx, | |
| 1762 | + EXT_LAST_INDEX(path[k].p_hdr)); | |
| 1763 | + err = -EFSCORRUPTED; | |
| 1764 | + goto clean; | |
| 1765 | + } | |
| 1751 | 1766 | path[k].p_idx->ei_block = border; |
| 1752 | 1767 | err = ext4_ext_dirty(handle, inode, path + k); |
| 1753 | 1768 | if (err) |
| @@ -3144,7 +3159,7 @@ static void ext4_zeroout_es(struct inode *inode, struct ext4_extent *ex) · 1390 unmodified lines | ||
| 3144 | 3159 | } |
| 3145 | 3160 | |
| 3146 | 3161 | /* FIXME!! we need to try to merge to left or right after zero-out */ |
| 3147 | −static int ext4_ext_zeroout(struct inode *inode, struct ext4_extent *ex) | |
| 3162 | +int ext4_ext_zeroout(struct inode *inode, struct ext4_extent *ex) | |
| 3148 | 3163 | { |
| 3149 | 3164 | ext4_fsblk_t ee_pblock; |
| 3150 | 3165 | unsigned int ee_len; |
| @@ -3239,6 +3254,9 @@ static struct ext4_ext_path *ext4_split_extent_at(handle_t *handle, · 88 unmodified lines | ||
| 3239 | 3254 | |
| 3240 | 3255 | insert_err = PTR_ERR(path); |
| 3241 | 3256 | err = 0; |
| 3257 | + if (insert_err != -ENOSPC && insert_err != -EDQUOT && | |
| 3258 | + insert_err != -ENOMEM) | |
| 3259 | + goto out_path; | |
| 3242 | 3260 | |
| 3243 | 3261 | /* |
| 3244 | 3262 | * Get a new path to try to zeroout or fix the extent length. |
| @@ -3255,13 +3273,20 @@ static struct ext4_ext_path *ext4_split_extent_at(handle_t *handle, · 10 unmodified lines | ||
| 3255 | 3273 | goto out_path; |
| 3256 | 3274 | } |
| 3257 | 3275 | |
| 3276 | + depth = ext_depth(inode); | |
| 3277 | + ex = path[depth].p_ext; | |
| 3278 | + if (!ex) { | |
| 3279 | + EXT4_ERROR_INODE(inode, | |
| 3280 | + "bad extent address lblock: %lu, depth: %d pblock %llu", | |
| 3281 | + (unsigned long)ee_block, depth, path[depth].p_block); | |
| 3282 | + err = -EFSCORRUPTED; | |
| 3283 | + goto out; | |
| 3284 | + } | |
| 3285 | + | |
| 3258 | 3286 | err = ext4_ext_get_access(handle, inode, path + depth); |
| 3259 | 3287 | if (err) |
| 3260 | 3288 | goto out; |
| 3261 | 3289 | |
| 3262 | − depth = ext_depth(inode); | |
| 3263 | − ex = path[depth].p_ext; | |
| 3264 | − | |
| 3265 | 3290 | fix_extent_len: |
| 3266 | 3291 | ex->ee_len = orig_ex.ee_len; |
| 3267 | 3292 | err = ext4_ext_dirty(handle, inode, path + path->p_depth); |
| @@ -3363,7 +3388,7 @@ static int ext4_split_extent_zeroout(handle_t *handle, struct inode *inode, · 95 unmodified lines | ||
| 3363 | 3388 | |
| 3364 | 3389 | ext4_ext_mark_initialized(ex); |
| 3365 | 3390 | |
| 3366 | − ext4_ext_dirty(handle, inode, path + depth); | |
| 3391 | + err = ext4_ext_dirty(handle, inode, path + depth); | |
| 3367 | 3392 | if (err) |
| 3368 | 3393 | return err; |
| 3369 | 3394 | |
| @@ -4457,9 +4482,13 @@ got_allocated_blocks: · 1087 unmodified lines | ||
| 4457 | 4482 | path = ext4_ext_insert_extent(handle, inode, path, &newex, flags); |
| 4458 | 4483 | if (IS_ERR(path)) { |
| 4459 | 4484 | err = PTR_ERR(path); |
| 4460 | − if (allocated_clusters) { | |
| 4485 | + /* | |
| 4486 | + * Gracefully handle out of space conditions. If the filesystem | |
| 4487 | + * is inconsistent, we'll just leak allocated blocks to avoid | |
| 4488 | + * causing even more damage. | |
| 4489 | + */ | |
| 4490 | + if (allocated_clusters && (err == -EDQUOT || err == -ENOSPC)) { | |
| 4461 | 4491 | int fb_flags = 0; |
| 4462 | − | |
| 4463 | 4492 | /* |
| 4464 | 4493 | * free data blocks we just allocated. |
| 4465 | 4494 | * not a good idea to call discard here directly, |
| @@ -6238,6 +6267,33 @@ out: · 1772 unmodified lines | ||
| 6238 | 6267 | return 0; |
| 6239 | 6268 | } |
| 6240 | 6269 | |
| 6241 | −#ifdef CONFIG_EXT4_KUNIT_TESTS | |
| 6242 | −#include "extents-test.c" | |
| 6270 | +#if IS_ENABLED(CONFIG_EXT4_KUNIT_TESTS) | |
| 6271 | +int ext4_ext_space_root_idx_test(struct inode *inode, int check) | |
| 6272 | +{ | |
| 6273 | + return ext4_ext_space_root_idx(inode, check); | |
| 6274 | +} | |
| 6275 | +EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_ext_space_root_idx_test); | |
| 6276 | + | |
| 6277 | +struct ext4_ext_path *ext4_split_convert_extents_test(handle_t *handle, | |
| 6278 | + struct inode *inode, struct ext4_map_blocks *map, | |
| 6279 | + struct ext4_ext_path *path, int flags, | |
| 6280 | + unsigned int *allocated) | |
| 6281 | +{ | |
| 6282 | + return ext4_split_convert_extents(handle, inode, map, path, | |
| 6283 | + flags, allocated); | |
| 6284 | +} | |
| 6285 | +EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_split_convert_extents_test); | |
| 6286 | + | |
| 6287 | +EXPORT_SYMBOL_FOR_EXT4_TEST(__ext4_ext_dirty); | |
| 6288 | +EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_ext_zeroout); | |
| 6289 | +EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_es_register_shrinker); | |
| 6290 | +EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_es_unregister_shrinker); | |
| 6291 | +EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_map_create_blocks); | |
| 6292 | +EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_es_init_tree); | |
| 6293 | +EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_es_lookup_extent); | |
| 6294 | +EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_es_insert_extent); | |
| 6295 | +EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_ext_insert_extent); | |
| 6296 | +EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_find_extent); | |
| 6297 | +EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_issue_zeroout); | |
| 6298 | +EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_map_query_blocks); | |
| 6243 | 6299 | #endif |
| @@ -975,13 +975,13 @@ static int ext4_fc_flush_data(journal_t *journal) · 974 unmodified lines | ||
| 975 | 975 | int ret = 0; |
| 976 | 976 | |
| 977 | 977 | list_for_each_entry(ei, &sbi->s_fc_q[FC_Q_MAIN], i_fc_list) { |
| 978 | − ret = jbd2_submit_inode_data(journal, ei->jinode); | |
| 978 | + ret = jbd2_submit_inode_data(journal, READ_ONCE(ei->jinode)); | |
| 979 | 979 | if (ret) |
| 980 | 980 | return ret; |
| 981 | 981 | } |
| 982 | 982 | |
| 983 | 983 | list_for_each_entry(ei, &sbi->s_fc_q[FC_Q_MAIN], i_fc_list) { |
| 984 | − ret = jbd2_wait_inode_data(journal, ei->jinode); | |
| 984 | + ret = jbd2_wait_inode_data(journal, READ_ONCE(ei->jinode)); | |
| 985 | 985 | if (ret) |
| 986 | 986 | return ret; |
| 987 | 987 | } |
| @@ -1613,19 +1613,21 @@ static int ext4_fc_replay_inode(struct super_block *sb, · 625 unmodified lines | ||
| 1613 | 1613 | /* Immediately update the inode on disk. */ |
| 1614 | 1614 | ret = ext4_handle_dirty_metadata(NULL, NULL, iloc.bh); |
| 1615 | 1615 | if (ret) |
| 1616 | − goto out; | |
| 1616 | + goto out_brelse; | |
| 1617 | 1617 | ret = sync_dirty_buffer(iloc.bh); |
| 1618 | 1618 | if (ret) |
| 1619 | − goto out; | |
| 1619 | + goto out_brelse; | |
| 1620 | 1620 | ret = ext4_mark_inode_used(sb, ino); |
| 1621 | 1621 | if (ret) |
| 1622 | − goto out; | |
| 1622 | + goto out_brelse; | |
| 1623 | 1623 | |
| 1624 | 1624 | /* Given that we just wrote the inode on disk, this SHOULD succeed. */ |
| 1625 | 1625 | inode = ext4_iget(sb, ino, EXT4_IGET_NORMAL); |
| 1626 | 1626 | if (IS_ERR(inode)) { |
| 1627 | 1627 | ext4_debug("Inode not found."); |
| 1628 | − return -EFSCORRUPTED; | |
| 1628 | + inode = NULL; | |
| 1629 | + ret = -EFSCORRUPTED; | |
| 1630 | + goto out_brelse; | |
| 1629 | 1631 | } |
| 1630 | 1632 | |
| 1631 | 1633 | /* |
| @@ -1642,13 +1644,14 @@ static int ext4_fc_replay_inode(struct super_block *sb, · 10 unmodified lines | ||
| 1642 | 1644 | ext4_inode_csum_set(inode, ext4_raw_inode(&iloc), EXT4_I(inode)); |
| 1643 | 1645 | ret = ext4_handle_dirty_metadata(NULL, NULL, iloc.bh); |
| 1644 | 1646 | sync_dirty_buffer(iloc.bh); |
| 1647 | +out_brelse: | |
| 1645 | 1648 | brelse(iloc.bh); |
| 1646 | 1649 | out: |
| 1647 | 1650 | iput(inode); |
| 1648 | 1651 | if (!ret) |
| 1649 | 1652 | blkdev_issue_flush(sb->s_bdev); |
| 1650 | 1653 | |
| 1651 | − return 0; | |
| 1654 | + return ret; | |
| 1652 | 1655 | } |
| 1653 | 1656 | |
| 1654 | 1657 | /* |
| @@ -83,11 +83,23 @@ static int ext4_fsync_nojournal(struct file *file, loff_t start, loff_t end, · 82 unmodified lines | ||
| 83 | 83 | int datasync, bool *needs_barrier) |
| 84 | 84 | { |
| 85 | 85 | struct inode *inode = file->f_inode; |
| 86 | + struct writeback_control wbc = { | |
| 87 | + .sync_mode = WB_SYNC_ALL, | |
| 88 | + .nr_to_write = 0, | |
| 89 | + }; | |
| 86 | 90 | int ret; |
| 87 | 91 | |
| 88 | 92 | ret = generic_buffers_fsync_noflush(file, start, end, datasync); |
| 89 | − if (!ret) | |
| 90 | − ret = ext4_sync_parent(inode); | |
| 93 | + if (ret) | |
| 94 | + return ret; | |
| 95 | + | |
| 96 | + /* Force writeout of inode table buffer to disk */ | |
| 97 | + ret = ext4_write_inode(inode, &wbc); | |
| 98 | + if (ret) | |
| 99 | + return ret; | |
| 100 | + | |
| 101 | + ret = ext4_sync_parent(inode); | |
| 102 | + | |
| 91 | 103 | if (test_opt(inode->i_sb, BARRIER)) |
| 92 | 104 | *needs_barrier = true; |
| 93 | 105 | |
| @@ -686,6 +686,12 @@ static int recently_deleted(struct super_block *sb, ext4_group_t group, int ino) · 685 unmodified lines | ||
| 686 | 686 | if (unlikely(!gdp)) |
| 687 | 687 | return 0; |
| 688 | 688 | |
| 689 | + /* Inode was never used in this filesystem? */ | |
| 690 | + if (ext4_has_group_desc_csum(sb) && | |
| 691 | + (gdp->bg_flags & cpu_to_le16(EXT4_BG_INODE_UNINIT) || | |
| 692 | + ino >= EXT4_INODES_PER_GROUP(sb) - ext4_itable_unused_count(sb, gdp))) | |
| 693 | + return 0; | |
| 694 | + | |
| 689 | 695 | bh = sb_find_get_block(sb, ext4_inode_table(sb, gdp) + |
| 690 | 696 | (ino / inodes_per_block)); |
| 691 | 697 | if (!bh || !buffer_uptodate(bh)) |
| @@ -522,7 +522,15 @@ static int ext4_read_inline_folio(struct inode *inode, struct folio *folio) · 521 unmodified lines | ||
| 522 | 522 | goto out; |
| 523 | 523 | |
| 524 | 524 | len = min_t(size_t, ext4_get_inline_size(inode), i_size_read(inode)); |
| 525 | − BUG_ON(len > PAGE_SIZE); | |
| 525 | + | |
| 526 | + if (len > PAGE_SIZE) { | |
| 527 | + ext4_error_inode(inode, __func__, __LINE__, 0, | |
| 528 | + "inline size %zu exceeds PAGE_SIZE", len); | |
| 529 | + ret = -EFSCORRUPTED; | |
| 530 | + brelse(iloc.bh); | |
| 531 | + goto out; | |
| 532 | + } | |
| 533 | + | |
| 526 | 534 | kaddr = kmap_local_folio(folio, 0); |
| 527 | 535 | ret = ext4_read_inline_data(inode, kaddr, len, &iloc); |
| 528 | 536 | kaddr = folio_zero_tail(folio, len, kaddr + len); |
| @@ -128,6 +128,8 @@ void ext4_inode_csum_set(struct inode *inode, struct ext4_inode *raw, · 127 unmodified lines | ||
| 128 | 128 | static inline int ext4_begin_ordered_truncate(struct inode *inode, |
| 129 | 129 | loff_t new_size) |
| 130 | 130 | { |
| 131 | + struct jbd2_inode *jinode = READ_ONCE(EXT4_I(inode)->jinode); | |
| 132 | + | |
| 131 | 133 | trace_ext4_begin_ordered_truncate(inode, new_size); |
| 132 | 134 | /* |
| 133 | 135 | * If jinode is zero, then we never opened the file for |
| @@ -135,10 +137,10 @@ static inline int ext4_begin_ordered_truncate(struct inode *inode, · 1 unmodified line | ||
| 135 | 137 | * jbd2_journal_begin_ordered_truncate() since there's no |
| 136 | 138 | * outstanding writes we need to flush. |
| 137 | 139 | */ |
| 138 | − if (!EXT4_I(inode)->jinode) | |
| 140 | + if (!jinode) | |
| 139 | 141 | return 0; |
| 140 | 142 | return jbd2_journal_begin_ordered_truncate(EXT4_JOURNAL(inode), |
| 141 | − EXT4_I(inode)->jinode, | |
| 143 | + jinode, | |
| 142 | 144 | new_size); |
| 143 | 145 | } |
| 144 | 146 | |
| @@ -184,6 +186,14 @@ void ext4_evict_inode(struct inode *inode) · 39 unmodified lines | ||
| 184 | 186 | if (EXT4_I(inode)->i_flags & EXT4_EA_INODE_FL) |
| 185 | 187 | ext4_evict_ea_inode(inode); |
| 186 | 188 | if (inode->i_nlink) { |
| 189 | + /* | |
| 190 | + * If there's dirty page will lead to data loss, user | |
| 191 | + * could see stale data. | |
| 192 | + */ | |
| 193 | + if (unlikely(!ext4_emergency_state(inode->i_sb) && | |
| 194 | + mapping_tagged(&inode->i_data, PAGECACHE_TAG_DIRTY))) | |
| 195 | + ext4_warning_inode(inode, "data will be lost"); | |
| 196 | + | |
| 187 | 197 | truncate_inode_pages_final(&inode->i_data); |
| 188 | 198 | |
| 189 | 199 | goto no_delete; |
| @@ -4451,8 +4461,13 @@ int ext4_inode_attach_jinode(struct inode *inode) · 4261 unmodified lines | ||
| 4451 | 4461 | spin_unlock(&inode->i_lock); |
| 4452 | 4462 | return -ENOMEM; |
| 4453 | 4463 | } |
| 4454 | − ei->jinode = jinode; | |
| 4455 | − jbd2_journal_init_jbd_inode(ei->jinode, inode); | |
| 4464 | + jbd2_journal_init_jbd_inode(jinode, inode); | |
| 4465 | + /* | |
| 4466 | + * Publish ->jinode only after it is fully initialized so that | |
| 4467 | + * readers never observe a partially initialized jbd2_inode. | |
| 4468 | + */ | |
| 4469 | + smp_wmb(); | |
| 4470 | + WRITE_ONCE(ei->jinode, jinode); | |
| 4456 | 4471 | jinode = NULL; |
| 4457 | 4472 | } |
| 4458 | 4473 | spin_unlock(&inode->i_lock); |
| @@ -5401,18 +5416,36 @@ struct inode *__ext4_iget(struct super_block *sb, unsigned long ino, · 942 unmodified lines | ||
| 5401 | 5416 | inode->i_op = &ext4_encrypted_symlink_inode_operations; |
| 5402 | 5417 | } else if (ext4_inode_is_fast_symlink(inode)) { |
| 5403 | 5418 | inode->i_op = &ext4_fast_symlink_inode_operations; |
| 5404 | − if (inode->i_size == 0 || | |
| 5405 | − inode->i_size >= sizeof(ei->i_data) || | |
| 5406 | − strnlen((char *)ei->i_data, inode->i_size + 1) != | |
| 5407 | − inode->i_size) { | |
| 5408 | − ext4_error_inode(inode, function, line, 0, | |
| 5409 | − "invalid fast symlink length %llu", | |
| 5410 | − (unsigned long long)inode->i_size); | |
| 5411 | − ret = -EFSCORRUPTED; | |
| 5412 | − goto bad_inode; | |
| 5419 | + | |
| 5420 | + /* | |
| 5421 | + * Orphan cleanup can see inodes with i_size == 0 | |
| 5422 | + * and i_data uninitialized. Skip size checks in | |
| 5423 | + * that case. This is safe because the first thing | |
| 5424 | + * ext4_evict_inode() does for fast symlinks is | |
| 5425 | + * clearing of i_data and i_size. | |
| 5426 | + */ | |
| 5427 | + if ((EXT4_SB(sb)->s_mount_state & EXT4_ORPHAN_FS)) { | |
| 5428 | + if (inode->i_nlink != 0) { | |
| 5429 | + ext4_error_inode(inode, function, line, 0, | |
| 5430 | + "invalid orphan symlink nlink %d", | |
| 5431 | + inode->i_nlink); | |
| 5432 | + ret = -EFSCORRUPTED; | |
| 5433 | + goto bad_inode; | |
| 5434 | + } | |
| 5435 | + } else { | |
| 5436 | + if (inode->i_size == 0 || | |
| 5437 | + inode->i_size >= sizeof(ei->i_data) || | |
| 5438 | + strnlen((char *)ei->i_data, inode->i_size + 1) != | |
| 5439 | + inode->i_size) { | |
| 5440 | + ext4_error_inode(inode, function, line, 0, | |
| 5441 | + "invalid fast symlink length %llu", | |
| 5442 | + (unsigned long long)inode->i_size); | |
| 5443 | + ret = -EFSCORRUPTED; | |
| 5444 | + goto bad_inode; | |
| 5445 | + } | |
| 5446 | + inode_set_cached_link(inode, (char *)ei->i_data, | |
| 5447 | + inode->i_size); | |
| 5413 | 5448 | } |
| 5414 | − inode_set_cached_link(inode, (char *)ei->i_data, | |
| 5415 | − inode->i_size); | |
| 5416 | 5449 | } else { |
| 5417 | 5450 | inode->i_op = &ext4_symlink_inode_operations; |
| 5418 | 5451 | } |
| @@ -5849,6 +5882,18 @@ int ext4_setattr(struct mnt_idmap *idmap, struct dentry *dentry, · 430 unmodified lines | ||
| 5849 | 5882 | if (attr->ia_size == inode->i_size) |
| 5850 | 5883 | inc_ivers = false; |
| 5851 | 5884 | |
| 5885 | + /* | |
| 5886 | + * If file has inline data but new size exceeds inline capacity, | |
| 5887 | + * convert to extent-based storage first to prevent inconsistent | |
| 5888 | + * state (inline flag set but size exceeds inline capacity). | |
| 5889 | + */ | |
| 5890 | + if (ext4_has_inline_data(inode) && | |
| 5891 | + attr->ia_size > EXT4_I(inode)->i_inline_size) { | |
| 5892 | + error = ext4_convert_inline_data(inode); | |
| 5893 | + if (error) | |
| 5894 | + goto err_out; | |
| 5895 | + } | |
| 5896 | + | |
| 5852 | 5897 | if (shrink) { |
| 5853 | 5898 | if (ext4_should_order_data(inode)) { |
| 5854 | 5899 | error = ext4_begin_ordered_truncate(inode, |
| @@ -8,6 +8,7 @@ · 7 unmodified lines | ||
| 8 | 8 | #include <linux/random.h> |
| 9 | 9 | |
| 10 | 10 | #include "ext4.h" |
| 11 | +#include "mballoc.h" | |
| 11 | 12 | |
| 12 | 13 | struct mbt_grp_ctx { |
| 13 | 14 | struct buffer_head bitmap_bh; |
| @@ -336,7 +337,7 @@ ext4_mb_mark_context_stub(handle_t *handle, struct super_block *sb, bool state, · 322 unmodified lines | ||
| 336 | 337 | if (state) |
| 337 | 338 | mb_set_bits(bitmap_bh->b_data, blkoff, len); |
| 338 | 339 | else |
| 339 | − mb_clear_bits(bitmap_bh->b_data, blkoff, len); | |
| 340 | + mb_clear_bits_test(bitmap_bh->b_data, blkoff, len); | |
| 340 | 341 | |
| 341 | 342 | return 0; |
| 342 | 343 | } |
| @@ -413,14 +414,14 @@ static void test_new_blocks_simple(struct kunit *test) · 70 unmodified lines | ||
| 413 | 414 | |
| 414 | 415 | /* get block at goal */ |
| 415 | 416 | ar.goal = ext4_group_first_block_no(sb, goal_group); |
| 416 | − found = ext4_mb_new_blocks_simple(&ar, &err); | |
| 417 | + found = ext4_mb_new_blocks_simple_test(&ar, &err); | |
| 417 | 418 | KUNIT_ASSERT_EQ_MSG(test, ar.goal, found, |
| 418 | 419 | "failed to alloc block at goal, expected %llu found %llu", |
| 419 | 420 | ar.goal, found); |
| 420 | 421 | |
| 421 | 422 | /* get block after goal in goal group */ |
| 422 | 423 | ar.goal = ext4_group_first_block_no(sb, goal_group); |
| 423 | − found = ext4_mb_new_blocks_simple(&ar, &err); | |
| 424 | + found = ext4_mb_new_blocks_simple_test(&ar, &err); | |
| 424 | 425 | KUNIT_ASSERT_EQ_MSG(test, ar.goal + EXT4_C2B(sbi, 1), found, |
| 425 | 426 | "failed to alloc block after goal in goal group, expected %llu found %llu", |
| 426 | 427 | ar.goal + 1, found); |
| @@ -428,7 +429,7 @@ static void test_new_blocks_simple(struct kunit *test) · 1 unmodified line | ||
| 428 | 429 | /* get block after goal group */ |
| 429 | 430 | mbt_ctx_mark_used(sb, goal_group, 0, EXT4_CLUSTERS_PER_GROUP(sb)); |
| 430 | 431 | ar.goal = ext4_group_first_block_no(sb, goal_group); |
| 431 | − found = ext4_mb_new_blocks_simple(&ar, &err); | |
| 432 | + found = ext4_mb_new_blocks_simple_test(&ar, &err); | |
| 432 | 433 | KUNIT_ASSERT_EQ_MSG(test, |
| 433 | 434 | ext4_group_first_block_no(sb, goal_group + 1), found, |
| 434 | 435 | "failed to alloc block after goal group, expected %llu found %llu", |
| @@ -438,7 +439,7 @@ static void test_new_blocks_simple(struct kunit *test) · 3 unmodified lines | ||
| 438 | 439 | for (i = goal_group; i < ext4_get_groups_count(sb); i++) |
| 439 | 440 | mbt_ctx_mark_used(sb, i, 0, EXT4_CLUSTERS_PER_GROUP(sb)); |
| 440 | 441 | ar.goal = ext4_group_first_block_no(sb, goal_group); |
| 441 | − found = ext4_mb_new_blocks_simple(&ar, &err); | |
| 442 | + found = ext4_mb_new_blocks_simple_test(&ar, &err); | |
| 442 | 443 | KUNIT_ASSERT_EQ_MSG(test, |
| 443 | 444 | ext4_group_first_block_no(sb, 0) + EXT4_C2B(sbi, 1), found, |
| 444 | 445 | "failed to alloc block before goal group, expected %llu found %llu", |
| @@ -448,7 +449,7 @@ static void test_new_blocks_simple(struct kunit *test) · 3 unmodified lines | ||
| 448 | 449 | for (i = 0; i < ext4_get_groups_count(sb); i++) |
| 449 | 450 | mbt_ctx_mark_used(sb, i, 0, EXT4_CLUSTERS_PER_GROUP(sb)); |
| 450 | 451 | ar.goal = ext4_group_first_block_no(sb, goal_group); |
| 451 | − found = ext4_mb_new_blocks_simple(&ar, &err); | |
| 452 | + found = ext4_mb_new_blocks_simple_test(&ar, &err); | |
| 452 | 453 | KUNIT_ASSERT_NE_MSG(test, err, 0, |
| 453 | 454 | "unexpectedly get block when no block is available"); |
| 454 | 455 | } |
| @@ -492,16 +493,16 @@ validate_free_blocks_simple(struct kunit *test, struct super_block *sb, · 37 unmodified lines | ||
| 492 | 493 | continue; |
| 493 | 494 | |
| 494 | 495 | bitmap = mbt_ctx_bitmap(sb, i); |
| 495 | − bit = mb_find_next_zero_bit(bitmap, max, 0); | |
| 496 | + bit = mb_find_next_zero_bit_test(bitmap, max, 0); | |
| 496 | 497 | KUNIT_ASSERT_EQ_MSG(test, bit, max, |
| 497 | 498 | "free block on unexpected group %d", i); |
| 498 | 499 | } |
| 499 | 500 | |
| 500 | 501 | bitmap = mbt_ctx_bitmap(sb, goal_group); |
| 501 | − bit = mb_find_next_zero_bit(bitmap, max, 0); | |
| 502 | + bit = mb_find_next_zero_bit_test(bitmap, max, 0); | |
| 502 | 503 | KUNIT_ASSERT_EQ(test, bit, start); |
| 503 | 504 | |
| 504 | − bit = mb_find_next_bit(bitmap, max, bit + 1); | |
| 505 | + bit = mb_find_next_bit_test(bitmap, max, bit + 1); | |
| 505 | 506 | KUNIT_ASSERT_EQ(test, bit, start + len); |
| 506 | 507 | } |
| 507 | 508 | |
| @@ -524,7 +525,7 @@ test_free_blocks_simple_range(struct kunit *test, ext4_group_t goal_group, · 16 unmodified lines | ||
| 524 | 525 | |
| 525 | 526 | block = ext4_group_first_block_no(sb, goal_group) + |
| 526 | 527 | EXT4_C2B(sbi, start); |
| 527 | − ext4_free_blocks_simple(inode, block, len); | |
| 528 | + ext4_free_blocks_simple_test(inode, block, len); | |
| 528 | 529 | validate_free_blocks_simple(test, sb, goal_group, start, len); |
| 529 | 530 | mbt_ctx_mark_used(sb, goal_group, 0, EXT4_CLUSTERS_PER_GROUP(sb)); |
| 530 | 531 | } |
| @@ -566,15 +567,15 @@ test_mark_diskspace_used_range(struct kunit *test, · 35 unmodified lines | ||
| 566 | 567 | |
| 567 | 568 | bitmap = mbt_ctx_bitmap(sb, TEST_GOAL_GROUP); |
| 568 | 569 | memset(bitmap, 0, sb->s_blocksize); |
| 569 | − ret = ext4_mb_mark_diskspace_used(ac, NULL); | |
| 570 | + ret = ext4_mb_mark_diskspace_used_test(ac, NULL); | |
| 570 | 571 | KUNIT_ASSERT_EQ(test, ret, 0); |
| 571 | 572 | |
| 572 | 573 | max = EXT4_CLUSTERS_PER_GROUP(sb); |
| 573 | − i = mb_find_next_bit(bitmap, max, 0); | |
| 574 | + i = mb_find_next_bit_test(bitmap, max, 0); | |
| 574 | 575 | KUNIT_ASSERT_EQ(test, i, start); |
| 575 | − i = mb_find_next_zero_bit(bitmap, max, i + 1); | |
| 576 | + i = mb_find_next_zero_bit_test(bitmap, max, i + 1); | |
| 576 | 577 | KUNIT_ASSERT_EQ(test, i, start + len); |
| 577 | − i = mb_find_next_bit(bitmap, max, i + 1); | |
| 578 | + i = mb_find_next_bit_test(bitmap, max, i + 1); | |
| 578 | 579 | KUNIT_ASSERT_EQ(test, max, i); |
| 579 | 580 | } |
| 580 | 581 | |
| @@ -617,54 +618,54 @@ static void mbt_generate_buddy(struct super_block *sb, void *buddy, · 36 unmodified lines | ||
| 617 | 618 | max = EXT4_CLUSTERS_PER_GROUP(sb); |
| 618 | 619 | bb_h = buddy + sbi->s_mb_offsets[1]; |
| 619 | 620 | |
| 620 | − off = mb_find_next_zero_bit(bb, max, 0); | |
| 621 | + off = mb_find_next_zero_bit_test(bb, max, 0); | |
| 621 | 622 | grp->bb_first_free = off; |
| 622 | 623 | while (off < max) { |
| 623 | 624 | grp->bb_counters[0]++; |
| 624 | 625 | grp->bb_free++; |
| 625 | 626 | |
| 626 | − if (!(off & 1) && !mb_test_bit(off + 1, bb)) { | |
| 627 | + if (!(off & 1) && !mb_test_bit_test(off + 1, bb)) { | |
| 627 | 628 | grp->bb_free++; |
| 628 | 629 | grp->bb_counters[0]--; |
| 629 | − mb_clear_bit(off >> 1, bb_h); | |
| 630 | + mb_clear_bit_test(off >> 1, bb_h); | |
| 630 | 631 | grp->bb_counters[1]++; |
| 631 | 632 | grp->bb_largest_free_order = 1; |
| 632 | 633 | off++; |
| 633 | 634 | } |
| 634 | 635 | |
| 635 | − off = mb_find_next_zero_bit(bb, max, off + 1); | |
| 636 | + off = mb_find_next_zero_bit_test(bb, max, off + 1); | |
| 636 | 637 | } |
| 637 | 638 | |
| 638 | 639 | for (order = 1; order < MB_NUM_ORDERS(sb) - 1; order++) { |
| 639 | 640 | bb = buddy + sbi->s_mb_offsets[order]; |
| 640 | 641 | bb_h = buddy + sbi->s_mb_offsets[order + 1]; |
| 641 | 642 | max = max >> 1; |
| 642 | − off = mb_find_next_zero_bit(bb, max, 0); | |
| 643 | + off = mb_find_next_zero_bit_test(bb, max, 0); | |
| 643 | 644 | |
| 644 | 645 | while (off < max) { |
| 645 | − if (!(off & 1) && !mb_test_bit(off + 1, bb)) { | |
| 646 | + if (!(off & 1) && !mb_test_bit_test(off + 1, bb)) { | |
| 646 | 647 | mb_set_bits(bb, off, 2); |
| 647 | 648 | grp->bb_counters[order] -= 2; |
| 648 | − mb_clear_bit(off >> 1, bb_h); | |
| 649 | + mb_clear_bit_test(off >> 1, bb_h); | |
| 649 | 650 | grp->bb_counters[order + 1]++; |
| 650 | 651 | grp->bb_largest_free_order = order + 1; |
| 651 | 652 | off++; |
| 652 | 653 | } |
| 653 | 654 | |
| 654 | − off = mb_find_next_zero_bit(bb, max, off + 1); | |
| 655 | + off = mb_find_next_zero_bit_test(bb, max, off + 1); | |
| 655 | 656 | } |
| 656 | 657 | } |
| 657 | 658 | |
| 658 | 659 | max = EXT4_CLUSTERS_PER_GROUP(sb); |
| 659 | − off = mb_find_next_zero_bit(bitmap, max, 0); | |
| 660 | + off = mb_find_next_zero_bit_test(bitmap, max, 0); | |
| 660 | 661 | while (off < max) { |
| 661 | 662 | grp->bb_fragments++; |
| 662 | 663 | |
| 663 | − off = mb_find_next_bit(bitmap, max, off + 1); | |
| 664 | + off = mb_find_next_bit_test(bitmap, max, off + 1); | |
| 664 | 665 | if (off + 1 >= max) |
| 665 | 666 | break; |
| 666 | 667 | |
| 667 | − off = mb_find_next_zero_bit(bitmap, max, off + 1); | |
| 668 | + off = mb_find_next_zero_bit_test(bitmap, max, off + 1); | |
| 668 | 669 | } |
| 669 | 670 | } |
| 670 | 671 | |
| @@ -706,7 +707,7 @@ do_test_generate_buddy(struct kunit *test, struct super_block *sb, void *bitmap, · 35 unmodified lines | ||
| 706 | 707 | /* needed by validation in ext4_mb_generate_buddy */ |
| 707 | 708 | ext4_grp->bb_free = mbt_grp->bb_free; |
| 708 | 709 | memset(ext4_buddy, 0xff, sb->s_blocksize); |
| 709 | − ext4_mb_generate_buddy(sb, ext4_buddy, bitmap, TEST_GOAL_GROUP, | |
| 710 | + ext4_mb_generate_buddy_test(sb, ext4_buddy, bitmap, TEST_GOAL_GROUP, | |
| 710 | 711 | ext4_grp); |
| 711 | 712 | |
| 712 | 713 | KUNIT_ASSERT_EQ(test, memcmp(mbt_buddy, ext4_buddy, sb->s_blocksize), |
| @@ -760,7 +761,7 @@ test_mb_mark_used_range(struct kunit *test, struct ext4_buddy *e4b, · 47 unmodified lines | ||
| 760 | 761 | ex.fe_group = TEST_GOAL_GROUP; |
| 761 | 762 | |
| 762 | 763 | ext4_lock_group(sb, TEST_GOAL_GROUP); |
| 763 | − mb_mark_used(e4b, &ex); | |
| 764 | + mb_mark_used_test(e4b, &ex); | |
| 764 | 765 | ext4_unlock_group(sb, TEST_GOAL_GROUP); |
| 765 | 766 | |
| 766 | 767 | mb_set_bits(bitmap, start, len); |
| @@ -769,7 +770,7 @@ test_mb_mark_used_range(struct kunit *test, struct ext4_buddy *e4b, · 2 unmodified lines | ||
| 769 | 770 | memset(buddy, 0xff, sb->s_blocksize); |
| 770 | 771 | for (i = 0; i < MB_NUM_ORDERS(sb); i++) |
| 771 | 772 | grp->bb_counters[i] = 0; |
| 772 | − ext4_mb_generate_buddy(sb, buddy, bitmap, 0, grp); | |
| 773 | + ext4_mb_generate_buddy_test(sb, buddy, bitmap, 0, grp); | |
| 773 | 774 | |
| 774 | 775 | KUNIT_ASSERT_EQ(test, memcmp(buddy, e4b->bd_buddy, sb->s_blocksize), |
| 775 | 776 | 0); |
| @@ -798,7 +799,7 @@ static void test_mb_mark_used(struct kunit *test) · 22 unmodified lines | ||
| 798 | 799 | bb_counters[MB_NUM_ORDERS(sb)]), GFP_KERNEL); |
| 799 | 800 | KUNIT_ASSERT_NOT_ERR_OR_NULL(test, grp); |
| 800 | 801 | |
| 801 | − ret = ext4_mb_load_buddy(sb, TEST_GOAL_GROUP, &e4b); | |
| 802 | + ret = ext4_mb_load_buddy_test(sb, TEST_GOAL_GROUP, &e4b); | |
| 802 | 803 | KUNIT_ASSERT_EQ(test, ret, 0); |
| 803 | 804 | |
| 804 | 805 | grp->bb_free = EXT4_CLUSTERS_PER_GROUP(sb); |
| @@ -809,7 +810,7 @@ static void test_mb_mark_used(struct kunit *test) · 4 unmodified lines | ||
| 809 | 810 | test_mb_mark_used_range(test, &e4b, ranges[i].start, |
| 810 | 811 | ranges[i].len, bitmap, buddy, grp); |
| 811 | 812 | |
| 812 | − ext4_mb_unload_buddy(&e4b); | |
| 813 | + ext4_mb_unload_buddy_test(&e4b); | |
| 813 | 814 | } |
| 814 | 815 | |
| 815 | 816 | static void |
| @@ -825,16 +826,16 @@ test_mb_free_blocks_range(struct kunit *test, struct ext4_buddy *e4b, · 9 unmodified lines | ||
| 825 | 826 | return; |
| 826 | 827 | |
| 827 | 828 | ext4_lock_group(sb, e4b->bd_group); |
| 828 | − mb_free_blocks(NULL, e4b, start, len); | |
| 829 | + mb_free_blocks_test(NULL, e4b, start, len); | |
| 829 | 830 | ext4_unlock_group(sb, e4b->bd_group); |
| 830 | 831 | |
| 831 | − mb_clear_bits(bitmap, start, len); | |
| 832 | + mb_clear_bits_test(bitmap, start, len); | |
| 832 | 833 | /* bypass bb_free validatoin in ext4_mb_generate_buddy */ |
| 833 | 834 | grp->bb_free += len; |
| 834 | 835 | memset(buddy, 0xff, sb->s_blocksize); |
| 835 | 836 | for (i = 0; i < MB_NUM_ORDERS(sb); i++) |
| 836 | 837 | grp->bb_counters[i] = 0; |
| 837 | − ext4_mb_generate_buddy(sb, buddy, bitmap, 0, grp); | |
| 838 | + ext4_mb_generate_buddy_test(sb, buddy, bitmap, 0, grp); | |
| 838 | 839 | |
| 839 | 840 | KUNIT_ASSERT_EQ(test, memcmp(buddy, e4b->bd_buddy, sb->s_blocksize), |
| 840 | 841 | 0); |
| @@ -865,7 +866,7 @@ static void test_mb_free_blocks(struct kunit *test) · 24 unmodified lines | ||
| 865 | 866 | bb_counters[MB_NUM_ORDERS(sb)]), GFP_KERNEL); |
| 866 | 867 | KUNIT_ASSERT_NOT_ERR_OR_NULL(test, grp); |
| 867 | 868 | |
| 868 | − ret = ext4_mb_load_buddy(sb, TEST_GOAL_GROUP, &e4b); | |
| 869 | + ret = ext4_mb_load_buddy_test(sb, TEST_GOAL_GROUP, &e4b); | |
| 869 | 870 | KUNIT_ASSERT_EQ(test, ret, 0); |
| 870 | 871 | |
| 871 | 872 | ex.fe_start = 0; |
| @@ -873,7 +874,7 @@ static void test_mb_free_blocks(struct kunit *test) · 1 unmodified line | ||
| 873 | 874 | ex.fe_group = TEST_GOAL_GROUP; |
| 874 | 875 | |
| 875 | 876 | ext4_lock_group(sb, TEST_GOAL_GROUP); |
| 876 | − mb_mark_used(&e4b, &ex); | |
| 877 | + mb_mark_used_test(&e4b, &ex); | |
| 877 | 878 | ext4_unlock_group(sb, TEST_GOAL_GROUP); |
| 878 | 879 | |
| 879 | 880 | grp->bb_free = 0; |
| @@ -886,7 +887,7 @@ static void test_mb_free_blocks(struct kunit *test) · 6 unmodified lines | ||
| 886 | 887 | test_mb_free_blocks_range(test, &e4b, ranges[i].start, |
| 887 | 888 | ranges[i].len, bitmap, buddy, grp); |
| 888 | 889 | |
| 889 | − ext4_mb_unload_buddy(&e4b); | |
| 890 | + ext4_mb_unload_buddy_test(&e4b); | |
| 890 | 891 | } |
| 891 | 892 | |
| 892 | 893 | #define COUNT_FOR_ESTIMATE 100000 |
| @@ -904,7 +905,7 @@ static void test_mb_mark_used_cost(struct kunit *test) · 11 unmodified lines | ||
| 904 | 905 | if (sb->s_blocksize > PAGE_SIZE) |
| 905 | 906 | kunit_skip(test, "blocksize exceeds pagesize"); |
| 906 | 907 | |
| 907 | − ret = ext4_mb_load_buddy(sb, TEST_GOAL_GROUP, &e4b); | |
| 908 | + ret = ext4_mb_load_buddy_test(sb, TEST_GOAL_GROUP, &e4b); | |
| 908 | 909 | KUNIT_ASSERT_EQ(test, ret, 0); |
| 909 | 910 | |
| 910 | 911 | ex.fe_group = TEST_GOAL_GROUP; |
| @@ -918,7 +919,7 @@ static void test_mb_mark_used_cost(struct kunit *test) · 7 unmodified lines | ||
| 918 | 919 | ex.fe_start = ranges[i].start; |
| 919 | 920 | ex.fe_len = ranges[i].len; |
| 920 | 921 | ext4_lock_group(sb, TEST_GOAL_GROUP); |
| 921 | − mb_mark_used(&e4b, &ex); | |
| 922 | + mb_mark_used_test(&e4b, &ex); | |
| 922 | 923 | ext4_unlock_group(sb, TEST_GOAL_GROUP); |
| 923 | 924 | } |
| 924 | 925 | end = jiffies; |
| @@ -929,14 +930,14 @@ static void test_mb_mark_used_cost(struct kunit *test) · 4 unmodified lines | ||
| 929 | 930 | continue; |
| 930 | 931 | |
| 931 | 932 | ext4_lock_group(sb, TEST_GOAL_GROUP); |
| 932 | − mb_free_blocks(NULL, &e4b, ranges[i].start, | |
| 933 | + mb_free_blocks_test(NULL, &e4b, ranges[i].start, | |
| 933 | 934 | ranges[i].len); |
| 934 | 935 | ext4_unlock_group(sb, TEST_GOAL_GROUP); |
| 935 | 936 | } |
| 936 | 937 | } |
| 937 | 938 | |
| 938 | 939 | kunit_info(test, "costed jiffies %lu\n", all); |
| 939 | − ext4_mb_unload_buddy(&e4b); | |
| 940 | + ext4_mb_unload_buddy_test(&e4b); | |
| 940 | 941 | } |
| 941 | 942 | |
| 942 | 943 | static const struct mbt_ext4_block_layout mbt_test_layouts[] = { |
| @@ -1199,6 +1199,8 @@ static int ext4_mb_scan_groups(struct ext4_allocation_context *ac) · 1198 unmodified lines | ||
| 1199 | 1199 | |
| 1200 | 1200 | /* searching for the right group start from the goal value specified */ |
| 1201 | 1201 | start = ac->ac_g_ex.fe_group; |
| 1202 | + if (start >= ngroups) | |
| 1203 | + start = 0; | |
| 1202 | 1204 | ac->ac_prefetch_grp = start; |
| 1203 | 1205 | ac->ac_prefetch_nr = 0; |
| 1204 | 1206 | |
| @@ -2443,8 +2445,12 @@ int ext4_mb_find_by_goal(struct ext4_allocation_context *ac, · 1238 unmodified lines | ||
| 2443 | 2445 | return 0; |
| 2444 | 2446 | |
| 2445 | 2447 | err = ext4_mb_load_buddy(ac->ac_sb, group, e4b); |
| 2446 | − if (err) | |
| 2448 | + if (err) { | |
| 2449 | + if (EXT4_MB_GRP_BBITMAP_CORRUPT(e4b->bd_info) && | |
| 2450 | + !(ac->ac_flags & EXT4_MB_HINT_GOAL_ONLY)) | |
| 2451 | + return 0; | |
| 2447 | 2452 | return err; |
| 2453 | + } | |
| 2448 | 2454 | |
| 2449 | 2455 | ext4_lock_group(ac->ac_sb, group); |
| 2450 | 2456 | if (unlikely(EXT4_MB_GRP_BBITMAP_CORRUPT(e4b->bd_info))) |
| @@ -3580,9 +3586,7 @@ err_freebuddy: · 1129 unmodified lines | ||
| 3580 | 3586 | rcu_read_unlock(); |
| 3581 | 3587 | iput(sbi->s_buddy_cache); |
| 3582 | 3588 | err_freesgi: |
| 3583 | − rcu_read_lock(); | |
| 3584 | − kvfree(rcu_dereference(sbi->s_group_info)); | |
| 3585 | − rcu_read_unlock(); | |
| 3589 | + kvfree(rcu_access_pointer(sbi->s_group_info)); | |
| 3586 | 3590 | return -ENOMEM; |
| 3587 | 3591 | } |
| 3588 | 3592 | |
| @@ -3889,15 +3893,14 @@ void ext4_mb_release(struct super_block *sb) · 300 unmodified lines | ||
| 3889 | 3893 | struct kmem_cache *cachep = get_groupinfo_cache(sb->s_blocksize_bits); |
| 3890 | 3894 | int count; |
| 3891 | 3895 | |
| 3892 | − if (test_opt(sb, DISCARD)) { | |
| 3893 | − /* | |
| 3894 | − * wait the discard work to drain all of ext4_free_data | |
| 3895 | − */ | |
| 3896 | − flush_work(&sbi->s_discard_work); | |
| 3897 | − WARN_ON_ONCE(!list_empty(&sbi->s_discard_list)); | |
| 3898 | − } | |
| 3896 | + /* | |
| 3897 | + * wait the discard work to drain all of ext4_free_data | |
| 3898 | + */ | |
| 3899 | + flush_work(&sbi->s_discard_work); | |
| 3900 | + WARN_ON_ONCE(!list_empty(&sbi->s_discard_list)); | |
| 3899 | 3901 | |
| 3900 | − if (sbi->s_group_info) { | |
| 3902 | + group_info = rcu_access_pointer(sbi->s_group_info); | |
| 3903 | + if (group_info) { | |
| 3901 | 3904 | for (i = 0; i < ngroups; i++) { |
| 3902 | 3905 | cond_resched(); |
| 3903 | 3906 | grinfo = ext4_get_group_info(sb, i); |
| @@ -3915,12 +3918,9 @@ void ext4_mb_release(struct super_block *sb) · 11 unmodified lines | ||
| 3915 | 3918 | num_meta_group_infos = (ngroups + |
| 3916 | 3919 | EXT4_DESC_PER_BLOCK(sb) - 1) >> |
| 3917 | 3920 | EXT4_DESC_PER_BLOCK_BITS(sb); |
| 3918 | − rcu_read_lock(); | |
| 3919 | − group_info = rcu_dereference(sbi->s_group_info); | |
| 3920 | 3921 | for (i = 0; i < num_meta_group_infos; i++) |
| 3921 | 3922 | kfree(group_info[i]); |
| 3922 | 3923 | kvfree(group_info); |
| 3923 | − rcu_read_unlock(); | |
| 3924 | 3924 | } |
| 3925 | 3925 | ext4_mb_avg_fragment_size_destroy(sbi); |
| 3926 | 3926 | ext4_mb_largest_free_orders_destroy(sbi); |
| @@ -4084,7 +4084,7 @@ void ext4_exit_mballoc(void) · 157 unmodified lines | ||
| 4084 | 4084 | |
| 4085 | 4085 | #define EXT4_MB_BITMAP_MARKED_CHECK 0x0001 |
| 4086 | 4086 | #define EXT4_MB_SYNC_UPDATE 0x0002 |
| 4087 | −static int | |
| 4087 | +int | |
| 4088 | 4088 | ext4_mb_mark_context(handle_t *handle, struct super_block *sb, bool state, |
| 4089 | 4089 | ext4_group_t group, ext4_grpblk_t blkoff, |
| 4090 | 4090 | ext4_grpblk_t len, int flags, ext4_grpblk_t *ret_changed) |
| @@ -7188,6 +7188,102 @@ out_unload: · 3097 unmodified lines | ||
| 7188 | 7188 | return error; |
| 7189 | 7189 | } |
| 7190 | 7190 | |
| 7191 | −#ifdef CONFIG_EXT4_KUNIT_TESTS | |
| 7192 | −#include "mballoc-test.c" | |
| 7191 | +#if IS_ENABLED(CONFIG_EXT4_KUNIT_TESTS) | |
| 7192 | +void mb_clear_bits_test(void *bm, int cur, int len) | |
| 7193 | +{ | |
| 7194 | + mb_clear_bits(bm, cur, len); | |
| 7195 | +} | |
| 7196 | +EXPORT_SYMBOL_FOR_EXT4_TEST(mb_clear_bits_test); | |
| 7197 | + | |
| 7198 | +ext4_fsblk_t | |
| 7199 | +ext4_mb_new_blocks_simple_test(struct ext4_allocation_request *ar, | |
| 7200 | + int *errp) | |
| 7201 | +{ | |
| 7202 | + return ext4_mb_new_blocks_simple(ar, errp); | |
| 7203 | +} | |
| 7204 | +EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_mb_new_blocks_simple_test); | |
| 7205 | + | |
| 7206 | +int mb_find_next_zero_bit_test(void *addr, int max, int start) | |
| 7207 | +{ | |
| 7208 | + return mb_find_next_zero_bit(addr, max, start); | |
| 7209 | +} | |
| 7210 | +EXPORT_SYMBOL_FOR_EXT4_TEST(mb_find_next_zero_bit_test); | |
| 7211 | + | |
| 7212 | +int mb_find_next_bit_test(void *addr, int max, int start) | |
| 7213 | +{ | |
| 7214 | + return mb_find_next_bit(addr, max, start); | |
| 7215 | +} | |
| 7216 | +EXPORT_SYMBOL_FOR_EXT4_TEST(mb_find_next_bit_test); | |
| 7217 | + | |
| 7218 | +void mb_clear_bit_test(int bit, void *addr) | |
| 7219 | +{ | |
| 7220 | + mb_clear_bit(bit, addr); | |
| 7221 | +} | |
| 7222 | +EXPORT_SYMBOL_FOR_EXT4_TEST(mb_clear_bit_test); | |
| 7223 | + | |
| 7224 | +int mb_test_bit_test(int bit, void *addr) | |
| 7225 | +{ | |
| 7226 | + return mb_test_bit(bit, addr); | |
| 7227 | +} | |
| 7228 | +EXPORT_SYMBOL_FOR_EXT4_TEST(mb_test_bit_test); | |
| 7229 | + | |
| 7230 | +int ext4_mb_mark_diskspace_used_test(struct ext4_allocation_context *ac, | |
| 7231 | + handle_t *handle) | |
| 7232 | +{ | |
| 7233 | + return ext4_mb_mark_diskspace_used(ac, handle); | |
| 7234 | +} | |
| 7235 | +EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_mb_mark_diskspace_used_test); | |
| 7236 | + | |
| 7237 | +int mb_mark_used_test(struct ext4_buddy *e4b, struct ext4_free_extent *ex) | |
| 7238 | +{ | |
| 7239 | + return mb_mark_used(e4b, ex); | |
| 7240 | +} | |
| 7241 | +EXPORT_SYMBOL_FOR_EXT4_TEST(mb_mark_used_test); | |
| 7242 | + | |
| 7243 | +void ext4_mb_generate_buddy_test(struct super_block *sb, void *buddy, | |
| 7244 | + void *bitmap, ext4_group_t group, | |
| 7245 | + struct ext4_group_info *grp) | |
| 7246 | +{ | |
| 7247 | + ext4_mb_generate_buddy(sb, buddy, bitmap, group, grp); | |
| 7248 | +} | |
| 7249 | +EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_mb_generate_buddy_test); | |
| 7250 | + | |
| 7251 | +int ext4_mb_load_buddy_test(struct super_block *sb, ext4_group_t group, | |
| 7252 | + struct ext4_buddy *e4b) | |
| 7253 | +{ | |
| 7254 | + return ext4_mb_load_buddy(sb, group, e4b); | |
| 7255 | +} | |
| 7256 | +EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_mb_load_buddy_test); | |
| 7257 | + | |
| 7258 | +void ext4_mb_unload_buddy_test(struct ext4_buddy *e4b) | |
| 7259 | +{ | |
| 7260 | + ext4_mb_unload_buddy(e4b); | |
| 7261 | +} | |
| 7262 | +EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_mb_unload_buddy_test); | |
| 7263 | + | |
| 7264 | +void mb_free_blocks_test(struct inode *inode, struct ext4_buddy *e4b, | |
| 7265 | + int first, int count) | |
| 7266 | +{ | |
| 7267 | + mb_free_blocks(inode, e4b, first, count); | |
| 7268 | +} | |
| 7269 | +EXPORT_SYMBOL_FOR_EXT4_TEST(mb_free_blocks_test); | |
| 7270 | + | |
| 7271 | +void ext4_free_blocks_simple_test(struct inode *inode, ext4_fsblk_t block, | |
| 7272 | + unsigned long count) | |
| 7273 | +{ | |
| 7274 | + return ext4_free_blocks_simple(inode, block, count); | |
| 7275 | +} | |
| 7276 | +EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_free_blocks_simple_test); | |
| 7277 | + | |
| 7278 | +EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_wait_block_bitmap); | |
| 7279 | +EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_mb_init); | |
| 7280 | +EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_get_group_desc); | |
| 7281 | +EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_count_free_clusters); | |
| 7282 | +EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_get_group_info); | |
| 7283 | +EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_free_group_clusters_set); | |
| 7284 | +EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_mb_release); | |
| 7285 | +EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_read_block_bitmap_nowait); | |
| 7286 | +EXPORT_SYMBOL_FOR_EXT4_TEST(mb_set_bits); | |
| 7287 | +EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_fc_init_inode); | |
| 7288 | +EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_mb_mark_context); | |
| 7193 | 7289 | #endif |
| @@ -270,4 +270,34 @@ ext4_mballoc_query_range( · 269 unmodified lines | ||
| 270 | 270 | ext4_mballoc_query_range_fn formatter, |
| 271 | 271 | void *priv); |
| 272 | 272 | |
| 273 | +extern int ext4_mb_mark_context(handle_t *handle, | |
| 274 | + struct super_block *sb, bool state, | |
| 275 | + ext4_group_t group, ext4_grpblk_t blkoff, | |
| 276 | + ext4_grpblk_t len, int flags, | |
| 277 | + ext4_grpblk_t *ret_changed); | |
| 278 | +#if IS_ENABLED(CONFIG_EXT4_KUNIT_TESTS) | |
| 279 | +extern void mb_clear_bits_test(void *bm, int cur, int len); | |
| 280 | +extern ext4_fsblk_t | |
| 281 | +ext4_mb_new_blocks_simple_test(struct ext4_allocation_request *ar, | |
| 282 | + int *errp); | |
| 283 | +extern int mb_find_next_zero_bit_test(void *addr, int max, int start); | |
| 284 | +extern int mb_find_next_bit_test(void *addr, int max, int start); | |
| 285 | +extern void mb_clear_bit_test(int bit, void *addr); | |
| 286 | +extern int mb_test_bit_test(int bit, void *addr); | |
| 287 | +extern int | |
| 288 | +ext4_mb_mark_diskspace_used_test(struct ext4_allocation_context *ac, | |
| 289 | + handle_t *handle); | |
| 290 | +extern int mb_mark_used_test(struct ext4_buddy *e4b, | |
| 291 | + struct ext4_free_extent *ex); | |
| 292 | +extern void ext4_mb_generate_buddy_test(struct super_block *sb, | |
| 293 | + void *buddy, void *bitmap, ext4_group_t group, | |
| 294 | + struct ext4_group_info *grp); | |
| 295 | +extern int ext4_mb_load_buddy_test(struct super_block *sb, | |
| 296 | + ext4_group_t group, struct ext4_buddy *e4b); | |
| 297 | +extern void ext4_mb_unload_buddy_test(struct ext4_buddy *e4b); | |
| 298 | +extern void mb_free_blocks_test(struct inode *inode, | |
| 299 | + struct ext4_buddy *e4b, int first, int count); | |
| 300 | +extern void ext4_free_blocks_simple_test(struct inode *inode, | |
| 301 | + ext4_fsblk_t block, unsigned long count); | |
| 302 | +#endif | |
| 273 | 303 | #endif |
| @@ -524,9 +524,15 @@ int ext4_bio_write_folio(struct ext4_io_submit *io, struct folio *folio, · 523 unmodified lines | ||
| 524 | 524 | nr_to_submit++; |
| 525 | 525 | } while ((bh = bh->b_this_page) != head); |
| 526 | 526 | |
| 527 | − /* Nothing to submit? Just unlock the folio... */ | |
| 528 | − if (!nr_to_submit) | |
| 527 | + if (!nr_to_submit) { | |
| 528 | + /* | |
| 529 | + * We have nothing to submit. Just cycle the folio through | |
| 530 | + * writeback state to properly update xarray tags. | |
| 531 | + */ | |
| 532 | + __folio_start_writeback(folio, keep_towrite); | |
| 533 | + folio_end_writeback(folio); | |
| 529 | 534 | return 0; |
| 535 | + } | |
| 530 | 536 | |
| 531 | 537 | bh = head = folio_buffers(folio); |
| 532 | 538 | |
| @@ -1254,12 +1254,10 @@ static void ext4_group_desc_free(struct ext4_sb_info *sbi) · 1253 unmodified lines | ||
| 1254 | 1254 | struct buffer_head **group_desc; |
| 1255 | 1255 | int i; |
| 1256 | 1256 | |
| 1257 | − rcu_read_lock(); | |
| 1258 | − group_desc = rcu_dereference(sbi->s_group_desc); | |
| 1257 | + group_desc = rcu_access_pointer(sbi->s_group_desc); | |
| 1259 | 1258 | for (i = 0; i < sbi->s_gdb_count; i++) |
| 1260 | 1259 | brelse(group_desc[i]); |
| 1261 | 1260 | kvfree(group_desc); |
| 1262 | − rcu_read_unlock(); | |
| 1263 | 1261 | } |
| 1264 | 1262 | |
| 1265 | 1263 | static void ext4_flex_groups_free(struct ext4_sb_info *sbi) |
| @@ -1267,14 +1265,12 @@ static void ext4_flex_groups_free(struct ext4_sb_info *sbi) · 1 unmodified line | ||
| 1267 | 1265 | struct flex_groups **flex_groups; |
| 1268 | 1266 | int i; |
| 1269 | 1267 | |
| 1270 | − rcu_read_lock(); | |
| 1271 | − flex_groups = rcu_dereference(sbi->s_flex_groups); | |
| 1268 | + flex_groups = rcu_access_pointer(sbi->s_flex_groups); | |
| 1272 | 1269 | if (flex_groups) { |
| 1273 | 1270 | for (i = 0; i < sbi->s_flex_groups_allocated; i++) |
| 1274 | 1271 | kvfree(flex_groups[i]); |
| 1275 | 1272 | kvfree(flex_groups); |
| 1276 | 1273 | } |
| 1277 | − rcu_read_unlock(); | |
| 1278 | 1274 | } |
| 1279 | 1275 | |
| 1280 | 1276 | static void ext4_put_super(struct super_block *sb) |
| @@ -1527,6 +1523,27 @@ void ext4_clear_inode(struct inode *inode) · 246 unmodified lines | ||
| 1527 | 1523 | invalidate_inode_buffers(inode); |
| 1528 | 1524 | clear_inode(inode); |
| 1529 | 1525 | ext4_discard_preallocations(inode); |
| 1526 | + /* | |
| 1527 | + * We must remove the inode from the hash before ext4_free_inode() | |
| 1528 | + * clears the bit in inode bitmap as otherwise another process reusing | |
| 1529 | + * the inode will block in insert_inode_hash() waiting for inode | |
| 1530 | + * eviction to complete while holding transaction handle open, but | |
| 1531 | + * ext4_evict_inode() still running for that inode could block waiting | |
| 1532 | + * for transaction commit if the inode is marked as IS_SYNC => deadlock. | |
| 1533 | + * | |
| 1534 | + * Removing the inode from the hash here is safe. There are two cases | |
| 1535 | + * to consider: | |
| 1536 | + * 1) The inode still has references to it (i_nlink > 0). In that case | |
| 1537 | + * we are keeping the inode and once we remove the inode from the hash, | |
| 1538 | + * iget() can create the new inode structure for the same inode number | |
| 1539 | + * and we are fine with that as all IO on behalf of the inode is | |
| 1540 | + * finished. | |
| 1541 | + * 2) We are deleting the inode (i_nlink == 0). In that case inode | |
| 1542 | + * number cannot be reused until ext4_free_inode() clears the bit in | |
| 1543 | + * the inode bitmap, at which point all IO is done and reuse is fine | |
| 1544 | + * again. | |
| 1545 | + */ | |
| 1546 | + remove_inode_hash(inode); | |
| 1530 | 1547 | ext4_es_remove_extent(inode, 0, EXT_MAX_BLOCKS); |
| 1531 | 1548 | dquot_drop(inode); |
| 1532 | 1549 | if (EXT4_I(inode)->jinode) { |
| @@ -3633,6 +3650,13 @@ int ext4_feature_set_ok(struct super_block *sb, int readonly) · 2100 unmodified lines | ||
| 3633 | 3650 | "extents feature\n"); |
| 3634 | 3651 | return 0; |
| 3635 | 3652 | } |
| 3653 | + if (ext4_has_feature_bigalloc(sb) && | |
| 3654 | + le32_to_cpu(EXT4_SB(sb)->s_es->s_first_data_block)) { | |
| 3655 | + ext4_msg(sb, KERN_WARNING, | |
| 3656 | + "bad geometry: bigalloc file system with non-zero " | |
| 3657 | + "first_data_block\n"); | |
| 3658 | + return 0; | |
| 3659 | + } | |
| 3636 | 3660 | |
| 3637 | 3661 | #if !IS_ENABLED(CONFIG_QUOTA) || !IS_ENABLED(CONFIG_QFMT_V2) |
| 3638 | 3662 | if (!readonly && (ext4_has_feature_quota(sb) || |
| @@ -5403,6 +5427,7 @@ static int __ext4_fill_super(struct fs_context *fc, struct super_block *sb) · 1764 unmodified lines | ||
| 5403 | 5427 | |
| 5404 | 5428 | timer_setup(&sbi->s_err_report, print_daily_error_info, 0); |
| 5405 | 5429 | spin_lock_init(&sbi->s_error_lock); |
| 5430 | + mutex_init(&sbi->s_error_notify_mutex); | |
| 5406 | 5431 | INIT_WORK(&sbi->s_sb_upd_work, update_super_work); |
| 5407 | 5432 | |
| 5408 | 5433 | err = ext4_group_desc_init(sb, es, logical_sb_block, &first_not_zeroed); |
| @@ -597,7 +597,10 @@ static const struct kobj_type ext4_feat_ktype = { · 596 unmodified lines | ||
| 597 | 597 | |
| 598 | 598 | void ext4_notify_error_sysfs(struct ext4_sb_info *sbi) |
| 599 | 599 | { |
| 600 | − sysfs_notify(&sbi->s_kobj, NULL, "errors_count"); | |
| 600 | + mutex_lock(&sbi->s_error_notify_mutex); | |
| 601 | + if (sbi->s_kobj.state_in_sysfs) | |
| 602 | + sysfs_notify(&sbi->s_kobj, NULL, "errors_count"); | |
| 603 | + mutex_unlock(&sbi->s_error_notify_mutex); | |
| 601 | 604 | } |
| 602 | 605 | |
| 603 | 606 | static struct kobject *ext4_root; |
| @@ -610,8 +613,10 @@ int ext4_register_sysfs(struct super_block *sb) · 6 unmodified lines | ||
| 610 | 613 | int err; |
| 611 | 614 | |
| 612 | 615 | init_completion(&sbi->s_kobj_unregister); |
| 616 | + mutex_lock(&sbi->s_error_notify_mutex); | |
| 613 | 617 | err = kobject_init_and_add(&sbi->s_kobj, &ext4_sb_ktype, ext4_root, |
| 614 | 618 | "%s", sb->s_id); |
| 619 | + mutex_unlock(&sbi->s_error_notify_mutex); | |
| 615 | 620 | if (err) { |
| 616 | 621 | kobject_put(&sbi->s_kobj); |
| 617 | 622 | wait_for_completion(&sbi->s_kobj_unregister); |
| @@ -644,7 +649,10 @@ void ext4_unregister_sysfs(struct super_block *sb) · 26 unmodified lines | ||
| 644 | 649 | |
| 645 | 650 | if (sbi->s_proc) |
| 646 | 651 | remove_proc_subtree(sb->s_id, ext4_proc_root); |
| 652 | + | |
| 653 | + mutex_lock(&sbi->s_error_notify_mutex); | |
| 647 | 654 | kobject_del(&sbi->s_kobj); |
| 655 | + mutex_unlock(&sbi->s_error_notify_mutex); | |
| 648 | 656 | } |
| 649 | 657 | |
| 650 | 658 | int __init ext4_init_sysfs(void) |
| @@ -267,7 +267,15 @@ restart: · 266 unmodified lines | ||
| 267 | 267 | */ |
| 268 | 268 | BUFFER_TRACE(bh, "queue"); |
| 269 | 269 | get_bh(bh); |
| 270 | − J_ASSERT_BH(bh, !buffer_jwrite(bh)); | |
| 270 | + if (WARN_ON_ONCE(buffer_jwrite(bh))) { | |
| 271 | + put_bh(bh); /* drop the ref we just took */ | |
| 272 | + spin_unlock(&journal->j_list_lock); | |
| 273 | + /* Clean up any previously batched buffers */ | |
| 274 | + if (batch_count) | |
| 275 | + __flush_batch(journal, &batch_count); | |
| 276 | + jbd2_journal_abort(journal, -EFSCORRUPTED); | |
| 277 | + return -EFSCORRUPTED; | |
| 278 | + } | |
| 271 | 279 | journal->j_chkpt_bhs[batch_count++] = bh; |
| 272 | 280 | transaction->t_chp_stats.cs_written++; |
| 273 | 281 | transaction->t_checkpoint_list = jh->b_cpnext; |
| @@ -325,7 +333,10 @@ int jbd2_cleanup_journal_tail(journal_t *journal) · 51 unmodified lines | ||
| 325 | 333 | |
| 326 | 334 | if (!jbd2_journal_get_log_tail(journal, &first_tid, &blocknr)) |
| 327 | 335 | return 1; |
| 328 | − J_ASSERT(blocknr != 0); | |
| 336 | + if (WARN_ON_ONCE(blocknr == 0)) { | |
| 337 | + jbd2_journal_abort(journal, -EFSCORRUPTED); | |
| 338 | + return -EFSCORRUPTED; | |
| 339 | + } | |
| 329 | 340 | |
| 330 | 341 | /* |
| 331 | 342 | * We need to make sure that any blocks that were recently written out |