diff mbox series

[RFC,10/13] fscrypt: allow multiple extents to reference one info

Message ID 1a4861d291e08233a0f16b482af562d4fcb2caf1.1693630890.git.sweettea-kernel@dorminy.me (mailing list archive)
State New, archived
Headers show
Series fscrypt: add extent encryption | expand

Commit Message

Sweet Tea Dorminy Sept. 2, 2023, 5:54 a.m. UTC
btrfs occasionally splits in-memory extents while holding a mutex. This
means we can't just copy the info, since setting up a new inlinecrypt
key requires taking a semaphore. Thus adding a mechanism to split
extents and merely take a new reference on the info is necessary.

Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
 fs/crypto/fscrypt_private.h |  5 +++++
 fs/crypto/keysetup.c        | 19 ++++++++++++++++++-
 include/linux/fscrypt.h     |  2 +-
 3 files changed, 24 insertions(+), 2 deletions(-)
diff mbox series

Patch

diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h
index dd7740105264..cf1eb7fe546f 100644
--- a/fs/crypto/fscrypt_private.h
+++ b/fs/crypto/fscrypt_private.h
@@ -307,6 +307,11 @@  struct fscrypt_info {
  */
 struct fscrypt_extent_info {
 	struct fscrypt_common_info info;
+
+	/* Reference count. Normally 1, unless a extent info is shared by
+	 * several virtual extents.
+	 */
+	refcount_t refs;
 };
 
 typedef enum {
diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c
index 34d4df4acb19..f0f70b888bd8 100644
--- a/fs/crypto/keysetup.c
+++ b/fs/crypto/keysetup.c
@@ -919,6 +919,21 @@  int fscrypt_prepare_new_inode(struct inode *dir, struct inode *inode,
 }
 EXPORT_SYMBOL_GPL(fscrypt_prepare_new_inode);
 
+/**
+ * fscrypt_get_extent_info_ref() - mark a second extent using the same info
+ * @info: the info to be used by another extent
+ *
+ * Sometimes, an existing extent must be split into multiple extents in memory.
+ * In such a case, this function allows multiple extents to use the same extent
+ * info without allocating or taking any lock, which is necessary in certain IO
+ * paths.
+ */
+void fscrypt_get_extent_info_ref(struct fscrypt_extent_info *info)
+{
+	if (info)
+		refcount_inc(&info->refs);
+}
+
 /**
  * fscrypt_put_encryption_info() - free most of an inode's fscrypt data
  * @inode: an inode being evicted
@@ -997,7 +1012,7 @@  EXPORT_SYMBOL_GPL(fscrypt_drop_inode);
 
 static void put_crypt_extent_info(struct fscrypt_extent_info *ci)
 {
-	if (!ci)
+	if (!ci || !refcount_dec_and_test(&ci->refs))
 		return;
 
 	free_prepared_key(&ci->info);
@@ -1042,6 +1057,8 @@  fscrypt_setup_extent_info(struct inode *inode,
 	if (res)
 		goto out;
 
+	refcount_set(&crypt_extent_info->refs, 1);
+
 	*info_ptr = crypt_extent_info;
 	add_info_to_mk_decrypted_list(crypt_info, mk);
 
diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h
index b57fc5645076..577f9e0a6e97 100644
--- a/include/linux/fscrypt.h
+++ b/include/linux/fscrypt.h
@@ -362,7 +362,7 @@  int fscrypt_prepare_new_extent(struct inode *inode,
 void fscrypt_free_extent_info(struct fscrypt_extent_info **info_ptr);
 int fscrypt_load_extent_info(struct inode *inode, void *buf, size_t len,
 			     struct fscrypt_extent_info **info_ptr);
-
+void fscrypt_get_extent_info_ref(struct fscrypt_extent_info *info);
 
 /* fname.c */
 int fscrypt_fname_encrypt(const struct inode *inode, const struct qstr *iname,