All of lore.kernel.org
 help / color / mirror / Atom feed
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


  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.