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@xxxxxxxxxx> --- fs/crypto/fscrypt_private.h | 5 +++++ fs/crypto/keysetup.c | 19 ++++++++++++++++++- include/linux/fscrypt.h | 2 +- 3 files changed, 24 insertions(+), 2 deletions(-) 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, -- 2.41.0