diff --git a/fs/verity/Makefile b/fs/verity/Makefile
index 435559a4fa9e..81a468ca0131 100644
--- a/fs/verity/Makefile
+++ b/fs/verity/Makefile
@@ -9,3 +9,5 @@ obj-$(CONFIG_FS_VERITY) += enable.o \
 			   verify.o
 
 obj-$(CONFIG_FS_VERITY_BUILTIN_SIGNATURES) += signature.o
+
+obj-$(CONFIG_SYSCTL) += sysctl.o
diff --git a/fs/verity/enable.c b/fs/verity/enable.c
index 77e159a0346b..f7d2e1ed4b69 100644
--- a/fs/verity/enable.c
+++ b/fs/verity/enable.c
@@ -14,6 +14,8 @@
 #include <linux/sched/signal.h>
 #include <linux/uaccess.h>
 
+extern int fsverity_enable;
+
 /*
  * Read a file data page for Merkle tree construction.  Do aggressive readahead,
  * since we're sequentially reading the entire file.
@@ -343,6 +345,9 @@ int fsverity_ioctl_enable(struct file *filp, const void __user *uarg)
 	struct fsverity_enable_arg arg;
 	int err;
 
+	if (!fsverity_enable)
+		return -EOPNOTSUPP;
+
 	if (copy_from_user(&arg, uarg, sizeof(arg)))
 		return -EFAULT;
 
diff --git a/fs/verity/fsverity_private.h b/fs/verity/fsverity_private.h
index a7920434bae5..43604b80a562 100644
--- a/fs/verity/fsverity_private.h
+++ b/fs/verity/fsverity_private.h
@@ -136,6 +136,21 @@ int fsverity_get_descriptor(struct inode *inode,
 int __init fsverity_init_info_cache(void);
 void __init fsverity_exit_info_cache(void);
 
+/* sysctl.c */
+#ifdef CONFIG_SYSCTL
+int __init fsverity_sysctl_init(void);
+void __init fsverity_exit_sysctl(void);
+#else /* !CONFIG_SYSCTL */
+static inline int __init fsverity_sysctl_init(void)
+{
+	return 0;
+}
+static inline void __init fsverity_exit_sysctl(void)
+{
+	return;
+}
+#endif /* !CONFIG_SYSCTL */
+
 /* signature.c */
 
 #ifdef CONFIG_FS_VERITY_BUILTIN_SIGNATURES
diff --git a/fs/verity/init.c b/fs/verity/init.c
index c98b7016f446..bd16495e8adf 100644
--- a/fs/verity/init.c
+++ b/fs/verity/init.c
@@ -45,13 +45,18 @@ static int __init fsverity_init(void)
 	if (err)
 		goto err_exit_info_cache;
 
-	err = fsverity_init_signature();
+	err = fsverity_sysctl_init();
 	if (err)
 		goto err_exit_workqueue;
+	err = fsverity_init_signature();
+	if (err)
+		goto err_exit_sysctl;
 
 	pr_debug("Initialized fs-verity\n");
 	return 0;
 
+err_exit_sysctl:
+	fsverity_exit_sysctl();
 err_exit_workqueue:
 	fsverity_exit_workqueue();
 err_exit_info_cache:
diff --git a/fs/verity/measure.c b/fs/verity/measure.c
index f0d7b30c62db..789d0a8672f9 100644
--- a/fs/verity/measure.c
+++ b/fs/verity/measure.c
@@ -9,6 +9,8 @@
 
 #include <linux/uaccess.h>
 
+extern int fsverity_enable;
+
 /**
  * fsverity_ioctl_measure() - get a verity file's digest
  * @filp: file to get digest of
@@ -28,6 +30,9 @@ int fsverity_ioctl_measure(struct file *filp, void __user *_uarg)
 	const struct fsverity_hash_alg *hash_alg;
 	struct fsverity_digest arg;
 
+	if (!fsverity_enable)
+		return -EOPNOTSUPP;
+
 	vi = fsverity_get_info(inode);
 	if (!vi)
 		return -ENODATA; /* not a verity file */
diff --git a/fs/verity/open.c b/fs/verity/open.c
index 60ff8af7219f..8230def90017 100644
--- a/fs/verity/open.c
+++ b/fs/verity/open.c
@@ -10,6 +10,7 @@
 #include <linux/slab.h>
 
 static struct kmem_cache *fsverity_info_cachep;
+extern int fsverity_enable;
 
 /**
  * fsverity_init_merkle_tree_params() - initialize Merkle tree parameters
@@ -344,7 +345,7 @@ static int ensure_verity_info(struct inode *inode)
  */
 int fsverity_file_open(struct inode *inode, struct file *filp)
 {
-	if (!IS_VERITY(inode))
+	if (!IS_VERITY(inode) || !fsverity_enable)
 		return 0;
 
 	if (filp->f_mode & FMODE_WRITE) {
diff --git a/fs/verity/read_metadata.c b/fs/verity/read_metadata.c
index 7e2d0c7bdf0d..d262e26086f5 100644
--- a/fs/verity/read_metadata.c
+++ b/fs/verity/read_metadata.c
@@ -12,6 +12,8 @@
 #include <linux/sched/signal.h>
 #include <linux/uaccess.h>
 
+extern int fsverity_enable;
+
 static int fsverity_read_merkle_tree(struct inode *inode,
 				     const struct fsverity_info *vi,
 				     void __user *buf, u64 offset, int length)
@@ -157,6 +159,9 @@ int fsverity_ioctl_read_metadata(struct file *filp, const void __user *uarg)
 	int length;
 	void __user *buf;
 
+	if (!fsverity_enable)
+		return -EOPNOTSUPP;
+
 	vi = fsverity_get_info(inode);
 	if (!vi)
 		return -ENODATA; /* not a verity file */
diff --git a/fs/verity/signature.c b/fs/verity/signature.c
index 143a530a8008..2b1280c66d21 100644
--- a/fs/verity/signature.c
+++ b/fs/verity/signature.c
@@ -12,11 +12,7 @@
 #include <linux/slab.h>
 #include <linux/verification.h>
 
-/*
- * /proc/sys/fs/verity/require_signatures
- * If 1, all verity files must have a valid builtin signature.
- */
-static int fsverity_require_signatures;
+extern int fsverity_require_signatures;
 
 /*
  * Keyring that contains the trusted X.509 certificates.
@@ -87,45 +83,6 @@ int fsverity_verify_signature(const struct fsverity_info *vi,
 	return 0;
 }
 
-#ifdef CONFIG_SYSCTL
-static struct ctl_table_header *fsverity_sysctl_header;
-
-static const struct ctl_path fsverity_sysctl_path[] = {
-	{ .procname = "fs", },
-	{ .procname = "verity", },
-	{ }
-};
-
-static struct ctl_table fsverity_sysctl_table[] = {
-	{
-		.procname       = "require_signatures",
-		.data           = &fsverity_require_signatures,
-		.maxlen         = sizeof(int),
-		.mode           = 0644,
-		.proc_handler   = proc_dointvec_minmax,
-		.extra1         = SYSCTL_ZERO,
-		.extra2         = SYSCTL_ONE,
-	},
-	{ }
-};
-
-static int __init fsverity_sysctl_init(void)
-{
-	fsverity_sysctl_header = register_sysctl_paths(fsverity_sysctl_path,
-						       fsverity_sysctl_table);
-	if (!fsverity_sysctl_header) {
-		pr_err("sysctl registration failed!\n");
-		return -ENOMEM;
-	}
-	return 0;
-}
-#else /* !CONFIG_SYSCTL */
-static inline int __init fsverity_sysctl_init(void)
-{
-	return 0;
-}
-#endif /* !CONFIG_SYSCTL */
-
 int __init fsverity_init_signature(void)
 {
 	struct key *ring;
@@ -139,14 +96,6 @@ int __init fsverity_init_signature(void)
 	if (IS_ERR(ring))
 		return PTR_ERR(ring);
 
-	err = fsverity_sysctl_init();
-	if (err)
-		goto err_put_ring;
-
 	fsverity_keyring = ring;
 	return 0;
-
-err_put_ring:
-	key_put(ring);
-	return err;
 }
diff --git a/fs/verity/sysctl.c b/fs/verity/sysctl.c
new file mode 100644
index 000000000000..6ff1ae34931e
--- /dev/null
+++ b/fs/verity/sysctl.c
@@ -0,0 +1,69 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#include "fsverity_private.h"
+
+#include <linux/sysctl.h>
+
+static int two = 2;
+/*
+ * /proc/sys/fs/verity/require_signatures
+ * If 1, all verity files must have a valid builtin signature.
+ */
+int fsverity_require_signatures;
+/*
+ * /proc/sys/fs/verity/enable
+ * If 0, disable verity, don't verify, ignore enable ioctl.
+ * If 1, allow enabling verity, but only log on verification failure.
+ * If 2, fully enable.
+ * default: 2
+ */
+int fsverity_enable = 2;
+
+#ifdef CONFIG_SYSCTL
+static struct ctl_table_header *fsverity_sysctl_header;
+
+static const struct ctl_path fsverity_sysctl_path[] = {
+	{ .procname = "fs", },
+	{ .procname = "verity", },
+	{ }
+};
+
+static struct ctl_table fsverity_sysctl_table[] = {
+	{
+		.procname       = "require_signatures",
+		.data           = &fsverity_require_signatures,
+		.maxlen         = sizeof(int),
+		.mode           = 0644,
+		.proc_handler   = proc_dointvec_minmax,
+		.extra1         = SYSCTL_ZERO,
+		.extra2         = SYSCTL_ONE,
+	},
+	{
+		.procname       = "enable",
+		.data           = &fsverity_enable,
+		.maxlen         = sizeof(int),
+		.mode           = 0644,
+		.proc_handler   = proc_dointvec_minmax,
+		.extra1         = SYSCTL_ZERO,
+		.extra2         = &two,
+	},
+	{ }
+};
+
+int __init fsverity_sysctl_init(void)
+{
+	fsverity_sysctl_header = register_sysctl_paths(fsverity_sysctl_path,
+						       fsverity_sysctl_table);
+	if (!fsverity_sysctl_header) {
+		pr_err("sysctl registration failed!\n");
+		return -ENOMEM;
+	}
+	return 0;
+}
+
+void __init fsverity_exit_sysctl(void)
+{
+	unregister_sysctl_table(fsverity_sysctl_header);
+	fsverity_sysctl_header = NULL;
+}
+#endif /* !CONFIG_SYSCTL */
diff --git a/fs/verity/verify.c b/fs/verity/verify.c
index 0adb970f4e73..0a52e4c16038 100644
--- a/fs/verity/verify.c
+++ b/fs/verity/verify.c
@@ -12,6 +12,7 @@
 #include <linux/ratelimit.h>
 
 static struct workqueue_struct *fsverity_read_workqueue;
+extern int fsverity_enable;
 
 /**
  * hash_at_level() - compute the location of the block's hash at the given level
@@ -98,8 +99,10 @@ static bool verify_page(struct inode *inode, const struct fsverity_info *vi,
 	unsigned int hoffsets[FS_VERITY_MAX_LEVELS];
 	int err;
 
-	if (WARN_ON_ONCE(!PageLocked(data_page) || PageUptodate(data_page)))
-		return false;
+	if (WARN_ON_ONCE(!PageLocked(data_page) || PageUptodate(data_page))) {
+		err = -EINVAL;
+		goto out;
+	}
 
 	pr_debug_ratelimited("Verifying data page %lu...\n", index);
 
@@ -174,6 +177,8 @@ static bool verify_page(struct inode *inode, const struct fsverity_info *vi,
 	for (; level > 0; level--)
 		put_page(hpages[level - 1]);
 
+	if (fsverity_enable == 1)
+		err = 0;
 	return err == 0;
 }
 
@@ -193,6 +198,8 @@ bool fsverity_verify_page(struct page *page)
 	struct ahash_request *req;
 	bool valid;
 
+	if (!fsverity_enable)
+		return true;
 	/* This allocation never fails, since it's mempool-backed. */
 	req = fsverity_alloc_hash_request(vi->tree_params.hash_alg, GFP_NOFS);
 
