[PATCH 06/11] ntfs: reuse the compression context during writes

Namjae Jeon <[email protected]> Tue, 21 Jul 2026 18:54:26 +0900
Newsgroups dev.linux.lists.ntfs
Message-ID <[email protected]>
ntfs_compress_block() allocates and initializes a roughly 40 KiB match
finder context for every 4 KiB sub-block. A 64 KiB compression unit thus
performs sixteen large allocations even though the calls are serialized.

Allocate one context for the complete write request and reset its hash
chains for each sub-block as before.

Signed-off-by: Namjae Jeon <[email protected]>
---
 fs/ntfs/compress.c | 28 +++++++++++++---------------
 1 file changed, 13 insertions(+), 15 deletions(-)

diff --git a/fs/ntfs/compress.c b/fs/ntfs/compress.c
index a993cba3b961..f073a53f6136 100644
--- a/fs/ntfs/compress.c
+++ b/fs/ntfs/compress.c
@@ -1072,10 +1072,9 @@ static void ntfs_skip_position(struct compress_context *pctx, const int i)
  * header (minimal size is 2, maximum size is 4098)
  * A negative error code if an error has been met.
  */
-static int ntfs_compress_block(const char *inbuf, const int bufsize,
-			       char *outbuf)
+static int ntfs_compress_block(struct compress_context *pctx,
+			       const char *inbuf, const int bufsize, char *outbuf)
 {
-	struct compress_context *pctx;
 	int i; /* current position */
 	int j; /* end of best match from current position */
 	int k; /* end of best match from next position */
@@ -1090,10 +1089,6 @@ static int ntfs_compress_block(const char *inbuf, const int bufsize,
 	int tag;    /* current value of tag */
 	int ntag;   /* count of bits still undefined in tag */
 
-	pctx = kvzalloc(sizeof(struct compress_context), GFP_NOFS);
-	if (!pctx)
-		return -ENOMEM;
-
 	/*
 	 * All hash chains start as empty.  The special value '-1' indicates the
 	 * end of each hash chain.
@@ -1249,16 +1244,12 @@ static int ntfs_compress_block(const char *inbuf, const int bufsize,
 		xout = NTFS_SB_SIZE + 2;
 	}
 
-	/*
-	 * Free the compression context and return the total number of bytes
-	 * written to 'outbuf'.
-	 */
-	kvfree(pctx);
 	return xout;
 }
 
 static int ntfs_write_cb(struct ntfs_inode *ni, loff_t pos, struct page **pages,
-		int pages_per_cb, unsigned int page_offset)
+		int pages_per_cb, unsigned int page_offset,
+		struct compress_context *ctx)
 {
 	struct ntfs_volume *vol = ni->vol;
 	char *outbuf = NULL, *pbuf, *inbuf, *in_mapping;
@@ -1321,7 +1312,7 @@ static int ntfs_write_cb(struct ntfs_inode *ni, loff_t pos, struct page **pages,
 		else
 			bsz = insz - p;
 		pbuf = &outbuf[compsz];
-		sz = ntfs_compress_block(&inbuf[p], bsz, pbuf);
+		sz = ntfs_compress_block(ctx, &inbuf[p], bsz, pbuf);
 		if (sz < 0) {
 			err = sz;
 			goto out;
@@ -1472,6 +1463,7 @@ static int ntfs_write_cb(struct ntfs_inode *ni, loff_t pos, struct page **pages,
 int ntfs_compress_write(struct ntfs_inode *ni, loff_t pos, size_t count,
 		struct iov_iter *from)
 {
+	struct compress_context *ctx;
 	struct folio *folio;
 	struct page **pages = NULL, *page;
 	int pages_per_cb;
@@ -1486,6 +1478,11 @@ int ntfs_compress_write(struct ntfs_inode *ni, loff_t pos, size_t count,
 	pages = kmalloc_array(pages_per_cb, sizeof(struct page *), GFP_NOFS);
 	if (!pages)
 		return -ENOMEM;
+	ctx = kvzalloc_obj(*ctx, GFP_NOFS);
+	if (!ctx) {
+		kfree(pages);
+		return -ENOMEM;
+	}
 
 	while (count) {
 		pgoff_t index;
@@ -1554,7 +1551,7 @@ int ntfs_compress_write(struct ntfs_inode *ni, loff_t pos, size_t count,
 			goto release_pages;
 		}
 
-		err = ntfs_write_cb(ni, pos, pages, pages_per_cb, page_offset);
+		err = ntfs_write_cb(ni, pos, pages, pages_per_cb, page_offset, ctx);
 		if (!err && pos + copied > ni->initialized_size) {
 			mutex_lock(&ni->mrec_lock);
 			err = ntfs_attr_set_initialized_size(ni, pos + copied);
@@ -1584,6 +1581,7 @@ int ntfs_compress_write(struct ntfs_inode *ni, loff_t pos, size_t count,
 	}
 
 out:
+	kvfree(ctx);
 	kfree(pages);
 	if (err < 0)
 		written = err;
-- 
2.34.1