[PATCH RFC 04/29] fsverity: add per-sb workqueue for post read processing

Andrey Albershteyn
2025-07-28T20:31:29Z

From: Andrey Albershteyn <aalbersh@redhat.com>

For XFS, fsverity's global workqueue is not really suitable due to:

1. High priority workqueues are used within XFS to ensure that data
IO completion cannot stall processing of journal IO completions.
Hence using a WQ_HIGHPRI workqueue directly in the user data IO
path is a potential filesystem livelock/deadlock vector.

2. The fsverity workqueue is global - it creates a cross-filesystem
contention point.

This patch adds per-filesystem, per-cpu workqueue for fsverity
work. This allows iomap to add verification work in the read path on
BIO completion.

Signed-off-by: Andrey Albershteyn <aalbersh@redhat.com>
[djwong: make it clearer that this workqueue is for verity]
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
fs/super.c | 3 +++
fs/verity/verify.c | 14 ++++++++++++++
include/linux/fs.h | 2 ++
include/linux/fsverity.h | 18 ++++++++++++++++++
4 files changed, 37 insertions(+)

diff --git a/fs/super.c b/fs/super.c
index 80418ca8e215..f80fe7395228 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -37,6 +37,7 @@
#include <linux/user_namespace.h>
#include <linux/fs_context.h>
#include <uapi/linux/mount.h>
+#include <linux/fsverity.h>
#include "internal.h"

static int thaw_super_locked(struct super_block *sb, enum freeze_holder who,
@@ -639,6 +640,8 @@ void generic_shutdown_super(struct super_block *sb)
sb->s_dio_done_wq = NULL;
}

+ fsverity_destroy_wq(sb);
+
if (sop->put_super)
sop->put_super(sb);

diff --git a/fs/verity/verify.c b/fs/verity/verify.c
index 4fcad0825a12..30a3f6ada2ad 100644
--- a/fs/verity/verify.c
+++ b/fs/verity/verify.c
@@ -334,6 +334,20 @@ void fsverity_verify_bio(struct bio *bio)
EXPORT_SYMBOL_GPL(fsverity_verify_bio);
#endif /* CONFIG_BLOCK */

+int fsverity_init_wq(struct super_block *sb, unsigned int wq_flags,
+ int max_active)
+{
+ WARN_ON_ONCE(sb->s_verity_wq != NULL);
+
+ sb->s_verity_wq = alloc_workqueue("fsverity/%s", wq_flags, max_active,
+ sb->s_id);
+ if (!sb->s_verity_wq)
+ return -ENOMEM;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(fsverity_init_wq);
+
/**
* fsverity_enqueue_verify_work() - enqueue work on the fs-verity workqueue
* @work: the work to enqueue
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 040c0036320f..abe31e9959fa 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1350,6 +1350,8 @@ struct super_block {
#endif
#ifdef CONFIG_FS_VERITY
const struct fsverity_operations *s_vop;
+ /* Completion queue for post read verification */
+ struct workqueue_struct *s_verity_wq;
#endif
#if IS_ENABLED(CONFIG_UNICODE)
struct unicode_map *s_encoding;
diff --git a/include/linux/fsverity.h b/include/linux/fsverity.h
index 1eb7eae580be..9b91bd54fb75 100644
--- a/include/linux/fsverity.h
+++ b/include/linux/fsverity.h
@@ -174,6 +174,17 @@ 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);

+int fsverity_init_wq(struct super_block *sb, unsigned int wq_flags,
+ int max_active);
+
+static inline void fsverity_destroy_wq(struct super_block *sb)
+{
+ if (sb->s_verity_wq) {
+ destroy_workqueue(sb->s_verity_wq);
+ sb->s_verity_wq = NULL;
+ }
+}
+
#else /* !CONFIG_FS_VERITY */

static inline struct fsverity_info *fsverity_get_info(const struct inode *inode)
@@ -251,6 +262,13 @@ static inline void fsverity_enqueue_verify_work(struct work_struct *work)
WARN_ON_ONCE(1);
}

+static inline int fsverity_init_wq(struct super_block *sb)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline void fsverity_destroy_wq(struct super_block *sb) { }
+
#endif /* !CONFIG_FS_VERITY */

static inline bool fsverity_verify_folio(struct folio *folio)

--
2.50.0