Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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/20200106205410.136707-1-ebiggers@kernel.org
Reviewed-by: Theodore Ts'o <tytso@mit.edu>
Signed-off-by: Eric Biggers <ebiggers@google.com>

+39 -6
+39 -6
fs/verity/enable.c
··· 13 13 #include <linux/sched/signal.h> 14 14 #include <linux/uaccess.h> 15 15 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, 17 44 u64 num_blocks_to_hash, 18 45 const struct merkle_tree_params *params, 19 46 u8 *pending_hashes, 20 47 struct ahash_request *req) 21 48 { 49 + struct inode *inode = file_inode(filp); 22 50 const struct fsverity_operations *vops = inode->i_sb->s_vop; 51 + struct file_ra_state ra = { 0 }; 23 52 unsigned int pending_size = 0; 24 53 u64 dst_block_num; 25 54 u64 i; ··· 65 36 dst_block_num = 0; /* unused */ 66 37 } 67 38 39 + file_ra_state_init(&ra, filp->f_mapping); 40 + 68 41 for (i = 0; i < num_blocks_to_hash; i++) { 69 42 struct page *src_page; 70 43 ··· 76 45 77 46 if (level == 0) { 78 47 /* Leaf: hashing a data block */ 79 - src_page = read_mapping_page(inode->i_mapping, i, NULL); 48 + src_page = read_file_data_page(filp, i, &ra, 49 + num_blocks_to_hash - i); 80 50 if (IS_ERR(src_page)) { 81 51 err = PTR_ERR(src_page); 82 52 fsverity_err(inode, ··· 135 103 } 136 104 137 105 /* 138 - * Build the Merkle tree for the given inode using the given parameters, and 106 + * Build the Merkle tree for the given file using the given parameters, and 139 107 * return the root hash in @root_hash. 140 108 * 141 109 * The tree is written to a filesystem-specific location as determined by the 142 110 * ->write_merkle_tree_block() method. However, the blocks that comprise the 143 111 * tree are the same for all filesystems. 144 112 */ 145 - static int build_merkle_tree(struct inode *inode, 113 + static int build_merkle_tree(struct file *filp, 146 114 const struct merkle_tree_params *params, 147 115 u8 *root_hash) 148 116 { 117 + struct inode *inode = file_inode(filp); 149 118 u8 *pending_hashes; 150 119 struct ahash_request *req; 151 120 u64 blocks; ··· 172 139 blocks = (inode->i_size + params->block_size - 1) >> 173 140 params->log_blocksize; 174 141 for (level = 0; level <= params->num_levels; level++) { 175 - err = build_merkle_tree_level(inode, level, blocks, params, 142 + err = build_merkle_tree_level(filp, level, blocks, params, 176 143 pending_hashes, req); 177 144 if (err) 178 145 goto out; ··· 260 227 */ 261 228 pr_debug("Building Merkle tree...\n"); 262 229 BUILD_BUG_ON(sizeof(desc->root_hash) < FS_VERITY_MAX_DIGEST_SIZE); 263 - err = build_merkle_tree(inode, &params, desc->root_hash); 230 + err = build_merkle_tree(filp, &params, desc->root_hash); 264 231 if (err) { 265 232 fsverity_err(inode, "Error %d building Merkle tree", err); 266 233 goto rollback;