fsverity.lists.linux.dev archive mirror
 help / color / mirror / Atom feed
From: Andrey Albershteyn <aalbersh@redhat.com>
To: fsverity@lists.linux.dev, linux-xfs@vger.kernel.org,
	linux-fsdevel@vger.kernel.org, chandan.babu@oracle.com,
	djwong@kernel.org, ebiggers@kernel.org
Cc: Andrey Albershteyn <aalbersh@redhat.com>,
	Eric Biggers <ebiggers@google.com>
Subject: [PATCH v4 01/25] fsverity: remove hash page spin lock
Date: Mon, 12 Feb 2024 17:57:58 +0100	[thread overview]
Message-ID: <20240212165821.1901300-2-aalbersh@redhat.com> (raw)
In-Reply-To: <20240212165821.1901300-1-aalbersh@redhat.com>

The spin lock is not necessary here as it can be replaced with
memory barrier which should be better performance-wise.

When Merkle tree block size differs from page size, in
is_hash_block_verified() two things are modified during check - a
bitmap and PG_checked flag of the page.

Each bit in the bitmap represent verification status of the Merkle
tree blocks. PG_checked flag tells if page was just re-instantiated
or was in pagecache. Both of this states are shared between
verification threads. Page which was re-instantiated can not have
already verified blocks (bit set in bitmap).

The spin lock was used to allow only one thread to modify both of
these states and keep order of operations. The only requirement here
is that PG_Checked is set strictly after bitmap is updated.
This way other threads which see that PG_Checked=1 (page cached)
knows that bitmap is up-to-date. Otherwise, if PG_Checked is set
before bitmap is cleared, other threads can see bit=1 and therefore
will not perform verification of that Merkle tree block.

However, there's still the case when one thread is setting a bit in
verify_data_block() and other thread is clearing it in
is_hash_block_verified(). This can happen if two threads get to
!PageChecked branch and one of the threads is rescheduled before
resetting the bitmap. This is fine as at worst blocks are
re-verified in each thread.

Signed-off-by: Eric Biggers <ebiggers@google.com>
Reviewed-by: Andrey Albershteyn <aalbersh@redhat.com>
Signed-off-by: Andrey Albershteyn <aalbersh@redhat.com>
---
 fs/verity/fsverity_private.h |  1 -
 fs/verity/open.c             |  1 -
 fs/verity/verify.c           | 48 ++++++++++++++++++------------------
 3 files changed, 24 insertions(+), 26 deletions(-)

diff --git a/fs/verity/fsverity_private.h b/fs/verity/fsverity_private.h
index a6a6b2749241..b3506f56e180 100644
--- a/fs/verity/fsverity_private.h
+++ b/fs/verity/fsverity_private.h
@@ -69,7 +69,6 @@ struct fsverity_info {
 	u8 file_digest[FS_VERITY_MAX_DIGEST_SIZE];
 	const struct inode *inode;
 	unsigned long *hash_block_verified;
-	spinlock_t hash_page_init_lock;
 };
 
 #define FS_VERITY_MAX_SIGNATURE_SIZE	(FS_VERITY_MAX_DESCRIPTOR_SIZE - \
diff --git a/fs/verity/open.c b/fs/verity/open.c
index 6c31a871b84b..fdeb95eca3af 100644
--- a/fs/verity/open.c
+++ b/fs/verity/open.c
@@ -239,7 +239,6 @@ struct fsverity_info *fsverity_create_info(const struct inode *inode,
 			err = -ENOMEM;
 			goto fail;
 		}
-		spin_lock_init(&vi->hash_page_init_lock);
 	}
 
 	return vi;
diff --git a/fs/verity/verify.c b/fs/verity/verify.c
index 904ccd7e8e16..4fcad0825a12 100644
--- a/fs/verity/verify.c
+++ b/fs/verity/verify.c
@@ -19,7 +19,6 @@ static struct workqueue_struct *fsverity_read_workqueue;
 static bool is_hash_block_verified(struct fsverity_info *vi, struct page *hpage,
 				   unsigned long hblock_idx)
 {
-	bool verified;
 	unsigned int blocks_per_page;
 	unsigned int i;
 
@@ -43,12 +42,20 @@ static bool is_hash_block_verified(struct fsverity_info *vi, struct page *hpage,
 	 * re-instantiated from the backing storage are re-verified.  To do
 	 * this, we use PG_checked again, but now it doesn't really mean
 	 * "checked".  Instead, now it just serves as an indicator for whether
-	 * the hash page is newly instantiated or not.
+	 * the hash page is newly instantiated or not.  If the page is new, as
+	 * indicated by PG_checked=0, we clear the bitmap bits for the page's
+	 * blocks since they are untrustworthy, then set PG_checked=1.
+	 * Otherwise we return the bitmap bit for the requested block.
 	 *
-	 * The first thread that sees PG_checked=0 must clear the corresponding
-	 * bitmap bits, then set PG_checked=1.  This requires a spinlock.  To
-	 * avoid having to take this spinlock in the common case of
-	 * PG_checked=1, we start with an opportunistic lockless read.
+	 * Multiple threads may execute this code concurrently on the same page.
+	 * This is safe because we use memory barriers to ensure that if a
+	 * thread sees PG_checked=1, then it also sees the associated bitmap
+	 * clearing to have occurred.  Also, all writes and their corresponding
+	 * reads are atomic, and all writes are safe to repeat in the event that
+	 * multiple threads get into the PG_checked=0 section.  (Clearing a
+	 * bitmap bit again at worst causes a hash block to be verified
+	 * redundantly.  That event should be very rare, so it's not worth using
+	 * a lock to avoid.  Setting PG_checked again has no effect.)
 	 */
 	if (PageChecked(hpage)) {
 		/*
@@ -58,24 +65,17 @@ static bool is_hash_block_verified(struct fsverity_info *vi, struct page *hpage,
 		smp_rmb();
 		return test_bit(hblock_idx, vi->hash_block_verified);
 	}
-	spin_lock(&vi->hash_page_init_lock);
-	if (PageChecked(hpage)) {
-		verified = test_bit(hblock_idx, vi->hash_block_verified);
-	} else {
-		blocks_per_page = vi->tree_params.blocks_per_page;
-		hblock_idx = round_down(hblock_idx, blocks_per_page);
-		for (i = 0; i < blocks_per_page; i++)
-			clear_bit(hblock_idx + i, vi->hash_block_verified);
-		/*
-		 * A write memory barrier is needed here to give RELEASE
-		 * semantics to the below SetPageChecked() operation.
-		 */
-		smp_wmb();
-		SetPageChecked(hpage);
-		verified = false;
-	}
-	spin_unlock(&vi->hash_page_init_lock);
-	return verified;
+	blocks_per_page = vi->tree_params.blocks_per_page;
+	hblock_idx = round_down(hblock_idx, blocks_per_page);
+	for (i = 0; i < blocks_per_page; i++)
+		clear_bit(hblock_idx + i, vi->hash_block_verified);
+	/*
+	 * A write memory barrier is needed here to give RELEASE semantics to
+	 * the below SetPageChecked() operation.
+	 */
+	smp_wmb();
+	SetPageChecked(hpage);
+	return false;
 }
 
 /*
-- 
2.42.0


  reply	other threads:[~2024-02-12 16:59 UTC|newest]

Thread overview: 44+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-02-12 16:57 [PATCH v4 00/25] fs-verity support for XFS Andrey Albershteyn
2024-02-12 16:57 ` Andrey Albershteyn [this message]
2024-02-12 16:57 ` [PATCH v4 02/25] xfs: add parent pointer support to attribute code Andrey Albershteyn
2024-02-12 16:58 ` [PATCH v4 03/25] xfs: define parent pointer ondisk extended attribute format Andrey Albershteyn
2024-02-12 16:58 ` [PATCH v4 04/25] xfs: add parent pointer validator functions Andrey Albershteyn
2024-02-12 16:58 ` [PATCH v4 05/25] fs: add FS_XFLAG_VERITY for verity files Andrey Albershteyn
2024-02-23  4:23   ` Eric Biggers
2024-02-23 12:55     ` Andrey Albershteyn
2024-02-23 17:59       ` Eric Biggers
2024-02-12 16:58 ` [PATCH v4 06/25] fsverity: pass log_blocksize to end_enable_verity() Andrey Albershteyn
2024-02-15 21:45   ` Dave Chinner
2024-02-16 16:18     ` Andrey Albershteyn
2024-02-23  4:26   ` Eric Biggers
2024-02-23 13:02     ` Andrey Albershteyn
2024-02-12 16:58 ` [PATCH v4 07/25] fsverity: support block-based Merkle tree caching Andrey Albershteyn
2024-02-23  5:24   ` Eric Biggers
2024-02-23 16:02     ` Andrey Albershteyn
2024-02-23 18:07       ` Eric Biggers
2024-02-24 14:10         ` Andrey Albershteyn
2024-02-12 16:58 ` [PATCH v4 08/25] fsverity: calculate readahead in bytes instead of pages Andrey Albershteyn
2024-02-23  5:29   ` Eric Biggers
2024-02-12 16:58 ` [PATCH v4 09/25] fsverity: add tracepoints Andrey Albershteyn
2024-02-23  5:31   ` Eric Biggers
2024-02-23 13:23     ` Andrey Albershteyn
2024-02-23 18:27       ` Eric Biggers
2024-02-26  2:24         ` Dave Chinner
2024-02-12 16:58 ` [PATCH v4 10/25] iomap: integrate fsverity verification into iomap's read path Andrey Albershteyn
2024-02-12 16:58 ` [PATCH v4 11/25] xfs: add XBF_VERITY_SEEN xfs_buf flag Andrey Albershteyn
2024-02-12 16:58 ` [PATCH v4 12/25] xfs: add XFS_DA_OP_BUFFER to make xfs_attr_get() return buffer Andrey Albershteyn
2024-02-12 16:58 ` [PATCH v4 13/25] xfs: introduce workqueue for post read IO work Andrey Albershteyn
2024-02-15 22:11   ` Dave Chinner
2024-02-16 16:29     ` Andrey Albershteyn
2024-02-12 16:58 ` [PATCH v4 14/25] xfs: add attribute type for fs-verity Andrey Albershteyn
2024-02-12 16:58 ` [PATCH v4 15/25] xfs: make xfs_buf_get() to take XBF_* flags Andrey Albershteyn
2024-02-12 16:58 ` [PATCH v4 16/25] xfs: add XBF_DOUBLE_ALLOC to increase size of the buffer Andrey Albershteyn
2024-02-12 16:58 ` [PATCH v4 17/25] xfs: add fs-verity ro-compat flag Andrey Albershteyn
2024-02-12 16:58 ` [PATCH v4 18/25] xfs: add inode on-disk VERITY flag Andrey Albershteyn
2024-02-12 16:58 ` [PATCH v4 19/25] xfs: initialize fs-verity on file open and cleanup on inode destruction Andrey Albershteyn
2024-02-12 16:58 ` [PATCH v4 20/25] xfs: don't allow to enable DAX on fs-verity sealsed inode Andrey Albershteyn
2024-02-12 16:58 ` [PATCH v4 21/25] xfs: disable direct read path for fs-verity files Andrey Albershteyn
2024-02-12 16:58 ` [PATCH v4 22/25] xfs: add fs-verity support Andrey Albershteyn
2024-02-12 16:58 ` [PATCH v4 23/25] xfs: make scrub aware of verity dinode flag Andrey Albershteyn
2024-02-12 16:58 ` [PATCH v4 24/25] xfs: add fs-verity ioctls Andrey Albershteyn
2024-02-12 16:58 ` [PATCH v4 25/25] xfs: enable ro-compat fs-verity flag Andrey Albershteyn

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20240212165821.1901300-2-aalbersh@redhat.com \
    --to=aalbersh@redhat.com \
    --cc=chandan.babu@oracle.com \
    --cc=djwong@kernel.org \
    --cc=ebiggers@google.com \
    --cc=ebiggers@kernel.org \
    --cc=fsverity@lists.linux.dev \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-xfs@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).