mirror of
https://github.com/torvalds/linux.git
synced 2026-09-23 22:14:03 +02:00
ntfs: reuse compression output workspace across write units
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. Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
This commit is contained in:
parent
33200693eb
commit
8cd2bf1d7a
|
|
@ -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_context *pctx,
|
|||
return xout;
|
||||
}
|
||||
|
||||
static int ntfs_compress_workspace_init(struct ntfs_inode *ni,
|
||||
struct ntfs_compress_workspace *ws)
|
||||
{
|
||||
unsigned int size, i;
|
||||
|
||||
size = ni->itype.compressed.block_size + 2 *
|
||||
(ni->itype.compressed.block_size / NTFS_SB_SIZE) + 2;
|
||||
ws->nr_pages = DIV_ROUND_UP(size, PAGE_SIZE);
|
||||
ws->pages = kcalloc(ws->nr_pages, sizeof(*ws->pages), GFP_NOFS);
|
||||
if (!ws->pages)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < ws->nr_pages; i++) {
|
||||
ws->pages[i] = alloc_page(GFP_NOFS);
|
||||
if (!ws->pages[i])
|
||||
goto free_pages;
|
||||
}
|
||||
|
||||
ws->outbuf = 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 = 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 = 0, i;
|
||||
|
||||
for (i = 0; i < pages_per_cb && copied < bytes; i++) {
|
||||
unsigned int offset = i ? 0 : page_offset;
|
||||
unsigned int len = min(bytes - copied, PAGE_SIZE - offset);
|
||||
void *addr = kmap_local_page(pages[i]);
|
||||
|
||||
memcpy(ws->outbuf + copied, addr + offset, len);
|
||||
kunmap_local(addr);
|
||||
copied += 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_workspace *ws)
|
||||
{
|
||||
struct ntfs_volume *vol = ni->vol;
|
||||
char *outbuf = NULL, *pbuf, *inbuf, *in_mapping;
|
||||
char *outbuf = ws->outbuf, *pbuf;
|
||||
u32 compsz, p, insz = ni->itype.compressed.block_size;
|
||||
s32 rounded, bio_size;
|
||||
int sz;
|
||||
|
|
@ -1264,55 +1327,32 @@ static int ntfs_write_cb(struct ntfs_inode *ni, loff_t pos, struct page **pages,
|
|||
static char twozeroes[] = {0x02, 0xb0, 0x00, 0x00, 0x00};
|
||||
/* more compressed zeroes, to be followed by some count */
|
||||
static char morezeroes[] = {0x03, 0xb0, 0x02, 0x00};
|
||||
struct page **pages_disk = NULL, *pg;
|
||||
s64 bio_lcn;
|
||||
struct runlist_element *rlc, *rl;
|
||||
int i, err;
|
||||
int pages_count = (round_up(ni->itype.compressed.block_size + 2 *
|
||||
(ni->itype.compressed.block_size / NTFS_SB_SIZE) + 2, PAGE_SIZE)) / PAGE_SIZE;
|
||||
u32 cb_clusters = ni->itype.compressed.block_clusters;
|
||||
size_t new_rl_count;
|
||||
struct bio *bio = NULL;
|
||||
loff_t cb_pos, new_length;
|
||||
s64 new_vcn;
|
||||
|
||||
in_mapping = vmap(pages, pages_per_cb, VM_MAP, PAGE_KERNEL_RO);
|
||||
if (!in_mapping)
|
||||
return -ENOMEM;
|
||||
inbuf = in_mapping + page_offset;
|
||||
|
||||
/* may need 2 extra bytes per block and 2 more bytes */
|
||||
pages_disk = kcalloc(pages_count, sizeof(struct page *), GFP_NOFS);
|
||||
if (!pages_disk) {
|
||||
vunmap(in_mapping);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for (i = 0; i < pages_count; i++) {
|
||||
pg = alloc_page(GFP_KERNEL);
|
||||
if (!pg) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
pages_disk[i] = pg;
|
||||
lock_page(pg);
|
||||
}
|
||||
|
||||
outbuf = vmap(pages_disk, pages_count, VM_MAP, PAGE_KERNEL);
|
||||
if (!outbuf) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
compsz = 0;
|
||||
allzeroes = true;
|
||||
for (p = 0; (p < insz) && !fail; p += NTFS_SB_SIZE) {
|
||||
unsigned int input_offset = page_offset + p;
|
||||
unsigned int page_idx = input_offset >> PAGE_SHIFT;
|
||||
const char *input;
|
||||
void *addr;
|
||||
|
||||
if ((p + NTFS_SB_SIZE) < insz)
|
||||
bsz = NTFS_SB_SIZE;
|
||||
else
|
||||
bsz = insz - p;
|
||||
pbuf = &outbuf[compsz];
|
||||
sz = ntfs_compress_block(ctx, &inbuf[p], bsz, pbuf);
|
||||
addr = kmap_local_page(pages[page_idx]);
|
||||
input = addr + offset_in_page(input_offset);
|
||||
sz = ntfs_compress_block(ctx, input, bsz, pbuf);
|
||||
kunmap_local(addr);
|
||||
if (sz < 0) {
|
||||
err = sz;
|
||||
goto out;
|
||||
|
|
@ -1352,14 +1392,12 @@ static int ntfs_write_cb(struct ntfs_inode *ni, loff_t pos, struct page **pages,
|
|||
rounded = ((compsz - 1) | (vol->cluster_size - 1)) + 1;
|
||||
memset(&outbuf[compsz], 0, rounded - compsz);
|
||||
bio_size = rounded;
|
||||
pages = pages_disk;
|
||||
} else if (allzeroes) {
|
||||
err = 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 = insz;
|
||||
pages = pages_disk;
|
||||
}
|
||||
|
||||
new_length = 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, loff_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 = submit_bio_wait(bio);
|
||||
bio_put(bio);
|
||||
if (err)
|
||||
|
|
@ -1443,17 +1481,6 @@ static int ntfs_write_cb(struct ntfs_inode *ni, loff_t pos, struct page **pages,
|
|||
ntfs_error(vol->sb, "Failed to free hot clusters.");
|
||||
kvfree(rlc);
|
||||
out:
|
||||
if (outbuf)
|
||||
vunmap(outbuf);
|
||||
for (i = 0; i < pages_count; i++) {
|
||||
pg = 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, 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 ntfs_compress_workspace ws = {};
|
||||
struct compress_context *ctx;
|
||||
struct folio *folio;
|
||||
struct page **pages = NULL, *page;
|
||||
|
|
@ -1483,6 +1511,12 @@ int ntfs_compress_write(struct ntfs_inode *ni, loff_t pos, size_t count,
|
|||
kfree(pages);
|
||||
return -ENOMEM;
|
||||
}
|
||||
err = 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 = ntfs_write_cb(ni, pos, pages, pages_per_cb, page_offset, ctx);
|
||||
err = 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 = 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)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user