Skip to content

Commit c22415d

Browse files
committed
fs-verity: implement readahead for FS_IOC_ENABLE_VERITY
When it builds the first level of the Merkle tree, FS_IOC_ENABLE_VERITY sequentially reads each page of the file using read_mapping_page(). This works fine if the file's data is already in pagecache, which should normally be the case, since this ioctl is normally used immediately after writing out the file. But in any other case this implementation performs very poorly, since only one page is read at a time. Fix this by implementing readahead using the functions from mm/readahead.c. This improves performance in the uncached case by about 20x, as seen in the following benchmarks done on a 250MB file (on x86_64 with SHA-NI): FS_IOC_ENABLE_VERITY uncached (before) 3.299s FS_IOC_ENABLE_VERITY uncached (after) 0.160s FS_IOC_ENABLE_VERITY cached 0.147s sha256sum uncached 0.191s sha256sum cached 0.145s Note: we could instead switch to kernel_read(). But that would mean we'd no longer be hashing the data directly from the pagecache, which is a nice optimization of its own. And using kernel_read() would require allocating another temporary buffer, hashing the data and tree pages separately, and explicitly zero-padding the last page -- so it wouldn't really be any simpler than direct pagecache access, at least for now. Link: https://lore.kernel.org/r/[email protected] Reviewed-by: Theodore Ts'o <[email protected]> Signed-off-by: Eric Biggers <[email protected]>
1 parent fd69884 commit c22415d

File tree

1 file changed

+39
-6
lines changed

1 file changed

+39
-6
lines changed

fs/verity/enable.c

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,42 @@
1313
#include <linux/sched/signal.h>
1414
#include <linux/uaccess.h>
1515

16-
static int build_merkle_tree_level(struct inode *inode, unsigned int level,
16+
/*
17+
* Read a file data page for Merkle tree construction. Do aggressive readahead,
18+
* since we're sequentially reading the entire file.
19+
*/
20+
static struct page *read_file_data_page(struct file *filp, pgoff_t index,
21+
struct file_ra_state *ra,
22+
unsigned long remaining_pages)
23+
{
24+
struct page *page;
25+
26+
page = find_get_page_flags(filp->f_mapping, index, FGP_ACCESSED);
27+
if (!page || !PageUptodate(page)) {
28+
if (page)
29+
put_page(page);
30+
else
31+
page_cache_sync_readahead(filp->f_mapping, ra, filp,
32+
index, remaining_pages);
33+
page = read_mapping_page(filp->f_mapping, index, NULL);
34+
if (IS_ERR(page))
35+
return page;
36+
}
37+
if (PageReadahead(page))
38+
page_cache_async_readahead(filp->f_mapping, ra, filp, page,
39+
index, remaining_pages);
40+
return page;
41+
}
42+
43+
static int build_merkle_tree_level(struct file *filp, unsigned int level,
1744
u64 num_blocks_to_hash,
1845
const struct merkle_tree_params *params,
1946
u8 *pending_hashes,
2047
struct ahash_request *req)
2148
{
49+
struct inode *inode = file_inode(filp);
2250
const struct fsverity_operations *vops = inode->i_sb->s_vop;
51+
struct file_ra_state ra = { 0 };
2352
unsigned int pending_size = 0;
2453
u64 dst_block_num;
2554
u64 i;
@@ -36,6 +65,8 @@ static int build_merkle_tree_level(struct inode *inode, unsigned int level,
3665
dst_block_num = 0; /* unused */
3766
}
3867

68+
file_ra_state_init(&ra, filp->f_mapping);
69+
3970
for (i = 0; i < num_blocks_to_hash; i++) {
4071
struct page *src_page;
4172

@@ -45,7 +76,8 @@ static int build_merkle_tree_level(struct inode *inode, unsigned int level,
4576

4677
if (level == 0) {
4778
/* Leaf: hashing a data block */
48-
src_page = read_mapping_page(inode->i_mapping, i, NULL);
79+
src_page = read_file_data_page(filp, i, &ra,
80+
num_blocks_to_hash - i);
4981
if (IS_ERR(src_page)) {
5082
err = PTR_ERR(src_page);
5183
fsverity_err(inode,
@@ -103,17 +135,18 @@ static int build_merkle_tree_level(struct inode *inode, unsigned int level,
103135
}
104136

105137
/*
106-
* Build the Merkle tree for the given inode using the given parameters, and
138+
* Build the Merkle tree for the given file using the given parameters, and
107139
* return the root hash in @root_hash.
108140
*
109141
* The tree is written to a filesystem-specific location as determined by the
110142
* ->write_merkle_tree_block() method. However, the blocks that comprise the
111143
* tree are the same for all filesystems.
112144
*/
113-
static int build_merkle_tree(struct inode *inode,
145+
static int build_merkle_tree(struct file *filp,
114146
const struct merkle_tree_params *params,
115147
u8 *root_hash)
116148
{
149+
struct inode *inode = file_inode(filp);
117150
u8 *pending_hashes;
118151
struct ahash_request *req;
119152
u64 blocks;
@@ -139,7 +172,7 @@ static int build_merkle_tree(struct inode *inode,
139172
blocks = (inode->i_size + params->block_size - 1) >>
140173
params->log_blocksize;
141174
for (level = 0; level <= params->num_levels; level++) {
142-
err = build_merkle_tree_level(inode, level, blocks, params,
175+
err = build_merkle_tree_level(filp, level, blocks, params,
143176
pending_hashes, req);
144177
if (err)
145178
goto out;
@@ -227,7 +260,7 @@ static int enable_verity(struct file *filp,
227260
*/
228261
pr_debug("Building Merkle tree...\n");
229262
BUILD_BUG_ON(sizeof(desc->root_hash) < FS_VERITY_MAX_DIGEST_SIZE);
230-
err = build_merkle_tree(inode, &params, desc->root_hash);
263+
err = build_merkle_tree(filp, &params, desc->root_hash);
231264
if (err) {
232265
fsverity_err(inode, "Error %d building Merkle tree", err);
233266
goto rollback;

0 commit comments

Comments
 (0)