Skip to content

Commit

Permalink
ubifs: Pass folios to acomp
Browse files Browse the repository at this point in the history
As the acomp interface supports folios, use that instead of mapping
the data in ubifs.

Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Tested-by: Zhihao Cheng <chengzhihao1@huawei.com> # For xfstests
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
  • Loading branch information
Herbert Xu committed Mar 21, 2025
1 parent 37b605f commit 7e0969b
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 55 deletions.
106 changes: 104 additions & 2 deletions fs/ubifs/compress.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*/

#include <crypto/acompress.h>
#include <linux/highmem.h>
#include "ubifs.h"

/* Fake description object for the "none" compressor */
Expand Down Expand Up @@ -126,7 +127,7 @@ void ubifs_compress(const struct ubifs_info *c, const void *in_buf,
{
ACOMP_REQUEST_ALLOC(req, compr->cc, GFP_NOFS | __GFP_NOWARN);

acomp_request_set_src_nondma(req, in_buf, in_len);
acomp_request_set_src_dma(req, in_buf, in_len);
err = ubifs_compress_req(c, req, out_buf, out_len, compr->name);
}

Expand All @@ -141,6 +142,58 @@ void ubifs_compress(const struct ubifs_info *c, const void *in_buf,
*compr_type = UBIFS_COMPR_NONE;
}

/**
* ubifs_compress_folio - compress folio.
* @c: UBIFS file-system description object
* @in_folio: data to compress
* @in_offset: offset into @in_folio
* @in_len: length of the data to compress
* @out_buf: output buffer where compressed data should be stored
* @out_len: output buffer length is returned here
* @compr_type: type of compression to use on enter, actually used compression
* type on exit
*
* This function compresses input folio @in_folio of length @in_len and
* stores the result in the output buffer @out_buf and the resulting length
* in @out_len. If the input buffer does not compress, it is just copied
* to the @out_buf. The same happens if @compr_type is %UBIFS_COMPR_NONE
* or if compression error occurred.
*
* Note, if the input buffer was not compressed, it is copied to the output
* buffer and %UBIFS_COMPR_NONE is returned in @compr_type.
*/
void ubifs_compress_folio(const struct ubifs_info *c, struct folio *in_folio,
size_t in_offset, int in_len, void *out_buf,
int *out_len, int *compr_type)
{
int err;
struct ubifs_compressor *compr = ubifs_compressors[*compr_type];

if (*compr_type == UBIFS_COMPR_NONE)
goto no_compr;

/* If the input data is small, do not even try to compress it */
if (in_len < UBIFS_MIN_COMPR_LEN)
goto no_compr;

{
ACOMP_REQUEST_ALLOC(req, compr->cc, GFP_NOFS | __GFP_NOWARN);

acomp_request_set_src_folio(req, in_folio, in_offset, in_len);
err = ubifs_compress_req(c, req, out_buf, out_len, compr->name);
}

if (err)
goto no_compr;

return;

no_compr:
memcpy_from_folio(out_buf, in_folio, in_offset, in_len);
*out_len = in_len;
*compr_type = UBIFS_COMPR_NONE;
}

static int ubifs_decompress_req(const struct ubifs_info *c,
struct acomp_req *req,
const void *in_buf, int in_len, int *out_len,
Expand Down Expand Up @@ -205,7 +258,56 @@ int ubifs_decompress(const struct ubifs_info *c, const void *in_buf,
{
ACOMP_REQUEST_ALLOC(req, compr->cc, GFP_NOFS | __GFP_NOWARN);

acomp_request_set_dst_nondma(req, out_buf, *out_len);
acomp_request_set_dst_dma(req, out_buf, *out_len);
return ubifs_decompress_req(c, req, in_buf, in_len, out_len,
compr->name);
}
}

/**
* ubifs_decompress_folio - decompress folio.
* @c: UBIFS file-system description object
* @in_buf: data to decompress
* @in_len: length of the data to decompress
* @out_folio: output folio where decompressed data should
* @out_offset: offset into @out_folio
* @out_len: output length is returned here
* @compr_type: type of compression
*
* This function decompresses data from buffer @in_buf into folio
* @out_folio. The length of the uncompressed data is returned in
* @out_len. This functions returns %0 on success or a negative error
* code on failure.
*/
int ubifs_decompress_folio(const struct ubifs_info *c, const void *in_buf,
int in_len, struct folio *out_folio,
size_t out_offset, int *out_len, int compr_type)
{
struct ubifs_compressor *compr;

if (unlikely(compr_type < 0 || compr_type >= UBIFS_COMPR_TYPES_CNT)) {
ubifs_err(c, "invalid compression type %d", compr_type);
return -EINVAL;
}

compr = ubifs_compressors[compr_type];

if (unlikely(!compr->capi_name)) {
ubifs_err(c, "%s compression is not compiled in", compr->name);
return -EINVAL;
}

if (compr_type == UBIFS_COMPR_NONE) {
memcpy_to_folio(out_folio, out_offset, in_buf, in_len);
*out_len = in_len;
return 0;
}

{
ACOMP_REQUEST_ALLOC(req, compr->cc, GFP_NOFS | __GFP_NOWARN);

acomp_request_set_dst_folio(req, out_folio, out_offset,
*out_len);
return ubifs_decompress_req(c, req, in_buf, in_len, out_len,
compr->name);
}
Expand Down
74 changes: 25 additions & 49 deletions fs/ubifs/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@
#include <linux/slab.h>
#include <linux/migrate.h>

static int read_block(struct inode *inode, void *addr, unsigned int block,
struct ubifs_data_node *dn)
static int read_block(struct inode *inode, struct folio *folio, size_t offset,
unsigned int block, struct ubifs_data_node *dn)
{
struct ubifs_info *c = inode->i_sb->s_fs_info;
int err, len, out_len;
Expand All @@ -55,7 +55,7 @@ static int read_block(struct inode *inode, void *addr, unsigned int block,
if (err) {
if (err == -ENOENT)
/* Not found, so it must be a hole */
memset(addr, 0, UBIFS_BLOCK_SIZE);
folio_zero_range(folio, offset, UBIFS_BLOCK_SIZE);
return err;
}

Expand All @@ -74,8 +74,8 @@ static int read_block(struct inode *inode, void *addr, unsigned int block,
}

out_len = UBIFS_BLOCK_SIZE;
err = ubifs_decompress(c, &dn->data, dlen, addr, &out_len,
le16_to_cpu(dn->compr_type));
err = ubifs_decompress_folio(c, &dn->data, dlen, folio, offset,
&out_len, le16_to_cpu(dn->compr_type));
if (err || len != out_len)
goto dump;

Expand All @@ -85,7 +85,7 @@ static int read_block(struct inode *inode, void *addr, unsigned int block,
* appending data). Ensure that the remainder is zeroed out.
*/
if (len < UBIFS_BLOCK_SIZE)
memset(addr + len, 0, UBIFS_BLOCK_SIZE - len);
folio_zero_range(folio, offset + len, UBIFS_BLOCK_SIZE - len);

return 0;

Expand All @@ -98,27 +98,25 @@ static int read_block(struct inode *inode, void *addr, unsigned int block,

static int do_readpage(struct folio *folio)
{
void *addr;
int err = 0, i;
unsigned int block, beyond;
struct ubifs_data_node *dn = NULL;
struct inode *inode = folio->mapping->host;
struct ubifs_info *c = inode->i_sb->s_fs_info;
loff_t i_size = i_size_read(inode);
size_t offset = 0;

dbg_gen("ino %lu, pg %lu, i_size %lld, flags %#lx",
inode->i_ino, folio->index, i_size, folio->flags);
ubifs_assert(c, !folio_test_checked(folio));
ubifs_assert(c, !folio->private);

addr = kmap_local_folio(folio, 0);

block = folio->index << UBIFS_BLOCKS_PER_PAGE_SHIFT;
beyond = (i_size + UBIFS_BLOCK_SIZE - 1) >> UBIFS_BLOCK_SHIFT;
if (block >= beyond) {
/* Reading beyond inode */
folio_set_checked(folio);
addr = folio_zero_tail(folio, 0, addr);
folio_zero_range(folio, 0, folio_size(folio));
goto out;
}

Expand All @@ -135,9 +133,9 @@ static int do_readpage(struct folio *folio)
if (block >= beyond) {
/* Reading beyond inode */
err = -ENOENT;
memset(addr, 0, UBIFS_BLOCK_SIZE);
folio_zero_range(folio, offset, UBIFS_BLOCK_SIZE);
} else {
ret = read_block(inode, addr, block, dn);
ret = read_block(inode, folio, offset, block, dn);
if (ret) {
err = ret;
if (err != -ENOENT)
Expand All @@ -147,17 +145,13 @@ static int do_readpage(struct folio *folio)
int ilen = i_size & (UBIFS_BLOCK_SIZE - 1);

if (ilen && ilen < dlen)
memset(addr + ilen, 0, dlen - ilen);
folio_zero_range(folio, offset + ilen, dlen - ilen);
}
}
if (++i >= (UBIFS_BLOCKS_PER_PAGE << folio_order(folio)))
break;
block += 1;
addr += UBIFS_BLOCK_SIZE;
if (folio_test_highmem(folio) && (offset_in_page(addr) == 0)) {
kunmap_local(addr - UBIFS_BLOCK_SIZE);
addr = kmap_local_folio(folio, i * UBIFS_BLOCK_SIZE);
}
offset += UBIFS_BLOCK_SIZE;
}

if (err) {
Expand All @@ -177,8 +171,6 @@ static int do_readpage(struct folio *folio)
kfree(dn);
if (!err)
folio_mark_uptodate(folio);
flush_dcache_folio(folio);
kunmap_local(addr);
return err;
}

Expand Down Expand Up @@ -602,18 +594,16 @@ static int populate_page(struct ubifs_info *c, struct folio *folio,
struct inode *inode = folio->mapping->host;
loff_t i_size = i_size_read(inode);
unsigned int page_block;
void *addr, *zaddr;
size_t offset = 0;
pgoff_t end_index;

dbg_gen("ino %lu, pg %lu, i_size %lld, flags %#lx",
inode->i_ino, folio->index, i_size, folio->flags);

addr = zaddr = kmap_local_folio(folio, 0);

end_index = (i_size - 1) >> PAGE_SHIFT;
if (!i_size || folio->index > end_index) {
hole = 1;
addr = folio_zero_tail(folio, 0, addr);
folio_zero_range(folio, 0, folio_size(folio));
goto out_hole;
}

Expand All @@ -623,7 +613,7 @@ static int populate_page(struct ubifs_info *c, struct folio *folio,

if (nn >= bu->cnt) {
hole = 1;
memset(addr, 0, UBIFS_BLOCK_SIZE);
folio_zero_range(folio, offset, UBIFS_BLOCK_SIZE);
} else if (key_block(c, &bu->zbranch[nn].key) == page_block) {
struct ubifs_data_node *dn;

Expand All @@ -645,13 +635,15 @@ static int populate_page(struct ubifs_info *c, struct folio *folio,
goto out_err;
}

err = ubifs_decompress(c, &dn->data, dlen, addr, &out_len,
le16_to_cpu(dn->compr_type));
err = ubifs_decompress_folio(
c, &dn->data, dlen, folio, offset, &out_len,
le16_to_cpu(dn->compr_type));
if (err || len != out_len)
goto out_err;

if (len < UBIFS_BLOCK_SIZE)
memset(addr + len, 0, UBIFS_BLOCK_SIZE - len);
folio_zero_range(folio, offset + len,
UBIFS_BLOCK_SIZE - len);

nn += 1;
read = (i << UBIFS_BLOCK_SHIFT) + len;
Expand All @@ -660,23 +652,19 @@ static int populate_page(struct ubifs_info *c, struct folio *folio,
continue;
} else {
hole = 1;
memset(addr, 0, UBIFS_BLOCK_SIZE);
folio_zero_range(folio, offset, UBIFS_BLOCK_SIZE);
}
if (++i >= UBIFS_BLOCKS_PER_PAGE)
break;
addr += UBIFS_BLOCK_SIZE;
offset += UBIFS_BLOCK_SIZE;
page_block += 1;
if (folio_test_highmem(folio) && (offset_in_page(addr) == 0)) {
kunmap_local(addr - UBIFS_BLOCK_SIZE);
addr = kmap_local_folio(folio, i * UBIFS_BLOCK_SIZE);
}
}

if (end_index == folio->index) {
int len = i_size & (PAGE_SIZE - 1);

if (len && len < read)
memset(zaddr + len, 0, read - len);
folio_zero_range(folio, len, read - len);
}

out_hole:
Expand All @@ -686,14 +674,10 @@ static int populate_page(struct ubifs_info *c, struct folio *folio,
}

folio_mark_uptodate(folio);
flush_dcache_folio(folio);
kunmap_local(addr);
*n = nn;
return 0;

out_err:
flush_dcache_folio(folio);
kunmap_local(addr);
ubifs_err(c, "bad data node (block %u, inode %lu)",
page_block, inode->i_ino);
return -EINVAL;
Expand Down Expand Up @@ -898,7 +882,6 @@ static int do_writepage(struct folio *folio, size_t len)
{
int err = 0, blen;
unsigned int block;
void *addr;
size_t offset = 0;
union ubifs_key key;
struct inode *inode = folio->mapping->host;
Expand All @@ -913,26 +896,19 @@ static int do_writepage(struct folio *folio, size_t len)

folio_start_writeback(folio);

addr = kmap_local_folio(folio, offset);
block = folio->index << UBIFS_BLOCKS_PER_PAGE_SHIFT;
for (;;) {
blen = min_t(size_t, len, UBIFS_BLOCK_SIZE);
data_key_init(c, &key, inode->i_ino, block);
err = ubifs_jnl_write_data(c, inode, &key, addr, blen);
err = ubifs_jnl_write_data(c, inode, &key, folio, offset, blen);
if (err)
break;
len -= blen;
if (!len)
break;
block += 1;
addr += blen;
if (folio_test_highmem(folio) && !offset_in_page(addr)) {
kunmap_local(addr - blen);
offset += PAGE_SIZE;
addr = kmap_local_folio(folio, offset);
}
offset += blen;
}
kunmap_local(addr);
if (err) {
mapping_set_error(folio->mapping, err);
ubifs_err(c, "cannot write folio %lu of inode %lu, error %d",
Expand Down
Loading

0 comments on commit 7e0969b

Please sign in to comment.