ubifs: Pass folios to acomp

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>
This commit is contained in:
Herbert Xu 2025-03-15 18:30:50 +08:00
parent 37b605f551
commit 7e0969bae4
4 changed files with 145 additions and 55 deletions

View File

@ -16,6 +16,7 @@
*/
#include <crypto/acompress.h>
#include <linux/highmem.h>
#include "ubifs.h"
/* Fake description object for the "none" compressor */
@ -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);
}
@ -141,6 +142,58 @@ no_compr:
*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,
@ -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);
}

View File

@ -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;
@ -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;
}
@ -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;
@ -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;
@ -98,27 +98,25 @@ dump:
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;
}
@ -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)
@ -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) {
@ -177,8 +171,6 @@ out:
kfree(dn);
if (!err)
folio_mark_uptodate(folio);
flush_dcache_folio(folio);
kunmap_local(addr);
return err;
}
@ -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;
}
@ -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;
@ -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;
@ -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:
@ -686,14 +674,10 @@ out_hole:
}
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;
@ -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;
@ -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",

View File

@ -845,14 +845,16 @@ out_ro:
* @c: UBIFS file-system description object
* @inode: inode the data node belongs to
* @key: node key
* @buf: buffer to write
* @folio: buffer to write
* @offset: offset to write at
* @len: data length (must not exceed %UBIFS_BLOCK_SIZE)
*
* This function writes a data node to the journal. Returns %0 if the data node
* was successfully written, and a negative error code in case of failure.
*/
int ubifs_jnl_write_data(struct ubifs_info *c, const struct inode *inode,
const union ubifs_key *key, const void *buf, int len)
const union ubifs_key *key, struct folio *folio,
size_t offset, int len)
{
struct ubifs_data_node *data;
int err, lnum, offs, compr_type, out_len, compr_len, auth_len;
@ -896,7 +898,8 @@ int ubifs_jnl_write_data(struct ubifs_info *c, const struct inode *inode,
compr_type = ui->compr_type;
out_len = compr_len = dlen - UBIFS_DATA_NODE_SZ;
ubifs_compress(c, buf, len, &data->data, &compr_len, &compr_type);
ubifs_compress_folio(c, folio, offset, len, &data->data, &compr_len,
&compr_type);
ubifs_assert(c, compr_len <= UBIFS_BLOCK_SIZE);
if (encrypted) {

View File

@ -263,6 +263,8 @@ enum {
ASSACT_PANIC,
};
struct folio;
/**
* struct ubifs_old_idx - index node obsoleted since last commit start.
* @rb: rb-tree node
@ -1784,7 +1786,8 @@ int ubifs_jnl_update(struct ubifs_info *c, const struct inode *dir,
const struct fscrypt_name *nm, const struct inode *inode,
int deletion, int xent, int in_orphan);
int ubifs_jnl_write_data(struct ubifs_info *c, const struct inode *inode,
const union ubifs_key *key, const void *buf, int len);
const union ubifs_key *key, struct folio *folio,
size_t offset, int len);
int ubifs_jnl_write_inode(struct ubifs_info *c, const struct inode *inode);
int ubifs_jnl_delete_inode(struct ubifs_info *c, const struct inode *inode);
int ubifs_jnl_xrename(struct ubifs_info *c, const struct inode *fst_dir,
@ -2084,8 +2087,14 @@ int __init ubifs_compressors_init(void);
void ubifs_compressors_exit(void);
void ubifs_compress(const struct ubifs_info *c, const void *in_buf, int in_len,
void *out_buf, int *out_len, int *compr_type);
void ubifs_compress_folio(const struct ubifs_info *c, struct folio *folio,
size_t offset, int in_len, void *out_buf,
int *out_len, int *compr_type);
int ubifs_decompress(const struct ubifs_info *c, const void *buf, int len,
void *out, int *out_len, int compr_type);
int ubifs_decompress_folio(const struct ubifs_info *c, const void *buf,
int len, struct folio *folio, size_t offset,
int *out_len, int compr_type);
/* sysfs.c */
int ubifs_sysfs_init(void);