[PATCH v4] ntfs: avoid heap allocation for free-cluster readahead state

DaeMyung Kang posted 1 patch 2 days, 4 hours ago
fs/ntfs/super.c | 22 ++++++----------------
1 file changed, 6 insertions(+), 16 deletions(-)
[PATCH v4] ntfs: avoid heap allocation for free-cluster readahead state
Posted by DaeMyung Kang 2 days, 4 hours ago
get_nr_free_clusters() allocates a temporary file_ra_state before it
publishes the precomputed free cluster count, sets NVolFreeClusterKnown(),
and wakes vol->free_waitq. If that allocation fails, the worker returns
without setting the flag or waking waiters, so callers waiting for the free
count can block indefinitely.

The readahead state is only used synchronously while scanning the bitmap.
Keep it on the stack and pass it by address to the readahead helper. This
eliminates the early allocation failure path instead of adding a special
case that publishes a conservative count and wakes the waitqueue.
Zero-initialize the on-stack state because file_ra_state_init() only sets
ra_pages and prev_pos.

Apply the same treatment to __get_nr_free_mft_records(), which scans the
MFT bitmap with the same short-lived readahead state.

Signed-off-by: DaeMyung Kang <charsyam@gmail.com>
---
Changes in v4:
- Rebase onto ntfs-next.
- Convert __get_nr_free_mft_records() in the same way as
  get_nr_free_clusters().

 fs/ntfs/super.c | 22 ++++++----------------
 1 file changed, 6 insertions(+), 16 deletions(-)

diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c
index 9ebd13d1613b..2a5ad7d56bc2 100644
--- a/fs/ntfs/super.c
+++ b/fs/ntfs/super.c
@@ -1848,7 +1848,7 @@ s64 get_nr_free_clusters(struct ntfs_volume *vol)
 	struct address_space *mapping = vol->lcnbmp_ino->i_mapping;
 	struct folio *folio;
 	pgoff_t index, max_index;
-	struct file_ra_state *ra;
+	struct file_ra_state ra = { 0 };
 
 	ntfs_debug("Entering.");
 	/* Serialize accesses to the cluster bitmap. */
@@ -1856,11 +1856,7 @@ s64 get_nr_free_clusters(struct ntfs_volume *vol)
 	if (NVolFreeClusterKnown(vol))
 		return atomic64_read(&vol->free_clusters);
 
-	ra = kzalloc(sizeof(*ra), GFP_NOFS);
-	if (!ra)
-		return 0;
-
-	file_ra_state_init(ra, mapping);
+	file_ra_state_init(&ra, mapping);
 
 	/*
 	 * Convert the number of bits into bytes rounded up, then convert into
@@ -1879,7 +1875,7 @@ s64 get_nr_free_clusters(struct ntfs_volume *vol)
 		 * Get folio from page cache, getting it from backing store
 		 * if necessary, and increment the use count.
 		 */
-		folio = ntfs_get_locked_folio(mapping, index, max_index, ra);
+		folio = ntfs_get_locked_folio(mapping, index, max_index, &ra);
 
 		/* Ignore pages which errored synchronously. */
 		if (IS_ERR(folio)) {
@@ -1918,7 +1914,6 @@ s64 get_nr_free_clusters(struct ntfs_volume *vol)
 	else
 		atomic64_set(&vol->free_clusters, nr_free);
 
-	kfree(ra);
 	NVolSetFreeClusterKnown(vol);
 	wake_up_all(&vol->free_waitq);
 	ntfs_debug("Exiting.");
@@ -1973,15 +1968,11 @@ static unsigned long __get_nr_free_mft_records(struct ntfs_volume *vol,
 	struct address_space *mapping = vol->mftbmp_ino->i_mapping;
 	struct folio *folio;
 	pgoff_t index;
-	struct file_ra_state *ra;
+	struct file_ra_state ra = { 0 };
 
 	ntfs_debug("Entering.");
 
-	ra = kzalloc(sizeof(*ra), GFP_NOFS);
-	if (!ra)
-		return 0;
-
-	file_ra_state_init(ra, mapping);
+	file_ra_state_init(&ra, mapping);
 
 	/* Use multiples of 4 bytes, thus max_size is PAGE_SIZE / 4. */
 	ntfs_debug("Reading $MFT/$BITMAP, max_index = 0x%lx, max_size = 0x%lx.",
@@ -1993,7 +1984,7 @@ static unsigned long __get_nr_free_mft_records(struct ntfs_volume *vol,
 		 * Get folio from page cache, getting it from backing store
 		 * if necessary, and increment the use count.
 		 */
-		folio = ntfs_get_locked_folio(mapping, index, max_index, ra);
+		folio = ntfs_get_locked_folio(mapping, index, max_index, &ra);
 
 		/* Ignore pages which errored synchronously. */
 		if (IS_ERR(folio)) {
@@ -2025,7 +2016,6 @@ static unsigned long __get_nr_free_mft_records(struct ntfs_volume *vol,
 	else
 		atomic64_set(&vol->free_mft_records, nr_free);
 
-	kfree(ra);
 	ntfs_debug("Exiting.");
 	return nr_free;
 }

base-commit: 5a717e8c387c2a3ccd372a1b34005c1dcedee297
-- 
2.43.0
Re: [PATCH v4] ntfs: avoid heap allocation for free-cluster readahead state
Posted by Namjae Jeon 1 day, 4 hours ago
On Fri, May 22, 2026 at 11:20 PM DaeMyung Kang <charsyam@gmail.com> wrote:
>
> get_nr_free_clusters() allocates a temporary file_ra_state before it
> publishes the precomputed free cluster count, sets NVolFreeClusterKnown(),
> and wakes vol->free_waitq. If that allocation fails, the worker returns
> without setting the flag or waking waiters, so callers waiting for the free
> count can block indefinitely.
>
> The readahead state is only used synchronously while scanning the bitmap.
> Keep it on the stack and pass it by address to the readahead helper. This
> eliminates the early allocation failure path instead of adding a special
> case that publishes a conservative count and wakes the waitqueue.
> Zero-initialize the on-stack state because file_ra_state_init() only sets
> ra_pages and prev_pos.
>
> Apply the same treatment to __get_nr_free_mft_records(), which scans the
> MFT bitmap with the same short-lived readahead state.
>
> Signed-off-by: DaeMyung Kang <charsyam@gmail.com>
Applied it to #ntfs-next.
Thanks!