From: Max Kellermann <max.kellermann@ionos.com>
To: tytso@mit.edu, adilger.kernel@dilger.ca,
libaokun@linux.alibaba.com, jack@suse.cz, ojaswin@linux.ibm.com,
ritesh.list@gmail.com, yi.zhang@huawei.com,
linux-ext4@vger.kernel.org, linux-kernel@vger.kernel.org
Cc: Max Kellermann <max.kellermann@ionos.com>
Subject: [PATCH 2/3] ext4: make bigalloc support optional
Date: Wed, 11 Nov 2026 13:52:41 +0100 [thread overview]
Message-ID: <20261111125242.1078620-3-max.kellermann@ionos.com> (raw)
In-Reply-To: <20261111125242.1078620-1-max.kellermann@ionos.com>
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 <max.kellermann@ionos.com>
---
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
next prev parent reply other threads:[~2026-11-11 12:52 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-11 12:52 [PATCH 0/3] ext4: reduce per-inode memory overhead Max Kellermann
2026-11-11 12:52 ` [PATCH 1/3] ext4: make fast commit support optional Max Kellermann
2026-11-11 13:15 ` sashiko-bot
2026-11-11 13:44 ` Max Kellermann
2026-11-11 12:52 ` Max Kellermann [this message]
2026-11-11 13:12 ` [PATCH 2/3] ext4: make bigalloc " sashiko-bot
2026-11-11 12:52 ` [PATCH 3/3] ext4: store creation time in separate seconds and nanoseconds Max Kellermann
2026-11-11 13:09 ` sashiko-bot
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261111125242.1078620-3-max.kellermann@ionos.com \
--to=max.kellermann@ionos.com \
--cc=adilger.kernel@dilger.ca \
--cc=jack@suse.cz \
--cc=libaokun@linux.alibaba.com \
--cc=linux-ext4@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=ojaswin@linux.ibm.com \
--cc=ritesh.list@gmail.com \
--cc=tytso@mit.edu \
--cc=yi.zhang@huawei.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.