diff --git a/fs/verity/fsverity_private.h b/fs/verity/fsverity_private.h
index b01343113e8b..de8798f141d4 100644
--- a/fs/verity/fsverity_private.h
+++ b/fs/verity/fsverity_private.h
@@ -167,14 +167,15 @@ static inline bool fsverity_caches_blocks(const struct inode *inode)
 static inline bool fsverity_uses_bitmap(const struct fsverity_info *vi,
 					const struct inode *inode)
 {
+	if (fsverity_caches_blocks(inode))
+		return false;
+
 	/*
 	 * If fs uses block-based Merkle tree caching, then fs-verity must use
 	 * hash_block_verified bitmap as there's no page to mark it with
 	 * PG_checked.
 	 */
-	if (vi->tree_params.block_size != PAGE_SIZE)
-		return true;
-	return fsverity_caches_blocks(inode);
+	return vi->tree_params.block_size != PAGE_SIZE;
 }
 
 int fsverity_read_merkle_tree_block(struct inode *inode,
diff --git a/fs/verity/verify.c b/fs/verity/verify.c
index cd84182f5e43..a61d1c99c485 100644
--- a/fs/verity/verify.c
+++ b/fs/verity/verify.c
@@ -26,12 +26,11 @@ static bool is_hash_block_verified(struct inode *inode,
 	struct page *hpage;
 
 	/*
-	 * If the filesystem uses block-based caching, then
-	 * ->hash_block_verified is always used and the filesystem pushes
-	 * invalidations to it as needed.
+	 * If the filesystem uses block-based caching, then rely on the
+	 * implementation to retain verified status.
 	 */
 	if (fsverity_caches_blocks(inode))
-		return test_bit(hblock_idx, vi->hash_block_verified);
+		return block->verified;
 
 	/* Otherwise, the filesystem uses page-based caching. */
 	hpage = (struct page *)block->context;
@@ -224,7 +223,9 @@ verify_data_block(struct inode *inode, struct fsverity_info *vi,
 		 * idempotent, as the same hash block might be verified by
 		 * multiple threads concurrently.
 		 */
-		if (fsverity_uses_bitmap(vi, inode))
+		if (fsverity_caches_blocks(inode))
+			block->verified = true;
+		else if (fsverity_uses_bitmap(vi, inode))
 			set_bit(hblock_idx, vi->hash_block_verified);
 		else
 			SetPageChecked((struct page *)block->context);
@@ -375,33 +376,6 @@ void __init fsverity_init_workqueue(void)
 		panic("failed to allocate fsverity_read_queue");
 }
 
-/**
- * fsverity_invalidate_block() - invalidate Merkle tree block
- * @inode: inode to which this Merkle tree blocks belong
- * @block: block to be invalidated
- *
- * This function invalidates/clears "verified" state of Merkle tree block
- * in the fs-verity bitmap. The block needs to have ->offset set.
- */
-void fsverity_invalidate_block(struct inode *inode,
-		struct fsverity_blockbuf *block)
-{
-	struct fsverity_info *vi = inode->i_verity_info;
-	const unsigned int log_blocksize = vi->tree_params.log_blocksize;
-
-	trace_fsverity_invalidate_block(inode, block);
-
-	if (block->offset >= vi->tree_params.tree_size) {
-		fsverity_err(inode,
-"Trying to invalidate beyond Merkle tree (tree %lld, offset %lld)",
-			     vi->tree_params.tree_size, block->offset);
-		return;
-	}
-
-	clear_bit(block->offset >> log_blocksize, vi->hash_block_verified);
-}
-EXPORT_SYMBOL_GPL(fsverity_invalidate_block);
-
 /**
  * fsverity_read_merkle_tree_block() - read Merkle tree block
  * @inode: inode to which this Merkle tree blocks belong
@@ -436,6 +410,7 @@ int fsverity_read_merkle_tree_block(struct inode *inode,
 			.log_blocksize = params->log_blocksize,
 			.ra_bytes = ra_bytes,
 		};
+		block->verified = false;
 
 		return vops->read_merkle_tree_block(&req, block);
 	}
diff --git a/include/linux/fsverity.h b/include/linux/fsverity.h
index 17bc0729119c..026e4f72290e 100644
--- a/include/linux/fsverity.h
+++ b/include/linux/fsverity.h
@@ -32,6 +32,7 @@
  * @offset: block's offset into Merkle tree
  * @size: the Merkle tree block size
  * @context: filesystem private context
+ * @verified: has this buffer been validated?
  *
  * Buffer containing single Merkle Tree block. These buffers are passed
  *  - to filesystem, when fs-verity is building merkel tree,
@@ -49,6 +50,7 @@
 struct fsverity_blockbuf {
 	void *kaddr;
 	u64 offset;
+	unsigned int verified:1;
 	unsigned int size;
 	void *context;
 };
@@ -168,9 +170,9 @@ struct fsverity_operations {
 	 * This can be called at any time on an open verity file.  It may be
 	 * called by multiple processes concurrently.
 	 *
-	 * In case that block was evicted from the memory filesystem has to use
-	 * fsverity_invalidate_block() to let fsverity know that block's
-	 * verification state is not valid anymore.
+	 * Implementations may cache the @block->verified state in
+	 * ->drop_merkle_tree_block.  They must clear the @block->verified
+	 * flag for a cache miss.
 	 *
 	 * If this function is implemented, ->drop_merkle_tree_block must also
 	 * be implemented.
@@ -204,6 +206,9 @@ struct fsverity_operations {
 	 * This is called when fs-verity is done with a block obtained with
 	 * ->read_merkle_tree_block().
 	 *
+	 * Implementations should cache a @block->verified==1 state to avoid
+	 * unnecessary revalidations during later accesses.
+	 *
 	 * If this function is implemented, ->read_merkle_tree_block must also
 	 * be implemented.
 	 */
@@ -264,8 +269,6 @@ int fsverity_ioctl_read_metadata(struct file *filp, const void __user *uarg);
 bool fsverity_verify_blocks(struct folio *folio, size_t len, size_t offset);
 void fsverity_verify_bio(struct bio *bio);
 void fsverity_enqueue_verify_work(struct work_struct *work);
-void fsverity_invalidate_block(struct inode *inode,
-		struct fsverity_blockbuf *block);
 
 static inline int fsverity_set_ops(struct super_block *sb,
 				   const struct fsverity_operations *ops)
diff --git a/include/trace/events/fsverity.h b/include/trace/events/fsverity.h
index 763890e47358..1a6ee2a2c3ce 100644
--- a/include/trace/events/fsverity.h
+++ b/include/trace/events/fsverity.h
@@ -109,25 +109,6 @@ TRACE_EVENT(fsverity_merkle_tree_block_verified,
 		__entry->direction == 0 ? "ascend" : "descend")
 );
 
-TRACE_EVENT(fsverity_invalidate_block,
-	TP_PROTO(struct inode *inode, struct fsverity_blockbuf *block),
-	TP_ARGS(inode, block),
-	TP_STRUCT__entry(
-		__field(ino_t, ino)
-		__field(u64, offset)
-		__field(unsigned int, block_size)
-	),
-	TP_fast_assign(
-		__entry->ino = inode->i_ino;
-		__entry->offset = block->offset;
-		__entry->block_size = block->size;
-	),
-	TP_printk("ino %lu block position %llu block size %u",
-		(unsigned long) __entry->ino,
-		__entry->offset,
-		__entry->block_size)
-);
-
 TRACE_EVENT(fsverity_read_merkle_tree_block,
 	TP_PROTO(struct inode *inode, u64 offset, unsigned int log_blocksize),
 	TP_ARGS(inode, offset, log_blocksize),
