[PATCH 06/13] fsverity: send the level of the merkle tree block to ->read_merkle_tree_block

Darrick J. Wong
2024-03-30T00:34:15Z

From: Darrick J. Wong <djwong@kernel.org>

When fsverity needs to pull in a merkle tree block for file data
verification, it knows the level of the block within the tree. For XFS,
we will cache the blocks in memory ourselves, and it is advantageous to
make higher level nodes more resistant to memory reclamation.

Therefore, we need to pass the anticipated level to the
->read_merkle_tree_block functions to enable this kind of caching.
Establish level == -1 to mean streaming read (e.g. downloading the
merkle tree).

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
fs/verity/fsverity_private.h | 2 +-
fs/verity/read_metadata.c | 2 +-
fs/verity/verify.c | 22 +++++++++++++++++-----
include/linux/fsverity.h | 32 ++++++++++++++++++++++----------
4 files changed, 41 insertions(+), 17 deletions(-)

diff --git a/fs/verity/fsverity_private.h b/fs/verity/fsverity_private.h
index d4a9178f9e827..de8798f141d4a 100644
--- a/fs/verity/fsverity_private.h
+++ b/fs/verity/fsverity_private.h
@@ -180,7 +180,7 @@ static inline bool fsverity_uses_bitmap(const struct fsverity_info *vi,

int fsverity_read_merkle_tree_block(struct inode *inode,
const struct merkle_tree_params *params,
- u64 pos, unsigned long ra_bytes,
+ int level, u64 pos, unsigned long ra_bytes,
struct fsverity_blockbuf *block);

/*
diff --git a/fs/verity/read_metadata.c b/fs/verity/read_metadata.c
index 94fffa060f829..87cc6f2896633 100644
--- a/fs/verity/read_metadata.c
+++ b/fs/verity/read_metadata.c
@@ -43,7 +43,7 @@ static int fsverity_read_merkle_tree(struct inode *inode,
params->block_size - offs_in_block);

err = fsverity_read_merkle_tree_block(inode, &vi->tree_params,
- pos - offs_in_block, ra_bytes, &block);
+ -1, pos - offs_in_block, ra_bytes, &block);
if (err) {
fsverity_err(inode,
"Error %d reading Merkle tree block %llu",
diff --git a/fs/verity/verify.c b/fs/verity/verify.c
index 85d8d2fcce9ab..c4ebf85ba2c79 100644
--- a/fs/verity/verify.c
+++ b/fs/verity/verify.c
@@ -185,8 +185,8 @@ verify_data_block(struct inode *inode, struct fsverity_info *vi,
else
ra_bytes = 0;

- err = fsverity_read_merkle_tree_block(inode, params, hblock_pos,
- ra_bytes, block);
+ err = fsverity_read_merkle_tree_block(inode, params, level,
+ hblock_pos, ra_bytes, block);
if (err) {
fsverity_err(inode,
"Error %d reading Merkle tree block %llu",
@@ -403,6 +403,8 @@ void __init fsverity_init_workqueue(void)
* fsverity_read_merkle_tree_block() - read Merkle tree block
* @inode: inode to which this Merkle tree blocks belong
* @params: merkle tree parameters
+ * @level: expected level of the block; level 0 are the leaves, -1 means a
+ * streaming read
* @pos: byte position within merkle tree
* @ra_bytes: try to read ahead this many btes
* @block: block to be loaded
@@ -411,7 +413,7 @@ void __init fsverity_init_workqueue(void)
*/
int fsverity_read_merkle_tree_block(struct inode *inode,
const struct merkle_tree_params *params,
- u64 pos, unsigned long ra_bytes,
+ int level, u64 pos, unsigned long ra_bytes,
struct fsverity_blockbuf *block)
{
const struct fsverity_operations *vops = inode->i_sb->s_vop;
@@ -420,10 +422,20 @@ int fsverity_read_merkle_tree_block(struct inode *inode,
unsigned long index;
unsigned int offset_in_page;

+ block->offset = pos;
+ block->size = params->block_size;
+
if (fsverity_caches_blocks(inode)) {
+ struct fsverity_readmerkle req = {
+ .inode = inode,
+ .level = level,
+ .num_levels = params->num_levels,
+ .log_blocksize = params->log_blocksize,
+ .ra_bytes = ra_bytes,
+ };
+
block->verified = false;
- return vops->read_merkle_tree_block(inode, pos, ra_bytes,
- params->log_blocksize, block);
+ return vops->read_merkle_tree_block(&req, block);
}

index = pos >> params->log_blocksize;
diff --git a/include/linux/fsverity.h b/include/linux/fsverity.h
index 495708fb1f26a..52de58d6f021f 100644
--- a/include/linux/fsverity.h
+++ b/include/linux/fsverity.h
@@ -55,6 +55,26 @@ struct fsverity_blockbuf {
void *context;
};

+/**
+ * struct fsverity_readmerkle - Request to read a Merkle Tree block buffer
+ * @inode: the inode to read
+ * @level: expected level of the block; level 0 are the leaves, -1 means a
+ * streaming read
+ * @num_levels: number of levels in the tree total
+ * @log_blocksize: log2 of the size of the expected block
+ * @ra_bytes: The number of bytes that should be prefetched starting at pos
+ * if the page at @block->offset isn't already cached.
+ * Implementations may ignore this argument; it's only a
+ * performance optimization.
+ */
+struct fsverity_readmerkle {
+ struct inode *inode;
+ unsigned long ra_bytes;
+ int level;
+ int num_levels;
+ u8 log_blocksize;
+};
+
/* Verity operations for filesystems */
struct fsverity_operations {

@@ -141,13 +161,7 @@ struct fsverity_operations {

/**
* Read a Merkle tree block of the given inode.
- * @inode: the inode
- * @pos: byte offset of the block within the Merkle tree
- * @ra_bytes: The number of bytes that should be
- * prefetched starting at @pos if the page at @pos
- * isn't already cached. Implementations may ignore this
- * argument; it's only a performance optimization.
- * @log_blocksize: log2 of the size of the expected block
+ * @req: read request; see struct fsverity_readmerkle
* @block: block buffer for filesystem to point it to the block
*
* This can be called at any time on an open verity file. It may be
@@ -162,9 +176,7 @@ struct fsverity_operations {
*
* Return: 0 on success, -errno on failure
*/
- int (*read_merkle_tree_block)(struct inode *inode,
- u64 pos, unsigned long ra_bytes,
- unsigned int log_blocksize,
+ int (*read_merkle_tree_block)(const struct fsverity_readmerkle *req,
struct fsverity_blockbuf *block);

/**