Add CONFIG_EXT4_FS_BIGALLOC, enabled by default. Disabling it omits pending cluster reservation tracking and saves 8 bytes per inode. Reject bigalloc filesystems before journal recovery, including on read-only mounts. Signed-off-by: Max Kellermann --- Documentation/filesystems/ext4/bigalloc.rst | 6 +++ fs/ext4/Kconfig | 15 +++++++ fs/ext4/ext4.h | 2 + fs/ext4/extents_status.c | 44 +++++++++++++++++++-- fs/ext4/extents_status.h | 15 +++++++ fs/ext4/super.c | 10 +++++ 6 files changed, 89 insertions(+), 3 deletions(-) diff --git a/Documentation/filesystems/ext4/bigalloc.rst b/Documentation/filesystems/ext4/bigalloc.rst index 976a180b209c..51aee018ec6d 100644 --- a/Documentation/filesystems/ext4/bigalloc.rst +++ b/Documentation/filesystems/ext4/bigalloc.rst @@ -3,6 +3,12 @@ Bigalloc -------- +Bigalloc support is enabled by default with ``CONFIG_EXT4_FS_BIGALLOC``. +Disabling this option removes the per-inode pending cluster reservation +tree and its tracking code. Such kernels reject filesystems with the +``bigalloc`` feature, including read-only mounts, before journal recovery. +Filesystems without this feature remain supported. + At the moment, the default size of a block is 4KiB, which is a commonly supported page size on most MMU-capable hardware. This is fortunate, as ext4 code is not prepared to handle the case where the block size diff --git a/fs/ext4/Kconfig b/fs/ext4/Kconfig index 0f3e702cd5e1..518bd68ed167 100644 --- a/fs/ext4/Kconfig +++ b/fs/ext4/Kconfig @@ -43,6 +43,21 @@ config EXT4_USE_FOR_EXT2 compiled kernel size by using one file system driver for ext2, ext3, and ext4 file systems. +config EXT4_FS_BIGALLOC + bool "Ext4 bigalloc support" + depends on EXT4_FS + default y + help + Support filesystems with the bigalloc feature, which allocate space + in clusters of multiple blocks to reduce allocation overhead for + large files. + + Say N to omit pending cluster reservation tracking and reduce memory + usage per inode. Filesystems with the bigalloc feature cannot be + mounted, including read-only mounts, when this option is disabled. + + If unsure, say Y. + config EXT4_FS_FAST_COMMIT bool "Ext4 fast commit support" depends on EXT4_FS diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 1aedbfbcb101..c2c242f60700 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -1194,8 +1194,10 @@ struct ext4_inode_info { /* ialloc */ ext4_group_t i_last_alloc_group; +#ifdef CONFIG_EXT4_FS_BIGALLOC /* pending cluster reservations for bigalloc file systems */ struct ext4_pending_tree i_pending_tree; +#endif /* on-disk additional length */ __u16 i_extra_isize; diff --git a/fs/ext4/extents_status.c b/fs/ext4/extents_status.c index 018b6146b9d8..68630b32720c 100644 --- a/fs/ext4/extents_status.c +++ b/fs/ext4/extents_status.c @@ -174,7 +174,9 @@ */ static struct kmem_cache *ext4_es_cachep; +#ifdef CONFIG_EXT4_FS_BIGALLOC static struct kmem_cache *ext4_pending_cachep; +#endif static int __es_insert_extent(struct inode *inode, struct extent_status *newes, struct extent_status *prealloc); @@ -185,9 +187,18 @@ static int __es_remove_extent(struct inode *inode, ext4_lblk_t lblk, static int es_reclaim_extents(struct ext4_inode_info *ei, int *nr_to_scan); static int __es_shrink(struct ext4_sb_info *sbi, int nr_to_scan, struct ext4_inode_info *locked_ei); +#ifdef CONFIG_EXT4_FS_BIGALLOC static int __revise_pending(struct inode *inode, ext4_lblk_t lblk, ext4_lblk_t len, struct pending_reservation **prealloc); +#else +static inline int __revise_pending(struct inode *inode, ext4_lblk_t lblk, + ext4_lblk_t len, + struct pending_reservation **prealloc) +{ + return 0; +} +#endif int __init ext4_init_es(void) { @@ -506,6 +517,7 @@ static void ext4_es_list_del(struct inode *inode) spin_unlock(&sbi->s_es_lock); } +#ifdef CONFIG_EXT4_FS_BIGALLOC static inline struct pending_reservation *__alloc_pending(bool nofail) { if (!nofail) @@ -518,6 +530,14 @@ static inline void __free_pending(struct pending_reservation *pr) { kmem_cache_free(ext4_pending_cachep, pr); } +#else +static inline struct pending_reservation *__alloc_pending(bool nofail) +{ + return NULL; +} + +static inline void __free_pending(struct pending_reservation *pr) {} +#endif /* * Returns true if we cannot fail to allocate memory for this extent_status @@ -934,7 +954,8 @@ void ext4_es_insert_extent(struct inode *inode, ext4_lblk_t lblk, ext4_es_insert_extent_check(inode, &newes); - revise_pending = sbi->s_cluster_ratio > 1 && + revise_pending = IS_ENABLED(CONFIG_EXT4_FS_BIGALLOC) && + sbi->s_cluster_ratio > 1 && test_opt(inode->i_sb, DELALLOC) && (status & (EXTENT_STATUS_WRITTEN | EXTENT_STATUS_UNWRITTEN)); @@ -1197,7 +1218,7 @@ static void init_rsvd(struct inode *inode, ext4_lblk_t lblk, * the region to be removed, if any, and note that there's no partial * cluster to track */ - if (sbi->s_cluster_ratio > 1) { + if (IS_ENABLED(CONFIG_EXT4_FS_BIGALLOC) && sbi->s_cluster_ratio > 1) { rc->first_do_lblk_found = false; if (lblk > es->es_lblk) { rc->left_es = es; @@ -1235,7 +1256,7 @@ static void count_rsvd(struct inode *inode, ext4_lblk_t lblk, long len, WARN_ON(len <= 0); - if (sbi->s_cluster_ratio == 1) { + if (!IS_ENABLED(CONFIG_EXT4_FS_BIGALLOC) || sbi->s_cluster_ratio == 1) { rc->ndelayed += (int) len; return; } @@ -1296,6 +1317,7 @@ static void count_rsvd(struct inode *inode, ext4_lblk_t lblk, long len, } } +#ifdef CONFIG_EXT4_FS_BIGALLOC /* * __pr_tree_search - search for a pending cluster reservation * @@ -1330,6 +1352,7 @@ static struct pending_reservation *__pr_tree_search(struct rb_root *root, } return NULL; } +#endif /* * get_rsvd - calculates and returns the number of cluster reservations to be @@ -1350,6 +1373,7 @@ static unsigned int get_rsvd(struct inode *inode, ext4_lblk_t end, struct extent_status *right_es, struct rsvd_count *rc) { +#ifdef CONFIG_EXT4_FS_BIGALLOC struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); struct pending_reservation *pr; struct ext4_pending_tree *tree = &EXT4_I(inode)->i_pending_tree; @@ -1457,6 +1481,7 @@ static unsigned int get_rsvd(struct inode *inode, ext4_lblk_t end, } } } +#endif return rc->ndelayed; } @@ -1996,6 +2021,7 @@ void ext4_clear_inode_es(struct inode *inode) write_unlock(&ei->i_es_lock); } +#ifdef CONFIG_EXT4_FS_BIGALLOC #ifdef ES_DEBUG__ static void ext4_print_pending_tree(struct inode *inode) { @@ -2188,6 +2214,16 @@ bool ext4_is_pending(struct inode *inode, ext4_lblk_t lblk) return ret; } +#else +#define ext4_print_pending_tree(inode) + +static inline int __insert_pending(struct inode *inode, ext4_lblk_t lblk, + struct pending_reservation **prealloc) +{ + return 0; +} +#endif + /* * ext4_es_insert_delayed_extent - adds some delayed blocks to the extents * status tree, adding a pending reservation @@ -2297,6 +2333,7 @@ void ext4_es_insert_delayed_extent(struct inode *inode, ext4_lblk_t lblk, return; } +#ifdef CONFIG_EXT4_FS_BIGALLOC /* * __revise_pending - makes, cancels, or leaves unchanged pending cluster * reservations for a specified block range depending @@ -2395,3 +2432,4 @@ static int __revise_pending(struct inode *inode, ext4_lblk_t lblk, out: return (ret < 0) ? ret : pendings; } +#endif /* CONFIG_EXT4_FS_BIGALLOC */ diff --git a/fs/ext4/extents_status.h b/fs/ext4/extents_status.h index 01cddcaaa68e..67d5059061a4 100644 --- a/fs/ext4/extents_status.h +++ b/fs/ext4/extents_status.h @@ -242,11 +242,26 @@ extern void ext4_es_destroy_stats(struct ext4_sb_info *sbi); extern int ext4_seq_es_shrinker_info_show(struct seq_file *seq, void *v); +#ifdef CONFIG_EXT4_FS_BIGALLOC extern int __init ext4_init_pending(void); extern void ext4_exit_pending(void); extern void ext4_init_pending_tree(struct ext4_pending_tree *tree); extern void ext4_remove_pending(struct inode *inode, ext4_lblk_t lblk); extern bool ext4_is_pending(struct inode *inode, ext4_lblk_t lblk); +#else +static inline int ext4_init_pending(void) +{ + return 0; +} + +static inline void ext4_exit_pending(void) {} +static inline void ext4_remove_pending(struct inode *inode, ext4_lblk_t lblk) {} +static inline bool ext4_is_pending(struct inode *inode, ext4_lblk_t lblk) +{ + return false; +} +#endif + extern void ext4_es_insert_delayed_extent(struct inode *inode, ext4_lblk_t lblk, ext4_lblk_t len, bool lclu_allocated, bool end_allocated); diff --git a/fs/ext4/super.c b/fs/ext4/super.c index aced1a67a554..a319077fb3ec 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -1438,7 +1438,9 @@ static struct inode *ext4_alloc_inode(struct super_block *sb) ei->i_es_seq = 0; ei->i_reserved_data_blocks = 0; spin_lock_init(&(ei->i_block_reservation_lock)); +#ifdef CONFIG_EXT4_FS_BIGALLOC ext4_init_pending_tree(&ei->i_pending_tree); +#endif #ifdef CONFIG_QUOTA ei->i_reserved_quota = 0; memset(&ei->i_dquot, 0, sizeof(ei->i_dquot)); @@ -3698,6 +3700,14 @@ int ext4_feature_set_ok(struct super_block *sb, int readonly) return 0; } + /* Read-only mounts can still replay the journal. */ + if (!IS_ENABLED(CONFIG_EXT4_FS_BIGALLOC) && + ext4_has_feature_bigalloc(sb)) { + ext4_msg(sb, KERN_ERR, + "Filesystem with bigalloc feature cannot be mounted without CONFIG_EXT4_FS_BIGALLOC"); + return 0; + } + if (readonly) return 1; -- 2.53.0