Skip to content

Commit

Permalink
Merge tag 'fsverity-for-linus' of git://git.kernel.org/pub/scm/fs/fsc…
Browse files Browse the repository at this point in the history
…rypt/fscrypt

Pull fsverity updates from Eric Biggers:

 - Optimize fs-verity sequential read performance by implementing
   readahead of Merkle tree pages. This allows the Merkle tree to be
   read in larger chunks.

 - Optimize FS_IOC_ENABLE_VERITY performance in the uncached case by
   implementing readahead of data pages.

 - Allocate the hash requests from a mempool in order to eliminate the
   possibility of allocation failures during I/O.

* tag 'fsverity-for-linus' of git://git.kernel.org/pub/scm/fs/fscrypt/fscrypt:
  fs-verity: use u64_to_user_ptr()
  fs-verity: use mempool for hash requests
  fs-verity: implement readahead of Merkle tree pages
  fs-verity: implement readahead for FS_IOC_ENABLE_VERITY
  • Loading branch information
torvalds committed Jan 28, 2020
2 parents f0d8744 + da3a3da commit c899437
Show file tree
Hide file tree
Showing 10 changed files with 273 additions and 67 deletions.
47 changes: 45 additions & 2 deletions fs/ext4/verity.c
Original file line number Diff line number Diff line change
Expand Up @@ -342,12 +342,55 @@ static int ext4_get_verity_descriptor(struct inode *inode, void *buf,
return desc_size;
}

/*
* Prefetch some pages from the file's Merkle tree.
*
* This is basically a stripped-down version of __do_page_cache_readahead()
* which works on pages past i_size.
*/
static void ext4_merkle_tree_readahead(struct address_space *mapping,
pgoff_t start_index, unsigned long count)
{
LIST_HEAD(pages);
unsigned int nr_pages = 0;
struct page *page;
pgoff_t index;
struct blk_plug plug;

for (index = start_index; index < start_index + count; index++) {
page = xa_load(&mapping->i_pages, index);
if (!page || xa_is_value(page)) {
page = __page_cache_alloc(readahead_gfp_mask(mapping));
if (!page)
break;
page->index = index;
list_add(&page->lru, &pages);
nr_pages++;
}
}
blk_start_plug(&plug);
ext4_mpage_readpages(mapping, &pages, NULL, nr_pages, true);
blk_finish_plug(&plug);
}

static struct page *ext4_read_merkle_tree_page(struct inode *inode,
pgoff_t index)
pgoff_t index,
unsigned long num_ra_pages)
{
struct page *page;

index += ext4_verity_metadata_pos(inode) >> PAGE_SHIFT;

return read_mapping_page(inode->i_mapping, index, NULL);
page = find_get_page_flags(inode->i_mapping, index, FGP_ACCESSED);
if (!page || !PageUptodate(page)) {
if (page)
put_page(page);
else if (num_ra_pages > 1)
ext4_merkle_tree_readahead(inode->i_mapping, index,
num_ra_pages);
page = read_mapping_page(inode->i_mapping, index, NULL);
}
return page;
}

static int ext4_write_merkle_tree_block(struct inode *inode, const void *buf,
Expand Down
2 changes: 1 addition & 1 deletion fs/f2fs/data.c
Original file line number Diff line number Diff line change
Expand Up @@ -1881,7 +1881,7 @@ static int f2fs_read_single_page(struct inode *inode, struct page *page,
* use ->readpage() or do the necessary surgery to decouple ->readpages()
* from read-ahead.
*/
static int f2fs_mpage_readpages(struct address_space *mapping,
int f2fs_mpage_readpages(struct address_space *mapping,
struct list_head *pages, struct page *page,
unsigned nr_pages, bool is_readahead)
{
Expand Down
3 changes: 3 additions & 0 deletions fs/f2fs/f2fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -3229,6 +3229,9 @@ int f2fs_reserve_new_block(struct dnode_of_data *dn);
int f2fs_get_block(struct dnode_of_data *dn, pgoff_t index);
int f2fs_preallocate_blocks(struct kiocb *iocb, struct iov_iter *from);
int f2fs_reserve_block(struct dnode_of_data *dn, pgoff_t index);
int f2fs_mpage_readpages(struct address_space *mapping,
struct list_head *pages, struct page *page,
unsigned nr_pages, bool is_readahead);
struct page *f2fs_get_read_data_page(struct inode *inode, pgoff_t index,
int op_flags, bool for_write);
struct page *f2fs_find_data_page(struct inode *inode, pgoff_t index);
Expand Down
47 changes: 45 additions & 2 deletions fs/f2fs/verity.c
Original file line number Diff line number Diff line change
Expand Up @@ -222,12 +222,55 @@ static int f2fs_get_verity_descriptor(struct inode *inode, void *buf,
return size;
}

/*
* Prefetch some pages from the file's Merkle tree.
*
* This is basically a stripped-down version of __do_page_cache_readahead()
* which works on pages past i_size.
*/
static void f2fs_merkle_tree_readahead(struct address_space *mapping,
pgoff_t start_index, unsigned long count)
{
LIST_HEAD(pages);
unsigned int nr_pages = 0;
struct page *page;
pgoff_t index;
struct blk_plug plug;

for (index = start_index; index < start_index + count; index++) {
page = xa_load(&mapping->i_pages, index);
if (!page || xa_is_value(page)) {
page = __page_cache_alloc(readahead_gfp_mask(mapping));
if (!page)
break;
page->index = index;
list_add(&page->lru, &pages);
nr_pages++;
}
}
blk_start_plug(&plug);
f2fs_mpage_readpages(mapping, &pages, NULL, nr_pages, true);
blk_finish_plug(&plug);
}

static struct page *f2fs_read_merkle_tree_page(struct inode *inode,
pgoff_t index)
pgoff_t index,
unsigned long num_ra_pages)
{
struct page *page;

index += f2fs_verity_metadata_pos(inode) >> PAGE_SHIFT;

return read_mapping_page(inode->i_mapping, index, NULL);
page = find_get_page_flags(inode->i_mapping, index, FGP_ACCESSED);
if (!page || !PageUptodate(page)) {
if (page)
put_page(page);
else if (num_ra_pages > 1)
f2fs_merkle_tree_readahead(inode->i_mapping, index,
num_ra_pages);
page = read_mapping_page(inode->i_mapping, index, NULL);
}
return page;
}

static int f2fs_write_merkle_tree_block(struct inode *inode, const void *buf,
Expand Down
67 changes: 53 additions & 14 deletions fs/verity/enable.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,48 @@
#include "fsverity_private.h"

#include <crypto/hash.h>
#include <linux/backing-dev.h>
#include <linux/mount.h>
#include <linux/pagemap.h>
#include <linux/sched/signal.h>
#include <linux/uaccess.h>

static int build_merkle_tree_level(struct inode *inode, unsigned int level,
/*
* Read a file data page for Merkle tree construction. Do aggressive readahead,
* since we're sequentially reading the entire file.
*/
static struct page *read_file_data_page(struct file *filp, pgoff_t index,
struct file_ra_state *ra,
unsigned long remaining_pages)
{
struct page *page;

page = find_get_page_flags(filp->f_mapping, index, FGP_ACCESSED);
if (!page || !PageUptodate(page)) {
if (page)
put_page(page);
else
page_cache_sync_readahead(filp->f_mapping, ra, filp,
index, remaining_pages);
page = read_mapping_page(filp->f_mapping, index, NULL);
if (IS_ERR(page))
return page;
}
if (PageReadahead(page))
page_cache_async_readahead(filp->f_mapping, ra, filp, page,
index, remaining_pages);
return page;
}

static int build_merkle_tree_level(struct file *filp, unsigned int level,
u64 num_blocks_to_hash,
const struct merkle_tree_params *params,
u8 *pending_hashes,
struct ahash_request *req)
{
struct inode *inode = file_inode(filp);
const struct fsverity_operations *vops = inode->i_sb->s_vop;
struct file_ra_state ra = { 0 };
unsigned int pending_size = 0;
u64 dst_block_num;
u64 i;
Expand All @@ -36,6 +66,8 @@ static int build_merkle_tree_level(struct inode *inode, unsigned int level,
dst_block_num = 0; /* unused */
}

file_ra_state_init(&ra, filp->f_mapping);

for (i = 0; i < num_blocks_to_hash; i++) {
struct page *src_page;

Expand All @@ -45,7 +77,8 @@ static int build_merkle_tree_level(struct inode *inode, unsigned int level,

if (level == 0) {
/* Leaf: hashing a data block */
src_page = read_mapping_page(inode->i_mapping, i, NULL);
src_page = read_file_data_page(filp, i, &ra,
num_blocks_to_hash - i);
if (IS_ERR(src_page)) {
err = PTR_ERR(src_page);
fsverity_err(inode,
Expand All @@ -54,9 +87,14 @@ static int build_merkle_tree_level(struct inode *inode, unsigned int level,
return err;
}
} else {
unsigned long num_ra_pages =
min_t(unsigned long, num_blocks_to_hash - i,
inode->i_sb->s_bdi->io_pages);

/* Non-leaf: hashing hash block from level below */
src_page = vops->read_merkle_tree_page(inode,
params->level_start[level - 1] + i);
params->level_start[level - 1] + i,
num_ra_pages);
if (IS_ERR(src_page)) {
err = PTR_ERR(src_page);
fsverity_err(inode,
Expand Down Expand Up @@ -103,17 +141,18 @@ static int build_merkle_tree_level(struct inode *inode, unsigned int level,
}

/*
* Build the Merkle tree for the given inode using the given parameters, and
* Build the Merkle tree for the given file using the given parameters, and
* return the root hash in @root_hash.
*
* The tree is written to a filesystem-specific location as determined by the
* ->write_merkle_tree_block() method. However, the blocks that comprise the
* tree are the same for all filesystems.
*/
static int build_merkle_tree(struct inode *inode,
static int build_merkle_tree(struct file *filp,
const struct merkle_tree_params *params,
u8 *root_hash)
{
struct inode *inode = file_inode(filp);
u8 *pending_hashes;
struct ahash_request *req;
u64 blocks;
Expand All @@ -126,9 +165,11 @@ static int build_merkle_tree(struct inode *inode,
return 0;
}

/* This allocation never fails, since it's mempool-backed. */
req = fsverity_alloc_hash_request(params->hash_alg, GFP_KERNEL);

pending_hashes = kmalloc(params->block_size, GFP_KERNEL);
req = ahash_request_alloc(params->hash_alg->tfm, GFP_KERNEL);
if (!pending_hashes || !req)
if (!pending_hashes)
goto out;

/*
Expand All @@ -139,7 +180,7 @@ static int build_merkle_tree(struct inode *inode,
blocks = (inode->i_size + params->block_size - 1) >>
params->log_blocksize;
for (level = 0; level <= params->num_levels; level++) {
err = build_merkle_tree_level(inode, level, blocks, params,
err = build_merkle_tree_level(filp, level, blocks, params,
pending_hashes, req);
if (err)
goto out;
Expand All @@ -150,7 +191,7 @@ static int build_merkle_tree(struct inode *inode,
err = 0;
out:
kfree(pending_hashes);
ahash_request_free(req);
fsverity_free_hash_request(params->hash_alg, req);
return err;
}

Expand All @@ -175,8 +216,7 @@ static int enable_verity(struct file *filp,

/* Get the salt if the user provided one */
if (arg->salt_size &&
copy_from_user(desc->salt,
(const u8 __user *)(uintptr_t)arg->salt_ptr,
copy_from_user(desc->salt, u64_to_user_ptr(arg->salt_ptr),
arg->salt_size)) {
err = -EFAULT;
goto out;
Expand All @@ -185,8 +225,7 @@ static int enable_verity(struct file *filp,

/* Get the signature if the user provided one */
if (arg->sig_size &&
copy_from_user(desc->signature,
(const u8 __user *)(uintptr_t)arg->sig_ptr,
copy_from_user(desc->signature, u64_to_user_ptr(arg->sig_ptr),
arg->sig_size)) {
err = -EFAULT;
goto out;
Expand Down Expand Up @@ -227,7 +266,7 @@ static int enable_verity(struct file *filp,
*/
pr_debug("Building Merkle tree...\n");
BUILD_BUG_ON(sizeof(desc->root_hash) < FS_VERITY_MAX_DIGEST_SIZE);
err = build_merkle_tree(inode, &params, desc->root_hash);
err = build_merkle_tree(filp, &params, desc->root_hash);
if (err) {
fsverity_err(inode, "Error %d building Merkle tree", err);
goto rollback;
Expand Down
17 changes: 12 additions & 5 deletions fs/verity/fsverity_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

#include <crypto/sha.h>
#include <linux/fsverity.h>
#include <linux/mempool.h>

struct ahash_request;

Expand All @@ -37,11 +38,12 @@ struct fsverity_hash_alg {
const char *name; /* crypto API name, e.g. sha256 */
unsigned int digest_size; /* digest size in bytes, e.g. 32 for SHA-256 */
unsigned int block_size; /* block size in bytes, e.g. 64 for SHA-256 */
mempool_t req_pool; /* mempool with a preallocated hash request */
};

/* Merkle tree parameters: hash algorithm, initial hash state, and topology */
struct merkle_tree_params {
const struct fsverity_hash_alg *hash_alg; /* the hash algorithm */
struct fsverity_hash_alg *hash_alg; /* the hash algorithm */
const u8 *hashstate; /* initial hash state or NULL */
unsigned int digest_size; /* same as hash_alg->digest_size */
unsigned int block_size; /* size of data and tree blocks */
Expand All @@ -50,6 +52,7 @@ struct merkle_tree_params {
unsigned int log_arity; /* log2(hashes_per_block) */
unsigned int num_levels; /* number of levels in Merkle tree */
u64 tree_size; /* Merkle tree size in bytes */
unsigned long level0_blocks; /* number of blocks in tree level 0 */

/*
* Starting block index for each tree level, ordered from leaf level (0)
Expand Down Expand Up @@ -114,14 +117,18 @@ struct fsverity_signed_digest {

extern struct fsverity_hash_alg fsverity_hash_algs[];

const struct fsverity_hash_alg *fsverity_get_hash_alg(const struct inode *inode,
unsigned int num);
const u8 *fsverity_prepare_hash_state(const struct fsverity_hash_alg *alg,
struct fsverity_hash_alg *fsverity_get_hash_alg(const struct inode *inode,
unsigned int num);
struct ahash_request *fsverity_alloc_hash_request(struct fsverity_hash_alg *alg,
gfp_t gfp_flags);
void fsverity_free_hash_request(struct fsverity_hash_alg *alg,
struct ahash_request *req);
const u8 *fsverity_prepare_hash_state(struct fsverity_hash_alg *alg,
const u8 *salt, size_t salt_size);
int fsverity_hash_page(const struct merkle_tree_params *params,
const struct inode *inode,
struct ahash_request *req, struct page *page, u8 *out);
int fsverity_hash_buffer(const struct fsverity_hash_alg *alg,
int fsverity_hash_buffer(struct fsverity_hash_alg *alg,
const void *data, size_t size, u8 *out);
void __init fsverity_check_hash_algs(void);

Expand Down
Loading

0 comments on commit c899437

Please sign in to comment.