Re: [PATCH 07/11] ntfs: reuse compression output workspace across write units
Hyunchul Lee <[email protected]> Thu, 23 Jul 2026 11:15:41 +0900
| Newsgroups | dev.linux.lists.ntfs |
|---|---|
| Message-ID | <CANFS6bbspS6v_bqbUFEq8efyoWKGdAnTG+=iKFPyKx2LRvSJ=w@mail.gmail.com> |
2026=EB=85=84 7=EC=9B=94 21=EC=9D=BC (=ED=99=94) =EC=98=A4=ED=9B=84 6:55, N= amjae Jeon <[email protected]>=EB=8B=98=EC=9D=B4 =EC=9E=91=EC=84=B1: > > ntfs_write_cb() allocates output pages and creates input and output > vmaps for every compression unit. Sequential writes repeatedly pay > those allocation and page-table costs even though each unit has the > same maximum output size. > Allocate and map the output workspace once per write request. Access > > input sub-blocks with kmap_local_page(), and reuse the output pages and > mapping for every compression unit in the request. > > Signed-off-by: Namjae Jeon <[email protected]> Looks good to me. Reviewed-by: Hyunchul Lee <[email protected]> > --- > fs/ntfs/compress.c | 135 ++++++++++++++++++++++++++++----------------- > 1 file changed, 85 insertions(+), 50 deletions(-) > > diff --git a/fs/ntfs/compress.c b/fs/ntfs/compress.c > index f073a53f6136..f3c14518f78e 100644 > --- a/fs/ntfs/compress.c > +++ b/fs/ntfs/compress.c > @@ -894,6 +894,12 @@ struct compress_context { > s16 prev[NTFS_SB_SIZE]; > }; > > +struct ntfs_compress_workspace { > + struct page **pages; > + char *outbuf; > + unsigned int nr_pages; > +}; > + > /* > * Hash the next 3-byte sequence in the input buffer > */ > @@ -1247,12 +1253,69 @@ static int ntfs_compress_block(struct compress_co= ntext *pctx, > return xout; > } > > +static int ntfs_compress_workspace_init(struct ntfs_inode *ni, > + struct ntfs_compress_workspace *w= s) > +{ > + unsigned int size, i; > + > + size =3D ni->itype.compressed.block_size + 2 * > + (ni->itype.compressed.block_size / NTFS_SB_SIZE) + 2; > + ws->nr_pages =3D DIV_ROUND_UP(size, PAGE_SIZE); > + ws->pages =3D kcalloc(ws->nr_pages, sizeof(*ws->pages), GFP_NOFS)= ; > + if (!ws->pages) > + return -ENOMEM; > + > + for (i =3D 0; i < ws->nr_pages; i++) { > + ws->pages[i] =3D alloc_page(GFP_NOFS); > + if (!ws->pages[i]) > + goto free_pages; > + } > + > + ws->outbuf =3D vmap(ws->pages, ws->nr_pages, VM_MAP, PAGE_KERNEL)= ; > + if (!ws->outbuf) > + goto free_pages; > + return 0; > + > +free_pages: > + while (i) > + put_page(ws->pages[--i]); > + kfree(ws->pages); > + return -ENOMEM; > +} > + > +static void ntfs_compress_workspace_free(struct ntfs_compress_workspace = *ws) > +{ > + unsigned int i; > + > + vunmap(ws->outbuf); > + for (i =3D 0; i < ws->nr_pages; i++) > + put_page(ws->pages[i]); > + kfree(ws->pages); > +} > + > +static void ntfs_copy_cb(struct page **pages, int pages_per_cb, > + unsigned int page_offset, > + struct ntfs_compress_workspace *ws, unsigned int= bytes) > +{ > + unsigned int copied =3D 0, i; > + > + for (i =3D 0; i < pages_per_cb && copied < bytes; i++) { > + unsigned int offset =3D i ? 0 : page_offset; > + unsigned int len =3D min(bytes - copied, PAGE_SIZE - offs= et); > + void *addr =3D kmap_local_page(pages[i]); > + > + memcpy(ws->outbuf + copied, addr + offset, len); > + kunmap_local(addr); > + copied +=3D len; > + } > +} > + > static int ntfs_write_cb(struct ntfs_inode *ni, loff_t pos, struct page = **pages, > int pages_per_cb, unsigned int page_offset, > - struct compress_context *ctx) > + struct compress_context *ctx, struct ntfs_compress_worksp= ace *ws) > { > struct ntfs_volume *vol =3D ni->vol; > - char *outbuf =3D NULL, *pbuf, *inbuf, *in_mapping; > + char *outbuf =3D ws->outbuf, *pbuf; > u32 compsz, p, insz =3D ni->itype.compressed.block_size; > s32 rounded, bio_size; > int sz; > @@ -1264,55 +1327,32 @@ static int ntfs_write_cb(struct ntfs_inode *ni, l= off_t pos, struct page **pages, > static char twozeroes[] =3D {0x02, 0xb0, 0x00, 0x00, 0x00}; > /* more compressed zeroes, to be followed by some count */ > static char morezeroes[] =3D {0x03, 0xb0, 0x02, 0x00}; > - struct page **pages_disk =3D NULL, *pg; > s64 bio_lcn; > struct runlist_element *rlc, *rl; > int i, err; > - int pages_count =3D (round_up(ni->itype.compressed.block_size + 2= * > - (ni->itype.compressed.block_size / NTFS_SB_SIZE) + 2, PAG= E_SIZE)) / PAGE_SIZE; > u32 cb_clusters =3D ni->itype.compressed.block_clusters; > size_t new_rl_count; > struct bio *bio =3D NULL; > loff_t cb_pos, new_length; > s64 new_vcn; > > - in_mapping =3D vmap(pages, pages_per_cb, VM_MAP, PAGE_KERNEL_RO); > - if (!in_mapping) > - return -ENOMEM; > - inbuf =3D in_mapping + page_offset; > - > - /* may need 2 extra bytes per block and 2 more bytes */ > - pages_disk =3D kcalloc(pages_count, sizeof(struct page *), GFP_NO= FS); > - if (!pages_disk) { > - vunmap(in_mapping); > - return -ENOMEM; > - } > - > - for (i =3D 0; i < pages_count; i++) { > - pg =3D alloc_page(GFP_KERNEL); > - if (!pg) { > - err =3D -ENOMEM; > - goto out; > - } > - pages_disk[i] =3D pg; > - lock_page(pg); > - } > - > - outbuf =3D vmap(pages_disk, pages_count, VM_MAP, PAGE_KERNEL); > - if (!outbuf) { > - err =3D -ENOMEM; > - goto out; > - } > - > compsz =3D 0; > allzeroes =3D true; > for (p =3D 0; (p < insz) && !fail; p +=3D NTFS_SB_SIZE) { > + unsigned int input_offset =3D page_offset + p; > + unsigned int page_idx =3D input_offset >> PAGE_SHIFT; > + const char *input; > + void *addr; > + > if ((p + NTFS_SB_SIZE) < insz) > bsz =3D NTFS_SB_SIZE; > else > bsz =3D insz - p; > pbuf =3D &outbuf[compsz]; > - sz =3D ntfs_compress_block(ctx, &inbuf[p], bsz, pbuf); > + addr =3D kmap_local_page(pages[page_idx]); > + input =3D addr + offset_in_page(input_offset); > + sz =3D ntfs_compress_block(ctx, input, bsz, pbuf); > + kunmap_local(addr); > if (sz < 0) { > err =3D sz; > goto out; > @@ -1352,14 +1392,12 @@ static int ntfs_write_cb(struct ntfs_inode *ni, l= off_t pos, struct page **pages, > rounded =3D ((compsz - 1) | (vol->cluster_size - 1)) + 1; > memset(&outbuf[compsz], 0, rounded - compsz); > bio_size =3D rounded; > - pages =3D pages_disk; > } else if (allzeroes) { > err =3D ntfs_non_resident_attr_punch_hole(ni, new_vcn, cb= _clusters); > goto out; > } else { > - memcpy(outbuf, inbuf, insz); > + ntfs_copy_cb(pages, pages_per_cb, page_offset, ws, insz); > bio_size =3D insz; > - pages =3D pages_disk; > } > > new_length =3D ntfs_bytes_to_cluster(vol, round_up(bio_size, vol-= >cluster_size)); > @@ -1398,7 +1436,7 @@ static int ntfs_write_cb(struct ntfs_inode *ni, lof= f_t pos, struct page **pages, > ((s64)i << PAGE_SHIFT)); > } > > - if (!bio_add_page(bio, pages[i], page_size, 0)) { > + if (!bio_add_page(bio, ws->pages[i], page_size, 0)) { > err =3D submit_bio_wait(bio); > bio_put(bio); > if (err) > @@ -1443,17 +1481,6 @@ static int ntfs_write_cb(struct ntfs_inode *ni, lo= ff_t pos, struct page **pages, > ntfs_error(vol->sb, "Failed to free hot clusters."); > kvfree(rlc); > out: > - if (outbuf) > - vunmap(outbuf); > - for (i =3D 0; i < pages_count; i++) { > - pg =3D pages_disk[i]; > - if (pg) { > - unlock_page(pg); > - put_page(pg); > - } > - } > - kfree(pages_disk); > - vunmap(in_mapping); > NInoSetFileNameDirty(ni); > mark_mft_record_dirty(ni); > > @@ -1463,6 +1490,7 @@ static int ntfs_write_cb(struct ntfs_inode *ni, lof= f_t pos, struct page **pages, > int ntfs_compress_write(struct ntfs_inode *ni, loff_t pos, size_t count, > struct iov_iter *from) > { > + struct ntfs_compress_workspace ws =3D {}; > struct compress_context *ctx; > struct folio *folio; > struct page **pages =3D NULL, *page; > @@ -1483,6 +1511,12 @@ int ntfs_compress_write(struct ntfs_inode *ni, lof= f_t pos, size_t count, > kfree(pages); > return -ENOMEM; > } > + err =3D ntfs_compress_workspace_init(ni, &ws); > + if (err) { > + kvfree(ctx); > + kfree(pages); > + return err; > + } > > while (count) { > pgoff_t index; > @@ -1551,7 +1585,7 @@ int ntfs_compress_write(struct ntfs_inode *ni, loff= _t pos, size_t count, > goto release_pages; > } > > - err =3D ntfs_write_cb(ni, pos, pages, pages_per_cb, page_= offset, ctx); > + err =3D ntfs_write_cb(ni, pos, pages, pages_per_cb, page_= offset, ctx, &ws); > if (!err && pos + copied > ni->initialized_size) { > mutex_lock(&ni->mrec_lock); > err =3D ntfs_attr_set_initialized_size(ni, pos + = copied); > @@ -1581,6 +1615,7 @@ int ntfs_compress_write(struct ntfs_inode *ni, loff= _t pos, size_t count, > } > > out: > + ntfs_compress_workspace_free(&ws); > kvfree(ctx); > kfree(pages); > if (err < 0) > -- > 2.34.1 > --=20 Thanks, Hyunchul