From 7ad194058d99bd30b0b7bf14ee150cc9c22cc7e8 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Thu, 18 Jun 2026 18:48:52 +0000 Subject: [PATCH 01/29] fscrypt: Use lock guards for mutexes Replace all remaining calls to mutex_lock() and mutex_unlock() in fs/crypto/ with lock guards. No functional change. Link: https://patch.msgid.link/20260618184852.3469301-1-ebiggers@kernel.org Signed-off-by: Eric Biggers --- fs/crypto/crypto.c | 13 ++++--------- fs/crypto/keyring.c | 3 +-- fs/crypto/keysetup.c | 21 ++++++++++----------- 3 files changed, 15 insertions(+), 22 deletions(-) diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c index 570a2231c945..10097a3251f5 100644 --- a/fs/crypto/crypto.c +++ b/fs/crypto/crypto.c @@ -323,7 +323,6 @@ EXPORT_SYMBOL(fscrypt_decrypt_block_inplace); */ int fscrypt_initialize(struct super_block *sb) { - int err = 0; mempool_t *pool; /* pairs with smp_store_release() below */ @@ -334,20 +333,16 @@ int fscrypt_initialize(struct super_block *sb) if (!sb->s_cop->needs_bounce_pages) return 0; - mutex_lock(&fscrypt_init_mutex); + guard(mutex)(&fscrypt_init_mutex); if (fscrypt_bounce_page_pool) - goto out_unlock; + return 0; - err = -ENOMEM; pool = mempool_create_page_pool(num_prealloc_crypto_pages, 0); if (!pool) - goto out_unlock; + return -ENOMEM; /* pairs with smp_load_acquire() above */ smp_store_release(&fscrypt_bounce_page_pool, pool); - err = 0; -out_unlock: - mutex_unlock(&fscrypt_init_mutex); - return err; + return 0; } void fscrypt_msg(const struct inode *inode, const char *level, diff --git a/fs/crypto/keyring.c b/fs/crypto/keyring.c index 38b73e703073..16bc348213ca 100644 --- a/fs/crypto/keyring.c +++ b/fs/crypto/keyring.c @@ -497,7 +497,7 @@ static int do_add_master_key(struct super_block *sb, struct fscrypt_master_key *mk; int err; - mutex_lock(&fscrypt_add_key_mutex); /* serialize find + link */ + guard(mutex)(&fscrypt_add_key_mutex); /* serialize find + link */ mk = fscrypt_find_master_key(sb, mk_spec); if (!mk) { @@ -524,7 +524,6 @@ static int do_add_master_key(struct super_block *sb, } fscrypt_put_master_key(mk); } - mutex_unlock(&fscrypt_add_key_mutex); return err; } diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c index f905f9f94bdd..281b5a8b0bcd 100644 --- a/fs/crypto/keysetup.c +++ b/fs/crypto/keysetup.c @@ -349,18 +349,17 @@ static int fscrypt_setup_iv_ino_lblk_32_key(struct fscrypt_inode_info *ci, /* pairs with smp_store_release() below */ if (!smp_load_acquire(&mk->mk_ino_hash_key_initialized)) { + guard(mutex)(&fscrypt_mode_key_setup_mutex); - mutex_lock(&fscrypt_mode_key_setup_mutex); - - if (mk->mk_ino_hash_key_initialized) - goto unlock; - - fscrypt_derive_siphash_key(mk, HKDF_CONTEXT_INODE_HASH_KEY, - NULL, 0, &mk->mk_ino_hash_key); - /* pairs with smp_load_acquire() above */ - smp_store_release(&mk->mk_ino_hash_key_initialized, true); -unlock: - mutex_unlock(&fscrypt_mode_key_setup_mutex); + if (!mk->mk_ino_hash_key_initialized) { + fscrypt_derive_siphash_key(mk, + HKDF_CONTEXT_INODE_HASH_KEY, + NULL, 0, + &mk->mk_ino_hash_key); + /* pairs with smp_load_acquire() above */ + smp_store_release(&mk->mk_ino_hash_key_initialized, + true); + } } /* From fa1517bc997ef89124d5cb63b2ced305b08784ad Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Thu, 18 Jun 2026 16:14:04 -0700 Subject: [PATCH 02/29] fscrypt: Remove FSCRYPT_MODE_MAX Now that the arrays of per-mode keys in struct fscrypt_master_key have been replaced by a linked list, the definition of FSCRYPT_MODE_MAX doesn't do anything useful. (Previously it was used to size these arrays.) Remove it. Link: https://patch.msgid.link/20260618231404.132829-1-ebiggers@kernel.org Signed-off-by: Eric Biggers --- fs/crypto/fscrypt_private.h | 3 --- fs/crypto/keysetup.c | 5 ----- include/uapi/linux/fscrypt.h | 1 - tools/include/uapi/linux/fscrypt.h | 1 - 4 files changed, 10 deletions(-) diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index 0053b5c45412..45307f7fa540 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -66,9 +66,6 @@ #define FSCRYPT_CONTEXT_V1 1 #define FSCRYPT_CONTEXT_V2 2 -/* Keep this in sync with include/uapi/linux/fscrypt.h */ -#define FSCRYPT_MODE_MAX FSCRYPT_MODE_AES_256_HCTR2 - struct fscrypt_context_v1 { u8 version; /* FSCRYPT_CONTEXT_V1 */ u8 contents_encryption_mode; diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c index 281b5a8b0bcd..cfd348e2252e 100644 --- a/fs/crypto/keysetup.c +++ b/fs/crypto/keysetup.c @@ -83,8 +83,6 @@ static struct fscrypt_mode * select_encryption_mode(const union fscrypt_policy *policy, const struct inode *inode) { - BUILD_BUG_ON(ARRAY_SIZE(fscrypt_modes) != FSCRYPT_MODE_MAX + 1); - if (S_ISREG(inode->i_mode)) return &fscrypt_modes[fscrypt_policy_contents_mode(policy)]; @@ -229,9 +227,6 @@ static int setup_per_mode_enc_key(struct fscrypt_inode_info *ci, bool use_hw_wrapped_key = false; int err; - if (WARN_ON_ONCE(mode_num > FSCRYPT_MODE_MAX)) - return -EINVAL; - if (mk->mk_secret.is_hw_wrapped && S_ISREG(inode->i_mode)) { /* Using a hardware-wrapped key for file contents encryption */ if (!fscrypt_using_inline_encryption(ci)) { diff --git a/include/uapi/linux/fscrypt.h b/include/uapi/linux/fscrypt.h index 3aff99f2696a..84507280b3ea 100644 --- a/include/uapi/linux/fscrypt.h +++ b/include/uapi/linux/fscrypt.h @@ -30,7 +30,6 @@ #define FSCRYPT_MODE_SM4_CTS 8 #define FSCRYPT_MODE_ADIANTUM 9 #define FSCRYPT_MODE_AES_256_HCTR2 10 -/* If adding a mode number > 10, update FSCRYPT_MODE_MAX in fscrypt_private.h */ /* * Legacy policy version; ad-hoc KDF and no key verification. diff --git a/tools/include/uapi/linux/fscrypt.h b/tools/include/uapi/linux/fscrypt.h index 3aff99f2696a..84507280b3ea 100644 --- a/tools/include/uapi/linux/fscrypt.h +++ b/tools/include/uapi/linux/fscrypt.h @@ -30,7 +30,6 @@ #define FSCRYPT_MODE_SM4_CTS 8 #define FSCRYPT_MODE_ADIANTUM 9 #define FSCRYPT_MODE_AES_256_HCTR2 10 -/* If adding a mode number > 10, update FSCRYPT_MODE_MAX in fscrypt_private.h */ /* * Legacy policy version; ad-hoc KDF and no key verification. From 928a1e6ba3201bcc21ecbc680500b0636530532b Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Thu, 18 Jun 2026 17:00:30 -0700 Subject: [PATCH 03/29] fscrypt: Simplify handling of errors during initcall Since CONFIG_FS_ENCRYPTION is a bool, not a tristate, fs/crypto/ can only be builtin or absent entirely; it can't be a loadable module. Therefore, the error code that gets returned from the fscrypt_init() initcall is never used. If any part of the initcall does fail, which should never happen, the kernel will be left in a bad state. Following the usual convention for builtin code, just panic the kernel if any of part of the initcall fails. This simplifies the code. This closely mirrors commit e77000ccc531 ("fsverity: simplify handling of errors during initcall"). Link: https://patch.msgid.link/20260619000030.166851-1-ebiggers@kernel.org Signed-off-by: Eric Biggers --- fs/crypto/crypto.c | 27 ++++----------------------- fs/crypto/fscrypt_private.h | 2 +- fs/crypto/keyring.c | 18 ++++++++---------- 3 files changed, 13 insertions(+), 34 deletions(-) diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c index 10097a3251f5..94dd6c89ddcd 100644 --- a/fs/crypto/crypto.c +++ b/fs/crypto/crypto.c @@ -369,15 +369,8 @@ void fscrypt_msg(const struct inode *inode, const char *level, va_end(args); } -/** - * fscrypt_init() - Set up for fs encryption. - * - * Return: 0 on success; -errno on failure - */ static int __init fscrypt_init(void) { - int err = -ENOMEM; - /* * Use an unbound workqueue to allow bios to be decrypted in parallel * even when they happen to complete on the same CPU. This sacrifices @@ -390,24 +383,12 @@ static int __init fscrypt_init(void) WQ_UNBOUND | WQ_HIGHPRI, num_online_cpus()); if (!fscrypt_read_workqueue) - goto fail; + panic("failed to allocate fscrypt_read_queue"); fscrypt_inode_info_cachep = KMEM_CACHE(fscrypt_inode_info, - SLAB_RECLAIM_ACCOUNT); - if (!fscrypt_inode_info_cachep) - goto fail_free_queue; - - err = fscrypt_init_keyring(); - if (err) - goto fail_free_inode_info; - + SLAB_RECLAIM_ACCOUNT | + SLAB_PANIC); + fscrypt_init_keyring(); return 0; - -fail_free_inode_info: - kmem_cache_destroy(fscrypt_inode_info_cachep); -fail_free_queue: - destroy_workqueue(fscrypt_read_workqueue); -fail: - return err; } late_initcall(fscrypt_init) diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index 45307f7fa540..8234ee542476 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -715,7 +715,7 @@ int fscrypt_add_test_dummy_key(struct super_block *sb, int fscrypt_verify_key_added(struct super_block *sb, const u8 identifier[FSCRYPT_KEY_IDENTIFIER_SIZE]); -int __init fscrypt_init_keyring(void); +void __init fscrypt_init_keyring(void); /* keysetup.c */ diff --git a/fs/crypto/keyring.c b/fs/crypto/keyring.c index 16bc348213ca..76e28d1e0064 100644 --- a/fs/crypto/keyring.c +++ b/fs/crypto/keyring.c @@ -1220,21 +1220,19 @@ int fscrypt_ioctl_get_key_status(struct file *filp, void __user *uarg) } EXPORT_SYMBOL_GPL(fscrypt_ioctl_get_key_status); -int __init fscrypt_init_keyring(void) +void __init fscrypt_init_keyring(void) { int err; + /* + * Note that register_key_type() fails only if a key type with the same + * name already exists, which should never happen here. + */ err = register_key_type(&key_type_fscrypt_user); if (err) - return err; - + panic("failed to register .fscrypt key type (%d)", err); err = register_key_type(&key_type_fscrypt_provisioning); if (err) - goto err_unregister_fscrypt_user; - - return 0; - -err_unregister_fscrypt_user: - unregister_key_type(&key_type_fscrypt_user); - return err; + panic("failed to register fscrypt-provisioning key type (%d)", + err); } From dbca1290258ff20b5742a3a3e294fc95df941939 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Thu, 18 Jun 2026 22:10:08 -0700 Subject: [PATCH 04/29] fscrypt: Remove workaround for bug in gcc 7 and earlier Since the kernel's minimum gcc version is now 8.1, the workaround for a strange gcc bug in fscrypt_ioctl_set_policy() is no longer needed. Link: https://patch.msgid.link/20260619051008.51223-1-ebiggers@kernel.org Signed-off-by: Eric Biggers --- fs/crypto/policy.c | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c index 9915e39362db..f40fb5924e75 100644 --- a/fs/crypto/policy.c +++ b/fs/crypto/policy.c @@ -507,7 +507,6 @@ int fscrypt_ioctl_set_policy(struct file *filp, const void __user *arg) union fscrypt_policy policy; union fscrypt_policy existing_policy; struct inode *inode = file_inode(filp); - u8 version; int size; int ret; @@ -518,21 +517,9 @@ int fscrypt_ioctl_set_policy(struct file *filp, const void __user *arg) if (size <= 0) return -EINVAL; - /* - * We should just copy the remaining 'size - 1' bytes here, but a - * bizarre bug in gcc 7 and earlier (fixed by gcc r255731) causes gcc to - * think that size can be 0 here (despite the check above!) *and* that - * it's a compile-time constant. Thus it would think copy_from_user() - * is passed compile-time constant ULONG_MAX, causing the compile-time - * buffer overflow check to fail, breaking the build. This only occurred - * when building an i386 kernel with -Os and branch profiling enabled. - * - * Work around it by just copying the first byte again... - */ - version = policy.version; - if (copy_from_user(&policy, arg, size)) + if (copy_from_user((u8 *)&policy + 1, (const u8 __user *)arg + 1, + size - 1)) return -EFAULT; - policy.version = version; if (!inode_owner_or_capable(&nop_mnt_idmap, inode)) return -EACCES; From a90d760d572c37417db157901dc5c22a774048e9 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sun, 12 Jul 2026 22:36:52 -0400 Subject: [PATCH 05/29] blk-crypto: Simplify check for fallback support Since blk-crypto-fallback supports all blk_crypto_keys except wrapped keys, just check for that condition directly instead of using __blk_crypto_cfg_supported(). With this done, __blk_crypto_cfg_supported() is now used only for the hardware support. Reviewed-by: Christoph Hellwig Link: https://patch.msgid.link/20260713023708.9245-2-ebiggers@kernel.org Signed-off-by: Eric Biggers --- block/blk-crypto-fallback.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/block/blk-crypto-fallback.c b/block/blk-crypto-fallback.c index 2a5c52ab74b4..2a8f40a65158 100644 --- a/block/blk-crypto-fallback.c +++ b/block/blk-crypto-fallback.c @@ -496,8 +496,7 @@ bool blk_crypto_fallback_bio_prep(struct bio *bio) return false; } - if (!__blk_crypto_cfg_supported(blk_crypto_fallback_profile, - &bc->bc_key->crypto_cfg)) { + if (bc->bc_key->crypto_cfg.key_type != BLK_CRYPTO_KEY_TYPE_RAW) { bio_endio_status(bio, BLK_STS_NOTSUPP); return false; } From 0ffa0da2e538f5edd215384fd86a734cb356af22 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sun, 12 Jul 2026 22:36:53 -0400 Subject: [PATCH 06/29] blk-crypto: Fold __blk_crypto_cfg_supported() into its caller __blk_crypto_cfg_supported() is called only by blk_crypto_config_supported_natively(), so fold it in. Reviewed-by: Christoph Hellwig Link: https://patch.msgid.link/20260713023708.9245-3-ebiggers@kernel.org Signed-off-by: Eric Biggers --- block/blk-crypto-internal.h | 3 --- block/blk-crypto-profile.c | 22 ---------------------- block/blk-crypto.c | 23 +++++++++++++++++++++-- 3 files changed, 21 insertions(+), 27 deletions(-) diff --git a/block/blk-crypto-internal.h b/block/blk-crypto-internal.h index 742694213529..2c7a0446572a 100644 --- a/block/blk-crypto-internal.h +++ b/block/blk-crypto-internal.h @@ -80,9 +80,6 @@ void blk_crypto_put_keyslot(struct blk_crypto_keyslot *slot); int __blk_crypto_evict_key(struct blk_crypto_profile *profile, const struct blk_crypto_key *key); -bool __blk_crypto_cfg_supported(struct blk_crypto_profile *profile, - const struct blk_crypto_config *cfg); - int blk_crypto_ioctl(struct block_device *bdev, unsigned int cmd, void __user *argp); diff --git a/block/blk-crypto-profile.c b/block/blk-crypto-profile.c index cf447ba4a66e..53126c091b0b 100644 --- a/block/blk-crypto-profile.c +++ b/block/blk-crypto-profile.c @@ -335,28 +335,6 @@ void blk_crypto_put_keyslot(struct blk_crypto_keyslot *slot) } } -/** - * __blk_crypto_cfg_supported() - Check whether the given crypto profile - * supports the given crypto configuration. - * @profile: the crypto profile to check - * @cfg: the crypto configuration to check for - * - * Return: %true if @profile supports the given @cfg. - */ -bool __blk_crypto_cfg_supported(struct blk_crypto_profile *profile, - const struct blk_crypto_config *cfg) -{ - if (!profile) - return false; - if (!(profile->modes_supported[cfg->crypto_mode] & cfg->data_unit_size)) - return false; - if (profile->max_dun_bytes_supported < cfg->dun_bytes) - return false; - if (!(profile->key_types_supported & cfg->key_type)) - return false; - return true; -} - /* * This is an internal function that evicts a key from an inline encryption * device that can be either a real device or the blk-crypto-fallback "device". diff --git a/block/blk-crypto.c b/block/blk-crypto.c index 15e25e41b166..de60f03b4d4b 100644 --- a/block/blk-crypto.c +++ b/block/blk-crypto.c @@ -351,11 +351,30 @@ int blk_crypto_init_key(struct blk_crypto_key *blk_key, } EXPORT_SYMBOL_GPL(blk_crypto_init_key); +/** + * blk_crypto_config_supported_natively() - Check whether a block device + * supports hardware inline encryption + * with the given configuration. + * @bdev: the block device + * @cfg: the crypto configuration to check for + * + * Return: %true if @bdev supports hardware inline encryption with @cfg. + */ bool blk_crypto_config_supported_natively(struct block_device *bdev, const struct blk_crypto_config *cfg) { - return __blk_crypto_cfg_supported(bdev_get_queue(bdev)->crypto_profile, - cfg); + struct blk_crypto_profile *profile = + bdev_get_queue(bdev)->crypto_profile; + + if (!profile) + return false; + if (!(profile->modes_supported[cfg->crypto_mode] & cfg->data_unit_size)) + return false; + if (profile->max_dun_bytes_supported < cfg->dun_bytes) + return false; + if (!(profile->key_types_supported & cfg->key_type)) + return false; + return true; } /* From 95b39df0413077b631cde9c6e35224c15258ccf5 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sun, 12 Jul 2026 22:36:54 -0400 Subject: [PATCH 07/29] blk-crypto: Allow control over whether hardware is used fscrypt uses inline encryption hardware only when the "inlinecrypt" mount option is given. I'd like to keep that behavior even after standardizing on the blk-crypto API for file contents encryption. That is, the default should continue to be the well-tested CPU-based encryption code, and the use of inline encryption hardware should continue to be an opt-in feature for systems where it's beneficial and has been fully validated (including verifying ciphertext correctness). To support this use case, extend blk_crypto_config with a new flag BLK_CRYPTO_CFG_ALLOW_HW. For now it's always set. Later commits will change that. Reviewed-by: Christoph Hellwig Link: https://patch.msgid.link/20260713023708.9245-4-ebiggers@kernel.org Signed-off-by: Eric Biggers --- block/blk-crypto.c | 11 ++++++++++- drivers/md/dm-inlinecrypt.c | 3 ++- fs/crypto/inline_crypt.c | 4 +++- include/linux/blk-crypto.h | 13 ++++++++++++- 4 files changed, 27 insertions(+), 4 deletions(-) diff --git a/block/blk-crypto.c b/block/blk-crypto.c index de60f03b4d4b..0fe6ef0eea1d 100644 --- a/block/blk-crypto.c +++ b/block/blk-crypto.c @@ -300,6 +300,7 @@ int __blk_crypto_rq_bio_prep(struct request *rq, struct bio *bio, * @dun_bytes: number of bytes that will be used to specify the DUN when this * key is used * @data_unit_size: the data unit size to use for en/decryption + * @flags: BLK_CRYPTO_CFG_* flags * * Return: 0 on success, -errno on failure. The caller is responsible for * zeroizing both blk_key and key_bytes when done with them. @@ -309,7 +310,7 @@ int blk_crypto_init_key(struct blk_crypto_key *blk_key, enum blk_crypto_key_type key_type, enum blk_crypto_mode_num crypto_mode, unsigned int dun_bytes, - unsigned int data_unit_size) + unsigned int data_unit_size, int flags) { const struct blk_crypto_mode *mode; @@ -318,6 +319,9 @@ int blk_crypto_init_key(struct blk_crypto_key *blk_key, if (crypto_mode >= ARRAY_SIZE(blk_crypto_modes)) return -EINVAL; + if (flags & ~BLK_CRYPTO_CFG_ALLOW_HW) + return -EINVAL; + mode = &blk_crypto_modes[crypto_mode]; switch (key_type) { case BLK_CRYPTO_KEY_TYPE_RAW: @@ -328,6 +332,8 @@ int blk_crypto_init_key(struct blk_crypto_key *blk_key, if (key_size < mode->security_strength || key_size > BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE) return -EINVAL; + if (!(flags & BLK_CRYPTO_CFG_ALLOW_HW)) + return -EINVAL; break; default: return -EINVAL; @@ -343,6 +349,7 @@ int blk_crypto_init_key(struct blk_crypto_key *blk_key, blk_key->crypto_cfg.dun_bytes = dun_bytes; blk_key->crypto_cfg.data_unit_size = data_unit_size; blk_key->crypto_cfg.key_type = key_type; + blk_key->crypto_cfg.flags = flags; blk_key->data_unit_size_bits = ilog2(data_unit_size); blk_key->size = key_size; memcpy(blk_key->bytes, key_bytes, key_size); @@ -368,6 +375,8 @@ bool blk_crypto_config_supported_natively(struct block_device *bdev, if (!profile) return false; + if (!(cfg->flags & BLK_CRYPTO_CFG_ALLOW_HW)) + return false; if (!(profile->modes_supported[cfg->crypto_mode] & cfg->data_unit_size)) return false; if (profile->max_dun_bytes_supported < cfg->dun_bytes) diff --git a/drivers/md/dm-inlinecrypt.c b/drivers/md/dm-inlinecrypt.c index 41293c18d10f..f50970db0f94 100644 --- a/drivers/md/dm-inlinecrypt.c +++ b/drivers/md/dm-inlinecrypt.c @@ -408,7 +408,8 @@ static int inlinecrypt_ctr(struct dm_target *ti, unsigned int argc, char **argv) err = blk_crypto_init_key(&ctx->key, key_bytes, ctx->key_size, ctx->key_type, cipher->mode_num, - dun_bytes, ctx->sector_size); + dun_bytes, ctx->sector_size, + BLK_CRYPTO_CFG_ALLOW_HW); if (err) { ti->error = "Error initializing blk-crypto key"; goto bad; diff --git a/fs/crypto/inline_crypt.c b/fs/crypto/inline_crypt.c index 66b9c9150fed..013f2bdc6f23 100644 --- a/fs/crypto/inline_crypt.c +++ b/fs/crypto/inline_crypt.c @@ -126,6 +126,7 @@ int fscrypt_select_encryption_impl(struct fscrypt_inode_info *ci, crypto_cfg.dun_bytes = fscrypt_get_dun_bytes(ci); crypto_cfg.key_type = is_hw_wrapped_key ? BLK_CRYPTO_KEY_TYPE_HW_WRAPPED : BLK_CRYPTO_KEY_TYPE_RAW; + crypto_cfg.flags = BLK_CRYPTO_CFG_ALLOW_HW; num_devs = fscrypt_get_devices(sb, devs); for (i = 0; i < num_devs; i++) { @@ -162,7 +163,8 @@ int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key, err = blk_crypto_init_key(blk_key, key_bytes, key_size, key_type, crypto_mode, fscrypt_get_dun_bytes(ci), - 1U << ci->ci_data_unit_bits); + 1U << ci->ci_data_unit_bits, + BLK_CRYPTO_CFG_ALLOW_HW); if (err) { fscrypt_err(inode, "error %d initializing blk-crypto key", err); goto fail; diff --git a/include/linux/blk-crypto.h b/include/linux/blk-crypto.h index f7c3cb4a342f..5f40821f99cd 100644 --- a/include/linux/blk-crypto.h +++ b/include/linux/blk-crypto.h @@ -68,6 +68,15 @@ enum blk_crypto_key_type { */ #define BLK_CRYPTO_SW_SECRET_SIZE 32 +/* Flags for blk_crypto_config::flags: */ + +/* + * If set, inline encryption hardware will be used if available. + * If unset, CPU-based encryption will always be used (requires + * CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK) + */ +#define BLK_CRYPTO_CFG_ALLOW_HW (1 << 0) + /** * struct blk_crypto_config - an inline encryption key's crypto configuration * @crypto_mode: encryption algorithm this key is for @@ -77,12 +86,14 @@ enum blk_crypto_key_type { * filesystem block size or the disk sector size. * @dun_bytes: the maximum number of bytes of DUN used when using this key * @key_type: the type of this key -- either raw or hardware-wrapped + * @flags: BLK_CRYPTO_CFG_* flags */ struct blk_crypto_config { enum blk_crypto_mode_num crypto_mode; unsigned int data_unit_size; unsigned int dun_bytes; enum blk_crypto_key_type key_type; + int flags; }; /** @@ -150,7 +161,7 @@ int blk_crypto_init_key(struct blk_crypto_key *blk_key, enum blk_crypto_key_type key_type, enum blk_crypto_mode_num crypto_mode, unsigned int dun_bytes, - unsigned int data_unit_size); + unsigned int data_unit_size, int flags); int blk_crypto_start_using_key(struct block_device *bdev, const struct blk_crypto_key *key); From b69664873f4af302c7867aa7478e818549932614 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sun, 12 Jul 2026 22:36:55 -0400 Subject: [PATCH 08/29] fscrypt: Fully disallow IV_INO_LBLK_32 with s_blocksize != PAGE_SIZE FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32 with s_blocksize != PAGE_SIZE works only with the fs-layer implementation of file contents encryption, not blk-crypto. This is a problem for standardizing on blk-crypto. Fortunately, no one should be using this combination anyway. It doesn't make sense because the entire point of IV_INO_LBLK_32 is to support inline encryption hardware that is limited to 32-bit DUNs. Thus, fully disallow IV_INO_LBLK_32 with s_blocksize != PAGE_SIZE. Reviewed-by: Christoph Hellwig Link: https://patch.msgid.link/20260713023708.9245-5-ebiggers@kernel.org Signed-off-by: Eric Biggers --- Documentation/filesystems/fscrypt.rst | 3 +++ fs/crypto/inline_crypt.c | 13 ------------- fs/crypto/policy.c | 17 +++++++++++++++++ 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/Documentation/filesystems/fscrypt.rst b/Documentation/filesystems/fscrypt.rst index c0dd35f1af12..92b8f311e211 100644 --- a/Documentation/filesystems/fscrypt.rst +++ b/Documentation/filesystems/fscrypt.rst @@ -336,6 +336,9 @@ per I/O request and may have only a small number of keyslots. This format results in some level of IV reuse, so it should only be used when necessary due to hardware limitations. +IV_INO_LBLK_32 is supported only when the filesystem block size is +equal to the page size. + Key identifiers --------------- diff --git a/fs/crypto/inline_crypt.c b/fs/crypto/inline_crypt.c index 013f2bdc6f23..118e29ccb1d7 100644 --- a/fs/crypto/inline_crypt.c +++ b/fs/crypto/inline_crypt.c @@ -104,19 +104,6 @@ int fscrypt_select_encryption_impl(struct fscrypt_inode_info *ci, if (!(sb->s_flags & SB_INLINECRYPT)) return 0; - /* - * When a page contains multiple logically contiguous filesystem blocks, - * some filesystem code only calls fscrypt_mergeable_bio() for the first - * block in the page. This is fine for most of fscrypt's IV generation - * strategies, where contiguous blocks imply contiguous IVs. But it - * doesn't work with IV_INO_LBLK_32. For now, simply exclude - * IV_INO_LBLK_32 with blocksize != PAGE_SIZE from inline encryption. - */ - if ((fscrypt_policy_flags(&ci->ci_policy) & - FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32) && - sb->s_blocksize != PAGE_SIZE) - return 0; - /* * On all the filesystem's block devices, blk-crypto must support the * crypto configuration that the file would use. diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c index f40fb5924e75..a7322dba7557 100644 --- a/fs/crypto/policy.c +++ b/fs/crypto/policy.c @@ -177,6 +177,23 @@ static bool supported_iv_ino_lblk_policy(const struct fscrypt_policy_v2 *policy, type, sb->s_id); return false; } + + /* + * IV_INO_LBLK_32 isn't compatible with inline encryption when + * s_blocksize != PAGE_SIZE. In that case the DUN can wrap around in + * the middle of a page, but sometimes fscrypt_mergeable_bio() is called + * only for the first block per page. Since IV_INO_LBLK_32 exists only + * to support inline encryption hardware that is limited to 32-bit DUNs, + * just disallow IV_INO_LBLK_32 with s_blocksize != PAGE_SIZE entirely. + */ + if ((policy->flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32) && + sb->s_blocksize != PAGE_SIZE) { + fscrypt_warn(inode, + "Can't use %s policy on filesystem '%s' with block size != PAGE_SIZE", + type, sb->s_id); + return false; + } + return true; } From 5acc6f649a687526bd42c4242098a2582689587b Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sun, 12 Jul 2026 22:36:56 -0400 Subject: [PATCH 09/29] fscrypt: Always use blk-crypto for contents on block-based filesystems For encrypting and decrypting file contents on block-based filesystems (i.e., ext4 and f2fs, but not ceph and ubifs), always use blk-crypto instead of fs-layer crypto (direct use of crypto_skcipher). Since the blk-crypto API provides a fallback to CPU-based encryption, it's all that's needed on block-based filesystems. The support for two alternative block-based file contents encryption implementations, fs-layer and blk-crypto, existed mainly for historical reasons, as the fs-layer path came first. Some of it is also still needed for the non-block-based filesystems, but a lot of it isn't. Removing the duplicate fs-layer code paths greatly simplifies the code, most of which is done in later commits. Specific implementation details: - SB_INLINECRYPT now controls whether blk_crypto_config::allow_hw is set to true, instead of whether blk-crypto is used at all. The effect is that the semantics are preserved: the inlinecrypt mount option selects the use of inline encryption hardware instead of the CPU. - Set up a blk_crypto_key iff the file is a regular file on a block-based filesystem. To determine whether the filesystem is block-based, add a bit fscrypt_operations::is_block_based. - Remove fscrypt_select_encryption_impl(). Move the logging logic that was previously there into fscrypt_prepare_inline_crypt_key(). Note that blk_crypto_config_supported() is no longer needed. Reviewed-by: Christoph Hellwig Link: https://patch.msgid.link/20260713023708.9245-6-ebiggers@kernel.org Signed-off-by: Eric Biggers --- arch/loongarch/configs/loongson32_defconfig | 1 - arch/loongarch/configs/loongson64_defconfig | 1 - fs/crypto/Kconfig | 8 +- fs/crypto/fscrypt_private.h | 21 +--- fs/crypto/inline_crypt.c | 105 ++++++-------------- fs/crypto/keysetup.c | 31 +----- fs/ext4/crypto.c | 1 + fs/f2fs/super.c | 1 + include/linux/fscrypt.h | 28 ++---- 9 files changed, 57 insertions(+), 140 deletions(-) diff --git a/arch/loongarch/configs/loongson32_defconfig b/arch/loongarch/configs/loongson32_defconfig index 7c8f01513ed2..6bf2867dbdc6 100644 --- a/arch/loongarch/configs/loongson32_defconfig +++ b/arch/loongarch/configs/loongson32_defconfig @@ -969,7 +969,6 @@ CONFIG_F2FS_FS_SECURITY=y CONFIG_F2FS_CHECK_FS=y CONFIG_F2FS_FS_COMPRESSION=y CONFIG_FS_ENCRYPTION=y -CONFIG_FS_ENCRYPTION_INLINE_CRYPT=y CONFIG_FS_VERITY=y CONFIG_FANOTIFY=y CONFIG_FANOTIFY_ACCESS_PERMISSIONS=y diff --git a/arch/loongarch/configs/loongson64_defconfig b/arch/loongarch/configs/loongson64_defconfig index 8e3906d3bd70..def104c9d405 100644 --- a/arch/loongarch/configs/loongson64_defconfig +++ b/arch/loongarch/configs/loongson64_defconfig @@ -1000,7 +1000,6 @@ CONFIG_F2FS_FS_SECURITY=y CONFIG_F2FS_CHECK_FS=y CONFIG_F2FS_FS_COMPRESSION=y CONFIG_FS_ENCRYPTION=y -CONFIG_FS_ENCRYPTION_INLINE_CRYPT=y CONFIG_FS_VERITY=y CONFIG_FANOTIFY=y CONFIG_FANOTIFY_ACCESS_PERMISSIONS=y diff --git a/fs/crypto/Kconfig b/fs/crypto/Kconfig index 983d8ad1f417..cd934e31dec4 100644 --- a/fs/crypto/Kconfig +++ b/fs/crypto/Kconfig @@ -1,6 +1,8 @@ # SPDX-License-Identifier: GPL-2.0-only config FS_ENCRYPTION bool "FS Encryption (Per-file encryption)" + select BLK_INLINE_ENCRYPTION if BLOCK + select BLK_INLINE_ENCRYPTION_FALLBACK if BLOCK select CRYPTO select CRYPTO_SKCIPHER select CRYPTO_LIB_AES @@ -34,7 +36,5 @@ config FS_ENCRYPTION_ALGS select CRYPTO_XTS config FS_ENCRYPTION_INLINE_CRYPT - bool "Enable fscrypt to use inline crypto" - depends on FS_ENCRYPTION && BLK_INLINE_ENCRYPTION - help - Enable fscrypt to use inline encryption hardware if available. + bool + default y if FS_ENCRYPTION && BLOCK diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index 8234ee542476..57b7ae2cfafc 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -266,14 +266,6 @@ struct fscrypt_inode_info { /* True if ci_enc_key should be freed when this struct is freed */ u8 ci_owns_key : 1; -#ifdef CONFIG_FS_ENCRYPTION_INLINE_CRYPT - /* - * True if this inode will use inline encryption (blk-crypto) instead of - * the traditional filesystem-layer encryption. - */ - u8 ci_inlinecrypt : 1; -#endif - /* True if ci_dirhash_key is initialized */ u8 ci_dirhash_key_initialized : 1; @@ -410,13 +402,12 @@ void fscrypt_hkdf_expand(const struct hmac_sha512_key *hkdf, u8 context, /* inline_crypt.c */ #ifdef CONFIG_FS_ENCRYPTION_INLINE_CRYPT -int fscrypt_select_encryption_impl(struct fscrypt_inode_info *ci, - bool is_hw_wrapped_key); - static inline bool fscrypt_using_inline_encryption(const struct fscrypt_inode_info *ci) { - return ci->ci_inlinecrypt; + const struct inode *inode = ci->ci_inode; + + return S_ISREG(inode->i_mode) && inode->i_sb->s_cop->is_block_based; } int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key, @@ -446,12 +437,6 @@ fscrypt_is_key_prepared(const struct fscrypt_prepared_key *prep_key, #else /* CONFIG_FS_ENCRYPTION_INLINE_CRYPT */ -static inline int fscrypt_select_encryption_impl(struct fscrypt_inode_info *ci, - bool is_hw_wrapped_key) -{ - return 0; -} - static inline bool fscrypt_using_inline_encryption(const struct fscrypt_inode_info *ci) { diff --git a/fs/crypto/inline_crypt.c b/fs/crypto/inline_crypt.c index 118e29ccb1d7..500ee9079a2a 100644 --- a/fs/crypto/inline_crypt.c +++ b/fs/crypto/inline_crypt.c @@ -62,72 +62,19 @@ static unsigned int fscrypt_get_dun_bytes(const struct fscrypt_inode_info *ci) * helpful for debugging problems where the "wrong" implementation is used. */ static void fscrypt_log_blk_crypto_impl(struct fscrypt_mode *mode, - struct block_device **devs, - unsigned int num_devs, - const struct blk_crypto_config *cfg) + struct block_device *dev, + const struct blk_crypto_key *blk_key) { - unsigned int i; - - for (i = 0; i < num_devs; i++) { - if (!IS_ENABLED(CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK) || - blk_crypto_config_supported_natively(devs[i], cfg)) { - if (!xchg(&mode->logged_blk_crypto_native, 1)) - pr_info("fscrypt: %s using blk-crypto (native)\n", - mode->friendly_name); - } else if (!xchg(&mode->logged_blk_crypto_fallback, 1)) { - pr_info("fscrypt: %s using blk-crypto-fallback\n", + if (blk_crypto_config_supported_natively(dev, &blk_key->crypto_cfg)) { + if (!xchg(&mode->logged_blk_crypto_native, 1)) + pr_info("fscrypt: %s using blk-crypto (native)\n", mode->friendly_name); - } + } else if (!xchg(&mode->logged_blk_crypto_fallback, 1)) { + pr_info("fscrypt: %s using blk-crypto-fallback\n", + mode->friendly_name); } } -/* Enable inline encryption for this file if supported. */ -int fscrypt_select_encryption_impl(struct fscrypt_inode_info *ci, - bool is_hw_wrapped_key) -{ - const struct inode *inode = ci->ci_inode; - struct super_block *sb = inode->i_sb; - struct blk_crypto_config crypto_cfg; - struct block_device *devs[FSCRYPT_MAX_DEVICES]; - unsigned int num_devs; - unsigned int i; - - /* The file must need contents encryption, not filenames encryption */ - if (!S_ISREG(inode->i_mode)) - return 0; - - /* The crypto mode must have a blk-crypto counterpart */ - if (ci->ci_mode->blk_crypto_mode == BLK_ENCRYPTION_MODE_INVALID) - return 0; - - /* The filesystem must be mounted with -o inlinecrypt */ - if (!(sb->s_flags & SB_INLINECRYPT)) - return 0; - - /* - * On all the filesystem's block devices, blk-crypto must support the - * crypto configuration that the file would use. - */ - crypto_cfg.crypto_mode = ci->ci_mode->blk_crypto_mode; - crypto_cfg.data_unit_size = 1U << ci->ci_data_unit_bits; - crypto_cfg.dun_bytes = fscrypt_get_dun_bytes(ci); - crypto_cfg.key_type = is_hw_wrapped_key ? - BLK_CRYPTO_KEY_TYPE_HW_WRAPPED : BLK_CRYPTO_KEY_TYPE_RAW; - crypto_cfg.flags = BLK_CRYPTO_CFG_ALLOW_HW; - - num_devs = fscrypt_get_devices(sb, devs); - for (i = 0; i < num_devs; i++) { - if (!blk_crypto_config_supported(devs[i], &crypto_cfg)) - return 0; - } - - fscrypt_log_blk_crypto_impl(ci->ci_mode, devs, num_devs, &crypto_cfg); - - ci->ci_inlinecrypt = true; - - return 0; -} - int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key, const u8 *key_bytes, size_t key_size, bool is_hw_wrapped, @@ -135,7 +82,8 @@ int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key, { const struct inode *inode = ci->ci_inode; struct super_block *sb = inode->i_sb; - enum blk_crypto_mode_num crypto_mode = ci->ci_mode->blk_crypto_mode; + bool inlinecrypt = sb->s_flags & SB_INLINECRYPT; + struct fscrypt_mode *mode = ci->ci_mode; enum blk_crypto_key_type key_type = is_hw_wrapped ? BLK_CRYPTO_KEY_TYPE_HW_WRAPPED : BLK_CRYPTO_KEY_TYPE_RAW; struct blk_crypto_key *blk_key; @@ -144,16 +92,28 @@ int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key, unsigned int i; int err; + if (is_hw_wrapped && !inlinecrypt) { + /* + * blk_crypto_init_key() would catch this anyway, but this + * provides a clearer error message. + */ + fscrypt_err( + inode, + "Hardware-wrapped keys require inline encryption (-o inlinecrypt)"); + return -EINVAL; + } + blk_key = kmalloc_obj(*blk_key); if (!blk_key) return -ENOMEM; err = blk_crypto_init_key(blk_key, key_bytes, key_size, key_type, - crypto_mode, fscrypt_get_dun_bytes(ci), + mode->blk_crypto_mode, + fscrypt_get_dun_bytes(ci), 1U << ci->ci_data_unit_bits, - BLK_CRYPTO_CFG_ALLOW_HW); + inlinecrypt ? BLK_CRYPTO_CFG_ALLOW_HW : 0); if (err) { - fscrypt_err(inode, "error %d initializing blk-crypto key", err); + fscrypt_err(inode, "Error %d initializing blk-crypto key", err); goto fail; } @@ -163,9 +123,16 @@ int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key, err = blk_crypto_start_using_key(devs[i], blk_key); if (err) break; + fscrypt_log_blk_crypto_impl(mode, devs[i], blk_key); } if (err) { - fscrypt_err(inode, "error %d starting to use blk-crypto", err); + if (err == -EOPNOTSUPP && is_hw_wrapped) + fscrypt_err( + inode, + "Hardware-wrapped key required, but no suitable inline encryption capabilities are available"); + else + fscrypt_err(inode, + "Error %d starting to use blk-crypto", err); goto fail; } @@ -227,12 +194,6 @@ int fscrypt_derive_sw_secret(struct super_block *sb, return err; } -bool __fscrypt_inode_uses_inline_crypto(const struct inode *inode) -{ - return fscrypt_get_inode_info_raw(inode)->ci_inlinecrypt; -} -EXPORT_SYMBOL_GPL(__fscrypt_inode_uses_inline_crypto); - static void fscrypt_generate_dun(const struct fscrypt_inode_info *ci, loff_t pos, u64 dun[BLK_CRYPTO_DUN_ARRAY_SIZE]) { diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c index cfd348e2252e..c9041f245246 100644 --- a/fs/crypto/keysetup.c +++ b/fs/crypto/keysetup.c @@ -144,9 +144,9 @@ fscrypt_allocate_skcipher(struct fscrypt_mode *mode, const u8 *raw_key, /* * Prepare the crypto transform object or blk-crypto key in @prep_key, given the - * raw key, encryption mode (@ci->ci_mode), flag indicating which encryption - * implementation (fs-layer or blk-crypto) will be used (@ci->ci_inlinecrypt), - * and IV generation method (@ci->ci_policy.flags). + * raw key, encryption mode (@ci->ci_mode), predicate indicating which style of + * key is needed (fscrypt_using_inline_encryption(ci)), IV generation method + * (@ci->ci_policy.flags), and data unit size (@ci->ci_data_unit_bits). */ int fscrypt_prepare_key(struct fscrypt_prepared_key *prep_key, const u8 *raw_key, const struct fscrypt_inode_info *ci) @@ -224,23 +224,8 @@ static int setup_per_mode_enc_key(struct fscrypt_inode_info *ci, u8 raw_mode_key[FSCRYPT_MAX_RAW_KEY_SIZE]; u8 hkdf_info[sizeof(mode_num) + sizeof(sb->s_uuid)]; unsigned int hkdf_infolen = 0; - bool use_hw_wrapped_key = false; int err; - if (mk->mk_secret.is_hw_wrapped && S_ISREG(inode->i_mode)) { - /* Using a hardware-wrapped key for file contents encryption */ - if (!fscrypt_using_inline_encryption(ci)) { - if (sb->s_flags & SB_INLINECRYPT) - fscrypt_warn(ci->ci_inode, - "Hardware-wrapped key required, but no suitable inline encryption capabilities are available"); - else - fscrypt_warn(ci->ci_inode, - "Hardware-wrapped keys require inline encryption (-o inlinecrypt)"); - return -EINVAL; - } - use_hw_wrapped_key = true; - } - prep_key = fscrypt_find_mode_key(mk, hkdf_context, mode_num, ci); if (prep_key) { ci->ci_enc_key = *prep_key; @@ -263,7 +248,7 @@ static int setup_per_mode_enc_key(struct fscrypt_inode_info *ci, new_node->data_unit_bits = ci->ci_data_unit_bits; prep_key = &new_node->key; - if (use_hw_wrapped_key) { + if (mk->mk_secret.is_hw_wrapped && S_ISREG(inode->i_mode)) { err = fscrypt_prepare_inline_crypt_key(prep_key, mk->mk_secret.bytes, mk->mk_secret.size, true, @@ -509,10 +494,6 @@ static int setup_file_encryption_key(struct fscrypt_inode_info *ci, if (ci->ci_policy.version != FSCRYPT_POLICY_V1) return -ENOKEY; - err = fscrypt_select_encryption_impl(ci, false); - if (err) - return err; - /* * As a legacy fallback for v1 policies, search for the key in * the current task's subscribed keyrings too. Don't move this @@ -534,10 +515,6 @@ static int setup_file_encryption_key(struct fscrypt_inode_info *ci, goto out_release_key; } - err = fscrypt_select_encryption_impl(ci, mk->mk_secret.is_hw_wrapped); - if (err) - goto out_release_key; - switch (ci->ci_policy.version) { case FSCRYPT_POLICY_V1: if (WARN_ON_ONCE(mk->mk_secret.is_hw_wrapped)) { diff --git a/fs/ext4/crypto.c b/fs/ext4/crypto.c index f41f320f4437..6b809ac80ef7 100644 --- a/fs/ext4/crypto.c +++ b/fs/ext4/crypto.c @@ -236,6 +236,7 @@ static bool ext4_has_stable_inodes(struct super_block *sb) const struct fscrypt_operations ext4_cryptops = { .inode_info_offs = (int)offsetof(struct ext4_inode_info, i_crypt_info) - (int)offsetof(struct ext4_inode_info, vfs_inode), + .is_block_based = 1, .needs_bounce_pages = 1, .has_32bit_inodes = 1, .supports_subblock_data_units = 1, diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 9760e4efeffe..06e8ae1e5c38 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -3775,6 +3775,7 @@ f2fs_get_devices(struct super_block *sb, static const struct fscrypt_operations f2fs_cryptops = { .inode_info_offs = (int)offsetof(struct f2fs_inode_info, i_crypt_info) - (int)offsetof(struct f2fs_inode_info, vfs_inode), + .is_block_based = 1, .needs_bounce_pages = 1, .has_32bit_inodes = 1, .supports_subblock_data_units = 1, diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index f6b235cd72b4..f57aa9b4faca 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -72,14 +72,15 @@ struct fscrypt_operations { ptrdiff_t inode_info_offs; /* - * If set, then fs/crypto/ will allocate a global bounce page pool the - * first time an encryption key is set up for a file. The bounce page - * pool is required by the following functions: - * - * - fscrypt_encrypt_pagecache_blocks() - * - fscrypt_zeroout_range() for files not using inline crypto - * - * If the filesystem doesn't use those, it doesn't need to set this. + * Set to 1 if the filesystem is block-based. This causes fs/crypto/ to + * set up the key for regular files as a blk_crypto_key. The filesystem + * then uses fscrypt_set_bio_crypt_ctx() and similar functions. + */ + unsigned int is_block_based : 1; + + /* + * Set to 1 if the filesystem uses fscrypt_encrypt_pagecache_blocks(). + * This enables the allocation of the bounce page pool it requires. */ unsigned int needs_bounce_pages : 1; @@ -865,8 +866,6 @@ static inline void fscrypt_set_ops(struct super_block *sb, /* inline_crypt.c */ #ifdef CONFIG_FS_ENCRYPTION_INLINE_CRYPT -bool __fscrypt_inode_uses_inline_crypto(const struct inode *inode); - void fscrypt_set_bio_crypt_ctx(struct bio *bio, const struct inode *inode, loff_t pos, gfp_t gfp_mask); @@ -879,11 +878,6 @@ u64 fscrypt_limit_io_blocks(const struct inode *inode, u64 lblk, u64 nr_blocks); #else /* CONFIG_FS_ENCRYPTION_INLINE_CRYPT */ -static inline bool __fscrypt_inode_uses_inline_crypto(const struct inode *inode) -{ - return false; -} - static inline void fscrypt_set_bio_crypt_ctx(struct bio *bio, const struct inode *inode, loff_t pos, gfp_t gfp_mask) { } @@ -919,7 +913,7 @@ static inline u64 fscrypt_limit_io_blocks(const struct inode *inode, u64 lblk, static inline bool fscrypt_inode_uses_inline_crypto(const struct inode *inode) { return fscrypt_needs_contents_encryption(inode) && - __fscrypt_inode_uses_inline_crypto(inode); + inode->i_sb->s_cop->is_block_based; } /** @@ -934,7 +928,7 @@ static inline bool fscrypt_inode_uses_inline_crypto(const struct inode *inode) static inline bool fscrypt_inode_uses_fs_layer_crypto(const struct inode *inode) { return fscrypt_needs_contents_encryption(inode) && - !__fscrypt_inode_uses_inline_crypto(inode); + !inode->i_sb->s_cop->is_block_based; } /** From d6a782f294031a766f1ec06deb1605967fc06526 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sun, 12 Jul 2026 22:36:57 -0400 Subject: [PATCH 10/29] Documentation: fscrypt: Update docs for inlinecrypt Update the documentation for the inlinecrypt mount option to reflect that it's now just about the choice of whether to use inline encryption hardware, not whether the blk-crypto framework is used. Also remove an outdated statement about the data unit size, and make the ext4 and f2fs docs reference the fscrypt docs rather than the block layer docs directly. Reviewed-by: Christoph Hellwig Link: https://patch.msgid.link/20260713023708.9245-7-ebiggers@kernel.org Signed-off-by: Eric Biggers --- Documentation/admin-guide/ext4.rst | 8 ++--- Documentation/filesystems/f2fs.rst | 10 +++--- Documentation/filesystems/fscrypt.rst | 51 ++++++++++----------------- 3 files changed, 26 insertions(+), 43 deletions(-) diff --git a/Documentation/admin-guide/ext4.rst b/Documentation/admin-guide/ext4.rst index ac0c709ea9e7..742a48e6fc0c 100644 --- a/Documentation/admin-guide/ext4.rst +++ b/Documentation/admin-guide/ext4.rst @@ -385,11 +385,9 @@ When mounting an ext4 filesystem, the following option are accepted: incompatible with data=journal. inlinecrypt - When possible, encrypt/decrypt the contents of encrypted files using the - blk-crypto framework rather than filesystem-layer encryption. This - allows the use of inline encryption hardware. The on-disk format is - unaffected. For more details, see - Documentation/block/inline-encryption.rst. + When possible, encrypt/decrypt the contents of encrypted files using + inline encryption hardware rather than the CPU. For more details, see + Documentation/filesystems/fscrypt.rst. Data Mode ========= diff --git a/Documentation/filesystems/f2fs.rst b/Documentation/filesystems/f2fs.rst index 8c4a14ae444f..b45d7a687625 100644 --- a/Documentation/filesystems/f2fs.rst +++ b/Documentation/filesystems/f2fs.rst @@ -351,12 +351,10 @@ compress_mode=%s Control file compression mode. This supports "fs" and "user" compress_cache Support to use address space of a filesystem managed inode to cache compressed block, in order to improve cache hit ratio of random read. -inlinecrypt When possible, encrypt/decrypt the contents of encrypted - files using the blk-crypto framework rather than - filesystem-layer encryption. This allows the use of - inline encryption hardware. The on-disk format is - unaffected. For more details, see - Documentation/block/inline-encryption.rst. +inlinecrypt When possible, encrypt/decrypt the contents of + encrypted files using inline encryption hardware rather + than the CPU. For more details, see + Documentation/filesystems/fscrypt.rst. atgc Enable age-threshold garbage collection, it provides high effectiveness and efficiency on background GC. discard_unit=%s Control discard unit, the argument can be "block", "segment" diff --git a/Documentation/filesystems/fscrypt.rst b/Documentation/filesystems/fscrypt.rst index 92b8f311e211..5f1b5b53aa16 100644 --- a/Documentation/filesystems/fscrypt.rst +++ b/Documentation/filesystems/fscrypt.rst @@ -1318,32 +1318,20 @@ Inline encryption support Many newer systems (especially mobile SoCs) have *inline encryption hardware* that can encrypt/decrypt data while it is on its way to/from -the storage device. Linux supports inline encryption through a set of -extensions to the block layer called *blk-crypto*. blk-crypto allows -filesystems to attach encryption contexts to bios (I/O requests) to -specify how the data will be encrypted or decrypted in-line. For more -information about blk-crypto, see -:ref:`Documentation/block/inline-encryption.rst `. +the storage device. On supported filesystems (currently ext4 and f2fs), fscrypt can use -blk-crypto instead of the kernel crypto API to encrypt/decrypt file -contents. To enable this, set CONFIG_FS_ENCRYPTION_INLINE_CRYPT=y in -the kernel configuration, and specify the "inlinecrypt" mount option -when mounting the filesystem. +inline encryption hardware instead of the CPU to encrypt/decrypt file +contents. To enable this, specify the "inlinecrypt" mount option when +mounting the filesystem. -Note that the "inlinecrypt" mount option just specifies to use inline -encryption when possible; it doesn't force its use. fscrypt will -still fall back to using the kernel crypto API on files where the -inline encryption hardware doesn't have the needed crypto capabilities -(e.g. support for the needed encryption algorithm and data unit size) -and where blk-crypto-fallback is unusable. (For blk-crypto-fallback -to be usable, it must be enabled in the kernel configuration with -CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK=y, and the file must be -protected by a raw key rather than a hardware-wrapped key.) +This causes the filesystem to use inline encryption hardware whenever +possible, falling back to the CPU only if such hardware is absent or +doesn't provide the needed crypto capabilities. -Currently fscrypt always uses the filesystem block size (which is -usually 4096 bytes) as the data unit size. Therefore, it can only use -inline encryption hardware that supports that data unit size. +For more information about the kernel's support for inline encryption +hardware, see :ref:`Documentation/block/inline-encryption.rst +`. Inline encryption doesn't affect the ciphertext or other aspects of the on-disk format, so users may freely switch back and forth between @@ -1425,10 +1413,8 @@ For direct I/O on an encrypted file to work, the following conditions must be met (in addition to the conditions for direct I/O on an unencrypted file): -* The file must be using inline encryption. Usually this means that - the filesystem must be mounted with ``-o inlinecrypt`` and inline - encryption hardware must be present. However, a software fallback - is also available. For details, see `Inline encryption support`_. +* The filesystem must be block-based. (Before Linux v7.3, the + filesystem also needed to be mounted with ``-o inlinecrypt``.) * The I/O request must be fully aligned to the filesystem block size. This means that the file position the I/O is targeting, the lengths @@ -1555,14 +1541,11 @@ Tests To test fscrypt, use xfstests, which is Linux's de facto standard filesystem test suite. First, run all the tests in the "encrypt" -group on the relevant filesystem(s). One can also run the tests -with the 'inlinecrypt' mount option to test the implementation for -inline encryption support. For example, to test ext4 and +group on the relevant filesystem(s). For example, to test ext4 and f2fs encryption using `kvm-xfstests `_:: kvm-xfstests -c ext4,f2fs -g encrypt - kvm-xfstests -c ext4,f2fs -g encrypt -m inlinecrypt UBIFS encryption can also be tested this way, but it should be done in a separate command, and it takes some time for kvm-xfstests to set up @@ -1584,7 +1567,6 @@ This tests the encrypted I/O paths more thoroughly. To do this with kvm-xfstests, use the "encrypt" filesystem configuration:: kvm-xfstests -c ext4/encrypt,f2fs/encrypt -g auto - kvm-xfstests -c ext4/encrypt,f2fs/encrypt -g auto -m inlinecrypt Because this runs many more tests than "-g encrypt" does, it takes much longer to run; so also consider using `gce-xfstests @@ -1592,4 +1574,9 @@ much longer to run; so also consider using `gce-xfstests instead of kvm-xfstests:: gce-xfstests -c ext4/encrypt,f2fs/encrypt -g auto - gce-xfstests -c ext4/encrypt,f2fs/encrypt -g auto -m inlinecrypt + +To test inline encryption hardware on a platform that supports such +hardware, run xfstests directly with the ``inlinecrypt`` mount option +enabled. For example:: + + EXT_MOUNT_OPTIONS="-o inlinecrypt" ./check -g encrypt From b4e409c18c7971fd7f1dccf5d87c4ac92d969a19 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sun, 12 Jul 2026 22:36:58 -0400 Subject: [PATCH 11/29] ext4: Remove fs-layer file contents en/decryption code Now that fscrypt's file contents en/decryption is always implemented using blk-crypto when the filesystem is block-based, the fs-layer en/decryption code in ext4 is unused code. Remove it. Note that this makes possible some additional cleanups, but they're left to later commits: - Making ext4_bio_write_folio() return void - Renaming bio_post_read_ctx to fsverity_ctx or similar, and allocating the pool only when fsverity support is needed Reviewed-by: Christoph Hellwig Link: https://patch.msgid.link/20260713023708.9245-8-ebiggers@kernel.org Signed-off-by: Eric Biggers --- fs/ext4/crypto.c | 1 - fs/ext4/inode.c | 28 ++-------------- fs/ext4/page-io.c | 68 ++------------------------------------- fs/ext4/readpage.c | 80 ++++------------------------------------------ 4 files changed, 13 insertions(+), 164 deletions(-) diff --git a/fs/ext4/crypto.c b/fs/ext4/crypto.c index 6b809ac80ef7..9265cfe62c83 100644 --- a/fs/ext4/crypto.c +++ b/fs/ext4/crypto.c @@ -237,7 +237,6 @@ const struct fscrypt_operations ext4_cryptops = { .inode_info_offs = (int)offsetof(struct ext4_inode_info, i_crypt_info) - (int)offsetof(struct ext4_inode_info, vfs_inode), .is_block_based = 1, - .needs_bounce_pages = 1, .has_32bit_inodes = 1, .supports_subblock_data_units = 1, .legacy_key_prefix = "ext4:", diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index ce99807c5f5b..8eb2af481129 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -1262,17 +1262,6 @@ int ext4_block_write_begin(handle_t *handle, struct folio *folio, from, to); else folio_zero_new_buffers(folio, from, to); - } else if (fscrypt_inode_uses_fs_layer_crypto(inode)) { - for (i = 0; i < nr_wait; i++) { - int err2; - - err2 = fscrypt_decrypt_pagecache_blocks(folio, - blocksize, bh_offset(wait[i])); - if (err2) { - clear_buffer_uptodate(wait[i]); - err = err2; - } - } } return err; @@ -3829,9 +3818,9 @@ static int ext4_iomap_begin(struct inode *inode, loff_t offset, loff_t length, return ret; out: /* - * When inline encryption is enabled, sometimes I/O to an encrypted file - * has to be broken up to guarantee DUN contiguity. Handle this by - * limiting the length of the mapping returned. + * Sometimes I/O to an encrypted file has to be broken up to guarantee + * DUN contiguity. Handle this by limiting the length of the mapping + * returned. */ map.m_len = fscrypt_limit_io_blocks(inode, map.m_lblk, map.m_len); @@ -4081,17 +4070,6 @@ static struct buffer_head *ext4_load_tail_bh(struct inode *inode, loff_t from) err = ext4_read_bh_lock(bh, 0, true); if (err) goto unlock; - if (fscrypt_inode_uses_fs_layer_crypto(inode)) { - /* We expect the key to be set. */ - BUG_ON(!fscrypt_has_encryption_key(inode)); - err = fscrypt_decrypt_pagecache_blocks(folio, - blocksize, - bh_offset(bh)); - if (err) { - clear_buffer_uptodate(bh); - goto unlock; - } - } } return bh; diff --git a/fs/ext4/page-io.c b/fs/ext4/page-io.c index bc674aa4a656..557f44178d87 100644 --- a/fs/ext4/page-io.c +++ b/fs/ext4/page-io.c @@ -103,18 +103,12 @@ static void ext4_finish_bio(struct bio *bio) bio_for_each_folio_all(fi, bio) { struct folio *folio = fi.folio; - struct folio *io_folio = NULL; struct buffer_head *bh, *head; size_t bio_start = fi.offset; size_t bio_end = bio_start + fi.length; unsigned under_io = 0; unsigned long flags; - if (fscrypt_is_bounce_folio(folio)) { - io_folio = folio; - folio = fscrypt_pagecache_folio(folio); - } - if (bio->bi_status) { int err = blk_status_to_errno(bio->bi_status); mapping_set_error(folio->mapping, err); @@ -139,10 +133,8 @@ static void ext4_finish_bio(struct bio *bio) } } while ((bh = bh->b_this_page) != head); spin_unlock_irqrestore(&head->b_uptodate_lock, flags); - if (!under_io) { - fscrypt_free_bounce_page(&io_folio->page); + if (!under_io) folio_end_writeback(folio); - } } } @@ -453,7 +445,6 @@ static bool io_submit_need_new_bio(struct ext4_io_submit *io, static void io_submit_add_bh(struct ext4_io_submit *io, struct inode *inode, struct folio *folio, - struct folio *io_folio, struct buffer_head *bh) { if (io->io_bio && io_submit_need_new_bio(io, inode, folio, bh)) { @@ -462,7 +453,7 @@ static void io_submit_add_bh(struct ext4_io_submit *io, } if (io->io_bio == NULL) io_submit_init_bio(io, inode, folio, bh); - if (!bio_add_folio(io->io_bio, io_folio, bh->b_size, bh_offset(bh))) + if (!bio_add_folio(io->io_bio, folio, bh->b_size, bh_offset(bh))) goto submit_and_retry; wbc_account_cgroup_owner(io->io_wbc, folio, bh->b_size); io->io_next_block++; @@ -471,11 +462,9 @@ static void io_submit_add_bh(struct ext4_io_submit *io, int ext4_bio_write_folio(struct ext4_io_submit *io, struct folio *folio, size_t len) { - struct folio *io_folio = folio; struct inode *inode = folio->mapping->host; unsigned block_start; struct buffer_head *bh, *head; - int ret = 0; int nr_to_submit = 0; struct writeback_control *wbc = io->io_wbc; bool keep_towrite = false; @@ -549,64 +538,13 @@ int ext4_bio_write_folio(struct ext4_io_submit *io, struct folio *folio, bh = head = folio_buffers(folio); - /* - * If any blocks are being written to an encrypted file, encrypt them - * into a bounce page. For simplicity, just encrypt until the last - * block which might be needed. This may cause some unneeded blocks - * (e.g. holes) to be unnecessarily encrypted, but this is rare and - * can't happen in the common case of blocksize == PAGE_SIZE. - */ - if (fscrypt_inode_uses_fs_layer_crypto(inode)) { - gfp_t gfp_flags = GFP_NOFS; - unsigned int enc_bytes = round_up(len, i_blocksize(inode)); - struct page *bounce_page; - - /* - * Since bounce page allocation uses a mempool, we can only use - * a waiting mask (i.e. request guaranteed allocation) on the - * first page of the bio. Otherwise it can deadlock. - */ - if (io->io_bio) - gfp_flags = GFP_NOWAIT; - retry_encrypt: - bounce_page = fscrypt_encrypt_pagecache_blocks(folio, - enc_bytes, 0, gfp_flags); - if (IS_ERR(bounce_page)) { - ret = PTR_ERR(bounce_page); - if (ret == -ENOMEM && - (io->io_bio || wbc->sync_mode == WB_SYNC_ALL)) { - gfp_t new_gfp_flags = GFP_NOFS; - if (io->io_bio) - ext4_io_submit(io); - else - new_gfp_flags |= __GFP_NOFAIL; - memalloc_retry_wait(gfp_flags); - gfp_flags = new_gfp_flags; - goto retry_encrypt; - } - - printk_ratelimited(KERN_ERR "%s: ret = %d\n", __func__, ret); - folio_redirty_for_writepage(wbc, folio); - do { - if (buffer_async_write(bh)) { - clear_buffer_async_write(bh); - set_buffer_dirty(bh); - } - bh = bh->b_this_page; - } while (bh != head); - - return ret; - } - io_folio = page_folio(bounce_page); - } - __folio_start_writeback(folio, keep_towrite); /* Now submit buffers to write */ do { if (!buffer_async_write(bh)) continue; - io_submit_add_bh(io, inode, folio, io_folio, bh); + io_submit_add_bh(io, inode, folio, bh); } while ((bh = bh->b_this_page) != head); return 0; diff --git a/fs/ext4/readpage.c b/fs/ext4/readpage.c index dd3627c71732..8af183798a33 100644 --- a/fs/ext4/readpage.c +++ b/fs/ext4/readpage.c @@ -52,20 +52,10 @@ static struct kmem_cache *bio_post_read_ctx_cache; static mempool_t *bio_post_read_ctx_pool; -/* postprocessing steps for read bios */ -enum bio_post_read_step { - STEP_INITIAL = 0, - STEP_DECRYPT, - STEP_VERITY, - STEP_MAX, -}; - struct bio_post_read_ctx { struct bio *bio; struct fsverity_info *vi; struct work_struct work; - unsigned int cur_step; - unsigned int enabled_steps; }; static void __read_end_io(struct bio *bio) @@ -79,20 +69,6 @@ static void __read_end_io(struct bio *bio) bio_put(bio); } -static void bio_post_read_processing(struct bio_post_read_ctx *ctx); - -static void decrypt_work(struct work_struct *work) -{ - struct bio_post_read_ctx *ctx = - container_of(work, struct bio_post_read_ctx, work); - struct bio *bio = ctx->bio; - - if (fscrypt_decrypt_bio(bio)) - bio_post_read_processing(ctx); - else - __read_end_io(bio); -} - static void verity_work(struct work_struct *work) { struct bio_post_read_ctx *ctx = @@ -101,13 +77,9 @@ static void verity_work(struct work_struct *work) struct fsverity_info *vi = ctx->vi; /* - * fsverity_verify_bio() may call readahead() again, and although verity - * will be disabled for that, decryption may still be needed, causing - * another bio_post_read_ctx to be allocated. So to guarantee that - * mempool_alloc() never deadlocks we must free the current ctx first. - * This is safe because verity is the last post-read step. + * Free the bio_post_read_ctx right away, since it's no longer needed. + * This relieves the pressure on the mempool as much as possible. */ - BUILD_BUG_ON(STEP_VERITY + 1 != STEP_MAX); mempool_free(ctx, bio_post_read_ctx_pool); bio->bi_private = NULL; @@ -116,39 +88,10 @@ static void verity_work(struct work_struct *work) __read_end_io(bio); } -static void bio_post_read_processing(struct bio_post_read_ctx *ctx) -{ - /* - * We use different work queues for decryption and for verity because - * verity may require reading metadata pages that need decryption, and - * we shouldn't recurse to the same workqueue. - */ - switch (++ctx->cur_step) { - case STEP_DECRYPT: - if (ctx->enabled_steps & (1 << STEP_DECRYPT)) { - INIT_WORK(&ctx->work, decrypt_work); - fscrypt_enqueue_decrypt_work(&ctx->work); - return; - } - ctx->cur_step++; - fallthrough; - case STEP_VERITY: - if (IS_ENABLED(CONFIG_FS_VERITY) && - ctx->enabled_steps & (1 << STEP_VERITY)) { - INIT_WORK(&ctx->work, verity_work); - fsverity_enqueue_verify_work(&ctx->work); - return; - } - ctx->cur_step++; - fallthrough; - default: - __read_end_io(ctx->bio); - } -} - static bool bio_post_read_required(struct bio *bio) { - return bio->bi_private && !bio->bi_status; + return IS_ENABLED(CONFIG_FS_VERITY) && bio->bi_private && + !bio->bi_status; } /* @@ -168,8 +111,8 @@ static void mpage_end_io(struct bio *bio) if (bio_post_read_required(bio)) { struct bio_post_read_ctx *ctx = bio->bi_private; - ctx->cur_step = STEP_INITIAL; - bio_post_read_processing(ctx); + INIT_WORK(&ctx->work, verity_work); + fsverity_enqueue_verify_work(&ctx->work); return; } __read_end_io(bio); @@ -179,22 +122,13 @@ static void ext4_set_bio_post_read_ctx(struct bio *bio, const struct inode *inode, struct fsverity_info *vi) { - unsigned int post_read_steps = 0; - - if (fscrypt_inode_uses_fs_layer_crypto(inode)) - post_read_steps |= 1 << STEP_DECRYPT; - - if (vi) - post_read_steps |= 1 << STEP_VERITY; - - if (post_read_steps) { + if (vi) { /* Due to the mempool, this never fails. */ struct bio_post_read_ctx *ctx = mempool_alloc(bio_post_read_ctx_pool, GFP_NOFS); ctx->bio = bio; ctx->vi = vi; - ctx->enabled_steps = post_read_steps; bio->bi_private = ctx; } } From afcc0bcbe385d2a93e46561514ccca8c5ee29769 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sun, 12 Jul 2026 22:36:59 -0400 Subject: [PATCH 12/29] ext4: Make ext4_bio_write_folio() return void Since the fs-layer file contents encryption implementation was removed, ext4_bio_write_folio() now always returns 0. Change it to return void, and likewise for its caller mpage_submit_folio(). Reviewed-by: Christoph Hellwig Link: https://patch.msgid.link/20260713023708.9245-9-ebiggers@kernel.org Signed-off-by: Eric Biggers --- fs/ext4/ext4.h | 2 +- fs/ext4/inode.c | 32 +++++++++----------------------- fs/ext4/page-io.c | 6 ++---- 3 files changed, 12 insertions(+), 28 deletions(-) diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index b37c136ea3ab..920a8ec1b948 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -3945,7 +3945,7 @@ extern void ext4_io_submit_init(struct ext4_io_submit *io, struct writeback_control *wbc); extern void ext4_end_io_rsv_work(struct work_struct *work); extern void ext4_io_submit(struct ext4_io_submit *io); -int ext4_bio_write_folio(struct ext4_io_submit *io, struct folio *page, +void ext4_bio_write_folio(struct ext4_io_submit *io, struct folio *page, size_t len); extern struct ext4_io_end_vec *ext4_alloc_io_end_vec(ext4_io_end_t *io_end); extern struct ext4_io_end_vec *ext4_last_io_end_vec(ext4_io_end_t *io_end); diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 8eb2af481129..567d62032bc5 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -2064,11 +2064,10 @@ static void mpage_folio_done(struct mpage_da_data *mpd, struct folio *folio) folio_unlock(folio); } -static int mpage_submit_folio(struct mpage_da_data *mpd, struct folio *folio) +static void mpage_submit_folio(struct mpage_da_data *mpd, struct folio *folio) { size_t len; loff_t size; - int err; WARN_ON_ONCE(folio_pos(folio) != mpd->start_pos); folio_clear_dirty_for_io(folio); @@ -2090,9 +2089,7 @@ static int mpage_submit_folio(struct mpage_da_data *mpd, struct folio *folio) if (folio_pos(folio) + len > size && !ext4_verity_in_progress(mpd->inode)) len = size & (len - 1); - err = ext4_bio_write_folio(&mpd->io_submit, folio, len); - - return err; + ext4_bio_write_folio(&mpd->io_submit, folio, len); } #define BH_FLAGS (BIT(BH_Unwritten) | BIT(BH_Delay)) @@ -2169,8 +2166,7 @@ static bool mpage_add_bh_to_extent(struct mpage_da_data *mpd, ext4_lblk_t lblk, * accumulated extent of buffers to map or add buffers in the page to the * extent of buffers to map. The function returns 1 if the caller can continue * by processing the next page, 0 if it should stop adding buffers to the - * extent to map because we cannot extend it anymore. It can also return value - * < 0 in case of error during IO submission. + * extent to map because we cannot extend it anymore. */ static int mpage_process_page_bufs(struct mpage_da_data *mpd, struct buffer_head *head, @@ -2178,7 +2174,6 @@ static int mpage_process_page_bufs(struct mpage_da_data *mpd, ext4_lblk_t lblk) { struct inode *inode = mpd->inode; - int err; ext4_lblk_t blocks = (i_size_read(inode) + i_blocksize(inode) - 1) >> inode->i_blkbits; @@ -2201,9 +2196,7 @@ static int mpage_process_page_bufs(struct mpage_da_data *mpd, } while (lblk++, (bh = bh->b_this_page) != head); /* So far everything mapped? Submit the page for IO. */ if (mpd->map.m_len == 0) { - err = mpage_submit_folio(mpd, head->b_folio); - if (err < 0) - return err; + mpage_submit_folio(mpd, head->b_folio); mpage_folio_done(mpd, head->b_folio); } if (lblk >= blocks) { @@ -2333,9 +2326,7 @@ static int mpage_map_and_submit_buffers(struct mpage_da_data *mpd) if (err < 0 || map_bh) goto out; /* Page fully mapped - let IO run! */ - err = mpage_submit_folio(mpd, folio); - if (err < 0) - goto out; + mpage_submit_folio(mpd, folio); mpage_folio_done(mpd, folio); } folio_batch_release(&fbatch); @@ -2408,7 +2399,6 @@ static int mpage_submit_partial_folio(struct mpage_da_data *mpd) struct inode *inode = mpd->inode; struct folio *folio; loff_t pos; - int ret; folio = filemap_get_folio(inode->i_mapping, mpd->start_pos >> PAGE_SHIFT); @@ -2423,9 +2413,7 @@ static int mpage_submit_partial_folio(struct mpage_da_data *mpd) !folio_contains(folio, pos >> PAGE_SHIFT))) return -EINVAL; - ret = mpage_submit_folio(mpd, folio); - if (ret) - goto out; + mpage_submit_folio(mpd, folio); /* * Update start_pos to prevent this folio from being released in * mpage_release_unused_pages(), it will be reset to the aligned folio @@ -2434,10 +2422,9 @@ static int mpage_submit_partial_folio(struct mpage_da_data *mpd) * entire folio has finished processing. */ mpd->start_pos = pos; -out: folio_unlock(folio); folio_put(folio); - return ret; + return 0; } /* @@ -2724,9 +2711,8 @@ static int mpage_prepare_extent_to_map(struct mpage_da_data *mpd) * through a pin. */ if (!mpd->can_map) { - err = mpage_submit_folio(mpd, folio); - if (err < 0) - goto out; + mpage_submit_folio(mpd, folio); + err = 0; /* Pending dirtying of journalled data? */ if (folio_test_checked(folio)) { err = mpage_journal_page_buffers(handle, diff --git a/fs/ext4/page-io.c b/fs/ext4/page-io.c index 557f44178d87..0236b6b9785a 100644 --- a/fs/ext4/page-io.c +++ b/fs/ext4/page-io.c @@ -459,7 +459,7 @@ static void io_submit_add_bh(struct ext4_io_submit *io, io->io_next_block++; } -int ext4_bio_write_folio(struct ext4_io_submit *io, struct folio *folio, +void ext4_bio_write_folio(struct ext4_io_submit *io, struct folio *folio, size_t len) { struct inode *inode = folio->mapping->host; @@ -533,7 +533,7 @@ int ext4_bio_write_folio(struct ext4_io_submit *io, struct folio *folio, */ __folio_start_writeback(folio, keep_towrite); folio_end_writeback(folio); - return 0; + return; } bh = head = folio_buffers(folio); @@ -546,6 +546,4 @@ int ext4_bio_write_folio(struct ext4_io_submit *io, struct folio *folio, continue; io_submit_add_bh(io, inode, folio, bh); } while ((bh = bh->b_this_page) != head); - - return 0; } From cc2d0cf518c56c9afffa89d07224d16ecc93c56e Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sun, 12 Jul 2026 22:37:00 -0400 Subject: [PATCH 13/29] ext4: Further de-generalize the bio postprocessing code Since the bio postprocessing code in fs/ext4/readpage.c is now used only for fsverity, rename things accordingly. Also: - Don't create the caches at all when !CONFIG_FS_VERITY. - Remove the unused inode argument from ext4_set_verity_work(). Link: https://patch.msgid.link/20260713023708.9245-10-ebiggers@kernel.org Signed-off-by: Eric Biggers --- fs/ext4/ext4.h | 4 +-- fs/ext4/readpage.c | 65 ++++++++++++++++++++++------------------------ fs/ext4/super.c | 6 ++--- 3 files changed, 36 insertions(+), 39 deletions(-) diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 920a8ec1b948..489ed6dcee52 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -3829,8 +3829,8 @@ static inline void ext4_set_de_type(struct super_block *sb, /* readpages.c */ int ext4_read_folio(struct file *file, struct folio *folio); void ext4_readahead(struct readahead_control *rac); -extern int __init ext4_init_post_read_processing(void); -extern void ext4_exit_post_read_processing(void); +int __init ext4_init_verity_caches(void); +void ext4_exit_verity_caches(void); /* symlink.c */ extern const struct inode_operations ext4_encrypted_symlink_inode_operations; diff --git a/fs/ext4/readpage.c b/fs/ext4/readpage.c index 8af183798a33..c7b6cdb2e124 100644 --- a/fs/ext4/readpage.c +++ b/fs/ext4/readpage.c @@ -47,12 +47,12 @@ #include "ext4.h" #include -#define NUM_PREALLOC_POST_READ_CTXS 128 +#define NUM_VERITY_WORKS 128 -static struct kmem_cache *bio_post_read_ctx_cache; -static mempool_t *bio_post_read_ctx_pool; +static struct kmem_cache *ext4_verity_work_cache; +static mempool_t *ext4_verity_work_pool; -struct bio_post_read_ctx { +struct ext4_verity_work { struct bio *bio; struct fsverity_info *vi; struct work_struct work; @@ -65,22 +65,22 @@ static void __read_end_io(struct bio *bio) bio_for_each_folio_all(fi, bio) folio_end_read(fi.folio, bio->bi_status == 0); if (bio->bi_private) - mempool_free(bio->bi_private, bio_post_read_ctx_pool); + mempool_free(bio->bi_private, ext4_verity_work_pool); bio_put(bio); } static void verity_work(struct work_struct *work) { - struct bio_post_read_ctx *ctx = - container_of(work, struct bio_post_read_ctx, work); + struct ext4_verity_work *ctx = + container_of(work, struct ext4_verity_work, work); struct bio *bio = ctx->bio; struct fsverity_info *vi = ctx->vi; /* - * Free the bio_post_read_ctx right away, since it's no longer needed. + * Free the ext4_verity_work right away, since it's no longer needed. * This relieves the pressure on the mempool as much as possible. */ - mempool_free(ctx, bio_post_read_ctx_pool); + mempool_free(ctx, ext4_verity_work_pool); bio->bi_private = NULL; fsverity_verify_bio(vi, bio); @@ -88,12 +88,6 @@ static void verity_work(struct work_struct *work) __read_end_io(bio); } -static bool bio_post_read_required(struct bio *bio) -{ - return IS_ENABLED(CONFIG_FS_VERITY) && bio->bi_private && - !bio->bi_status; -} - /* * I/O completion handler for multipage BIOs. * @@ -108,8 +102,9 @@ static bool bio_post_read_required(struct bio *bio) */ static void mpage_end_io(struct bio *bio) { - if (bio_post_read_required(bio)) { - struct bio_post_read_ctx *ctx = bio->bi_private; + if (IS_ENABLED(CONFIG_FS_VERITY) && bio->bi_private && + !bio->bi_status) { + struct ext4_verity_work *ctx = bio->bi_private; INIT_WORK(&ctx->work, verity_work); fsverity_enqueue_verify_work(&ctx->work); @@ -118,14 +113,12 @@ static void mpage_end_io(struct bio *bio) __read_end_io(bio); } -static void ext4_set_bio_post_read_ctx(struct bio *bio, - const struct inode *inode, - struct fsverity_info *vi) +static void ext4_set_verity_work(struct bio *bio, struct fsverity_info *vi) { if (vi) { /* Due to the mempool, this never fails. */ - struct bio_post_read_ctx *ctx = - mempool_alloc(bio_post_read_ctx_pool, GFP_NOFS); + struct ext4_verity_work *ctx = + mempool_alloc(ext4_verity_work_pool, GFP_NOFS); ctx->bio = bio; ctx->vi = vi; @@ -289,7 +282,7 @@ static int ext4_mpage_readpages(struct inode *inode, struct fsverity_info *vi, bio = bio_alloc(bdev, bio_max_segs(nr_pages), REQ_OP_READ, GFP_KERNEL); fscrypt_set_bio_crypt_ctx(bio, inode, pos, GFP_KERNEL); - ext4_set_bio_post_read_ctx(bio, inode, vi); + ext4_set_verity_work(bio, vi); bio->bi_iter.bi_sector = first_block << (blkbits - 9); bio->bi_end_io = mpage_end_io; if (rac) @@ -363,27 +356,31 @@ void ext4_readahead(struct readahead_control *rac) ext4_mpage_readpages(inode, vi, rac, NULL); } -int __init ext4_init_post_read_processing(void) +int __init ext4_init_verity_caches(void) { - bio_post_read_ctx_cache = KMEM_CACHE(bio_post_read_ctx, SLAB_RECLAIM_ACCOUNT); + if (!IS_ENABLED(CONFIG_FS_VERITY)) + return 0; + ext4_verity_work_cache = + KMEM_CACHE(ext4_verity_work, SLAB_RECLAIM_ACCOUNT); - if (!bio_post_read_ctx_cache) + if (!ext4_verity_work_cache) goto fail; - bio_post_read_ctx_pool = - mempool_create_slab_pool(NUM_PREALLOC_POST_READ_CTXS, - bio_post_read_ctx_cache); - if (!bio_post_read_ctx_pool) + ext4_verity_work_pool = mempool_create_slab_pool( + NUM_VERITY_WORKS, ext4_verity_work_cache); + if (!ext4_verity_work_pool) goto fail_free_cache; return 0; fail_free_cache: - kmem_cache_destroy(bio_post_read_ctx_cache); + kmem_cache_destroy(ext4_verity_work_cache); fail: return -ENOMEM; } -void ext4_exit_post_read_processing(void) +void ext4_exit_verity_caches(void) { - mempool_destroy(bio_post_read_ctx_pool); - kmem_cache_destroy(bio_post_read_ctx_cache); + if (!IS_ENABLED(CONFIG_FS_VERITY)) + return; + mempool_destroy(ext4_verity_work_pool); + kmem_cache_destroy(ext4_verity_work_cache); } diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 245f67d10ded..cb9ca0dc4664 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -7531,7 +7531,7 @@ static int __init ext4_init_fs(void) if (err) goto out7; - err = ext4_init_post_read_processing(); + err = ext4_init_verity_caches(); if (err) goto out6; @@ -7580,7 +7580,7 @@ static int __init ext4_init_fs(void) out4: ext4_exit_pageio(); out5: - ext4_exit_post_read_processing(); + ext4_exit_verity_caches(); out6: ext4_exit_pending(); out7: @@ -7601,7 +7601,7 @@ static void __exit ext4_exit_fs(void) ext4_exit_sysfs(); ext4_exit_system_zone(); ext4_exit_pageio(); - ext4_exit_post_read_processing(); + ext4_exit_verity_caches(); ext4_exit_es(); ext4_exit_pending(); } From 987387f2ec45f8dcd54f02aaf0c4fd3db9c4a598 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sun, 12 Jul 2026 22:37:01 -0400 Subject: [PATCH 14/29] f2fs: Remove fs-layer file contents en/decryption code Now that fscrypt's file contents en/decryption is always implemented using blk-crypto when the filesystem is block-based, the fs-layer en/decryption code in f2fs is unused code. Remove it. Note that the struct f2fs_io_info field encrypted_page is kept because it is still used by the garbage collection path to relocate encrypted blocks using raw meta pages from META_MAPPING. Link: https://patch.msgid.link/20260713023708.9245-11-ebiggers@kernel.org Signed-off-by: Eric Biggers --- fs/f2fs/compress.c | 31 ++-------------- fs/f2fs/data.c | 93 +++++----------------------------------------- fs/f2fs/f2fs.h | 2 - fs/f2fs/segment.c | 2 - fs/f2fs/super.c | 1 - 5 files changed, 12 insertions(+), 117 deletions(-) diff --git a/fs/f2fs/compress.c b/fs/f2fs/compress.c index 91855d91bbdd..ce88092d9ce2 100644 --- a/fs/f2fs/compress.c +++ b/fs/f2fs/compress.c @@ -1286,8 +1286,6 @@ static int f2fs_write_compressed_pages(struct compress_ctx *cc, .compressed_page = NULL, .io_type = io_type, .io_wbc = wbc, - .encrypted = fscrypt_inode_uses_fs_layer_crypto(cc->inode) ? - 1 : 0, }; struct folio *folio; struct dnode_of_data dn; @@ -1361,14 +1359,6 @@ static int f2fs_write_compressed_pages(struct compress_ctx *cc, /* wait for GCed page writeback via META_MAPPING */ f2fs_wait_on_block_writeback(inode, fio.old_blkaddr); - - if (fio.encrypted) { - fio.page = cc->rpages[i + 1]; - err = f2fs_encrypt_one_page(&fio); - if (err) - goto out_destroy_crypt; - cc->cpages[i] = fio.encrypted_page; - } } set_cluster_writeback(cc); @@ -1406,21 +1396,15 @@ static int f2fs_write_compressed_pages(struct compress_ctx *cc, f2fs_bug_on(fio.sbi, blkaddr == NULL_ADDR); - if (fio.encrypted) - fio.encrypted_page = cc->cpages[i - 1]; - else - fio.compressed_page = cc->cpages[i - 1]; + fio.compressed_page = cc->cpages[i - 1]; cc->cpages[i - 1] = NULL; fio.submitted = 0; f2fs_outplace_write_data(&dn, &fio); if (unlikely(!fio.submitted)) { cancel_cluster_writeback(cc, cic, i); - - /* To call fscrypt_finalize_bounce_page */ - i = cc->valid_nr_cpages; *submitted = 0; - goto out_destroy_crypt; + goto out_free_page_array; } (*submitted)++; unlock_continue: @@ -1452,17 +1436,8 @@ static int f2fs_write_compressed_pages(struct compress_ctx *cc, f2fs_destroy_compress_ctx(cc, false); return 0; -out_destroy_crypt: +out_free_page_array: page_array_free(sbi, cic->rpages, cc->cluster_size); - - if (!fio.encrypted) - goto out_put_cic; - - for (--i; i >= 0; i--) { - if (!cc->cpages[i]) - continue; - fscrypt_finalize_bounce_page(&cc->cpages[i]); - } out_put_cic: kmem_cache_free(cic_entry_slab, cic); out_put_dnode: diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index a765fda71536..65117dd2e123 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -65,9 +65,6 @@ bool f2fs_is_cp_guaranteed(const struct folio *folio) struct inode *inode; struct f2fs_sb_info *sbi; - if (fscrypt_is_bounce_folio(folio)) - return folio_test_f2fs_gcing(fscrypt_pagecache_folio(folio)); - inode = mapping->host; sbi = F2FS_I_SB(inode); @@ -101,11 +98,6 @@ static enum count_type __read_io_type(struct folio *folio) /* postprocessing steps for read bios */ enum bio_post_read_step { -#ifdef CONFIG_FS_ENCRYPTION - STEP_DECRYPT = BIT(0), -#else - STEP_DECRYPT = 0, /* compile out the decryption-related code */ -#endif #ifdef CONFIG_F2FS_FS_COMPRESSION STEP_DECOMPRESS = BIT(1), #else @@ -301,11 +293,6 @@ static void f2fs_post_read_work(struct work_struct *work) container_of(work, struct bio_post_read_ctx, work); struct bio *bio = ctx->bio; - if ((ctx->enabled_steps & STEP_DECRYPT) && !fscrypt_decrypt_bio(bio)) { - f2fs_finish_read_bio(bio, true); - return; - } - if (ctx->enabled_steps & STEP_DECOMPRESS) f2fs_handle_step_decompress(ctx, true); @@ -329,18 +316,11 @@ static void f2fs_read_end_io(struct bio *bio) return; } - if (ctx) { - unsigned int enabled_steps = ctx->enabled_steps & - (STEP_DECRYPT | STEP_DECOMPRESS); - - /* - * If we have only decompression step between decompression and - * decrypt, we don't need post processing for this. - */ - if (enabled_steps == STEP_DECOMPRESS && - !f2fs_low_mem_mode(sbi)) { + if (ctx && (ctx->enabled_steps & STEP_DECOMPRESS)) { + if (!f2fs_low_mem_mode(sbi)) { + /* Decompress inline. */ f2fs_handle_step_decompress(ctx, intask); - } else if (enabled_steps) { + } else { INIT_WORK(&ctx->work, f2fs_post_read_work); queue_work(ctx->sbi->wq, &ctx->work); return; @@ -362,13 +342,6 @@ static void f2fs_write_end_bio(struct bio *bio) struct folio *folio = fi.folio; enum count_type type; - if (fscrypt_is_bounce_folio(folio)) { - struct folio *io_folio = folio; - - folio = fscrypt_pagecache_folio(io_folio); - fscrypt_free_bounce_page(&io_folio->page); - } - #ifdef CONFIG_F2FS_FS_COMPRESSION if (f2fs_is_compressed_page(folio)) { f2fs_compress_write_end_io(bio, folio); @@ -614,11 +587,6 @@ static bool __has_merged_page(struct bio *bio, struct inode *inode, bio_for_each_folio_all(fi, bio) { struct folio *target = fi.folio; - if (fscrypt_is_bounce_folio(target)) { - target = fscrypt_pagecache_folio(target); - if (IS_ERR(target)) - continue; - } if (f2fs_is_compressed_page(target)) { target = f2fs_compress_control_folio(target); if (IS_ERR(target)) @@ -1161,9 +1129,6 @@ static struct bio *f2fs_grab_read_bio(struct inode *inode, f2fs_set_bio_crypt_ctx(bio, inode, first_idx, NULL, GFP_NOFS); bio->bi_end_io = f2fs_read_end_io; - if (fscrypt_inode_uses_fs_layer_crypto(inode)) - post_read_steps |= STEP_DECRYPT; - if (vi) post_read_steps |= STEP_VERITY; @@ -2852,35 +2817,6 @@ static void f2fs_readahead(struct readahead_control *rac) f2fs_mpage_readpages(inode, vi, rac, NULL); } -int f2fs_encrypt_one_page(struct f2fs_io_info *fio) -{ - struct inode *inode = fio_inode(fio); - struct folio *mfolio; - struct page *page; - - if (!f2fs_encrypted_file(inode)) - return 0; - - page = fio->compressed_page ? fio->compressed_page : fio->page; - - if (fscrypt_inode_uses_inline_crypto(inode)) - return 0; - - fio->encrypted_page = fscrypt_encrypt_pagecache_blocks(page_folio(page), - PAGE_SIZE, 0, GFP_NOFS); - if (IS_ERR(fio->encrypted_page)) - return PTR_ERR(fio->encrypted_page); - - mfolio = filemap_lock_folio(META_MAPPING(fio->sbi), fio->old_blkaddr); - if (!IS_ERR(mfolio)) { - if (folio_test_uptodate(mfolio)) - memcpy(folio_address(mfolio), - page_address(fio->encrypted_page), PAGE_SIZE); - f2fs_folio_put(mfolio, true); - } - return 0; -} - static inline bool check_inplace_update_policy(struct inode *inode, struct f2fs_io_info *fio) { @@ -3053,22 +2989,15 @@ int f2fs_do_write_data_page(struct f2fs_io_info *fio) if (ipu_force || (__is_valid_data_blkaddr(fio->old_blkaddr) && need_inplace_update(fio))) { - err = f2fs_encrypt_one_page(fio); - if (err) - goto out_writepage; - folio_start_writeback(folio); f2fs_put_dnode(&dn); if (fio->need_lock == LOCK_REQ) f2fs_unlock_op(fio->sbi, &lc); err = f2fs_inplace_write_data(fio); - if (err) { - if (fscrypt_inode_uses_fs_layer_crypto(inode)) - fscrypt_finalize_bounce_page(&fio->encrypted_page); + if (err) folio_end_writeback(folio); - } else { + else set_inode_flag(inode, FI_UPDATE_WRITE); - } trace_f2fs_do_write_data_page(folio, IPU); return err; } @@ -3087,10 +3016,6 @@ int f2fs_do_write_data_page(struct f2fs_io_info *fio) fio->version = ni.version; - err = f2fs_encrypt_one_page(fio); - if (err) - goto out_writepage; - folio_start_writeback(folio); if (fio->compr_blocks && fio->old_blkaddr == COMPRESS_ADDR) @@ -4602,9 +4527,9 @@ static int f2fs_iomap_begin(struct inode *inode, loff_t offset, loff_t length, iomap->offset = F2FS_BLK_TO_BYTES(map.m_lblk); /* - * When inline encryption is enabled, sometimes I/O to an encrypted file - * has to be broken up to guarantee DUN contiguity. Handle this by - * limiting the length of the mapping returned. + * Sometimes I/O to an encrypted file has to be broken up to guarantee + * DUN contiguity. Handle this by limiting the length of the mapping + * returned. */ map.m_len = fscrypt_limit_io_blocks(inode, map.m_lblk, map.m_len); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 8f3e632f315c..8376bbe58ee3 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1364,7 +1364,6 @@ struct f2fs_io_info { unsigned int submitted:1; /* indicate IO submission */ unsigned int in_list:1; /* indicate fio is in io_list */ unsigned int is_por:1; /* indicate IO is from recovery or not */ - unsigned int encrypted:1; /* indicate file is encrypted */ unsigned int meta_gc:1; /* require meta inode GC */ enum iostat_type io_type; /* io type */ struct writeback_control *io_wbc; /* writeback control */ @@ -4199,7 +4198,6 @@ int f2fs_do_write_data_page(struct f2fs_io_info *fio); int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map, int flag); int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, u64 start, u64 len); -int f2fs_encrypt_one_page(struct f2fs_io_info *fio); bool f2fs_should_update_inplace(struct inode *inode, struct f2fs_io_info *fio); bool f2fs_should_update_outplace(struct inode *inode, struct f2fs_io_info *fio); int f2fs_write_single_data_page(struct folio *folio, int *submitted, diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index d71ddb3ee918..642668a1847c 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -3986,8 +3986,6 @@ static void do_write_page(struct f2fs_summary *sum, struct f2fs_io_info *fio) "%s Failed to allocate data block, ino:%u, index:%lu, type:%d, old_blkaddr:0x%x, new_blkaddr:0x%x, err:%d", __func__, fio->ino, folio->index, type, fio->old_blkaddr, fio->new_blkaddr, err); - if (fscrypt_inode_uses_fs_layer_crypto(folio->mapping->host)) - fscrypt_finalize_bounce_page(&fio->encrypted_page); folio_end_writeback(folio); if (f2fs_in_warm_node_list(folio)) f2fs_del_fsync_node_entry(fio->sbi, folio); diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 06e8ae1e5c38..bda2205d608b 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -3776,7 +3776,6 @@ static const struct fscrypt_operations f2fs_cryptops = { .inode_info_offs = (int)offsetof(struct f2fs_inode_info, i_crypt_info) - (int)offsetof(struct f2fs_inode_info, vfs_inode), .is_block_based = 1, - .needs_bounce_pages = 1, .has_32bit_inodes = 1, .supports_subblock_data_units = 1, .legacy_key_prefix = "f2fs:", From 8e72a4f3be91d7d526efe7ff9cf4cd2a4118c17c Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sun, 12 Jul 2026 22:37:02 -0400 Subject: [PATCH 15/29] fs/buffer: Remove fs-layer decryption code Now that fscrypt's file contents en/decryption is always implemented using blk-crypto when the filesystem is block-based, the fs-layer decryption code in fs/buffer.c is unused code. Remove it. Reviewed-by: Jan Kara Reviewed-by: Christian Brauner (Amutable) Reviewed-by: Christoph Hellwig Link: https://patch.msgid.link/20260713023708.9245-12-ebiggers@kernel.org Signed-off-by: Eric Biggers --- fs/buffer.c | 45 ++++++++------------------------------------- 1 file changed, 8 insertions(+), 37 deletions(-) diff --git a/fs/buffer.c b/fs/buffer.c index 9af5f061a1f8..21dd9596a941 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -336,7 +336,7 @@ static void end_buffer_async_read(struct buffer_head *bh, int uptodate) spin_unlock_irqrestore(&first->b_uptodate_lock, flags); } -struct postprocess_bh_ctx { +struct verify_bh_ctx { struct work_struct work; struct buffer_head *bh; struct fsverity_info *vi; @@ -344,8 +344,8 @@ struct postprocess_bh_ctx { static void verify_bh(struct work_struct *work) { - struct postprocess_bh_ctx *ctx = - container_of(work, struct postprocess_bh_ctx, work); + struct verify_bh_ctx *ctx = + container_of(work, struct verify_bh_ctx, work); struct buffer_head *bh = ctx->bh; bool valid; @@ -355,29 +355,6 @@ static void verify_bh(struct work_struct *work) kfree(ctx); } -static void decrypt_bh(struct work_struct *work) -{ - struct postprocess_bh_ctx *ctx = - container_of(work, struct postprocess_bh_ctx, work); - struct buffer_head *bh = ctx->bh; - int err; - - err = fscrypt_decrypt_pagecache_blocks(bh->b_folio, bh->b_size, - bh_offset(bh)); - if (err == 0 && ctx->vi) { - /* - * We use different work queues for decryption and for verity - * because verity may require reading metadata pages that need - * decryption, and we shouldn't recurse to the same workqueue. - */ - INIT_WORK(&ctx->work, verify_bh); - fsverity_enqueue_verify_work(&ctx->work); - return; - } - end_buffer_async_read(bh, err == 0); - kfree(ctx); -} - /* * I/O completion handler for block_read_full_folio() - folios * which come unlocked at the end of I/O. @@ -387,27 +364,21 @@ static void bh_end_async_read(struct bio *bio) struct buffer_head *bh; bool uptodate = bio_endio_bh(bio, &bh); struct inode *inode = bh->b_folio->mapping->host; - bool decrypt = fscrypt_inode_uses_fs_layer_crypto(inode); struct fsverity_info *vi = NULL; /* needed by ext4 */ if (bh->b_folio->index < DIV_ROUND_UP(inode->i_size, PAGE_SIZE)) vi = fsverity_get_info(inode); - /* Decrypt (with fscrypt) and/or verify (with fsverity) if needed. */ - if (uptodate && (decrypt || vi)) { - struct postprocess_bh_ctx *ctx = kmalloc_obj(*ctx, GFP_ATOMIC); + /* Verify (with fsverity) if needed. */ + if (vi && uptodate) { + struct verify_bh_ctx *ctx = kmalloc_obj(*ctx, GFP_ATOMIC); if (ctx) { ctx->bh = bh; ctx->vi = vi; - if (decrypt) { - INIT_WORK(&ctx->work, decrypt_bh); - fscrypt_enqueue_decrypt_work(&ctx->work); - } else { - INIT_WORK(&ctx->work, verify_bh); - fsverity_enqueue_verify_work(&ctx->work); - } + INIT_WORK(&ctx->work, verify_bh); + fsverity_enqueue_verify_work(&ctx->work); return; } uptodate = false; From 170c3aa85d251ccd0f23a022a5efc5b3e105ac95 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sun, 12 Jul 2026 22:37:03 -0400 Subject: [PATCH 16/29] fscrypt: Replace calls to fscrypt_inode_uses_inline_crypto() Now that fscrypt's file contents en/decryption is always implemented using blk-crypto when the filesystem is block-based, the calls to fscrypt_inode_uses_inline_crypto() in fs/crypto/inline_crypt.c (which contains functions that are called only from block-based filesystems) are equivalent to checking whether the file is an encrypted regular file, i.e. fscrypt_needs_contents_encryption(). Use that instead. Reviewed-by: Christoph Hellwig Link: https://patch.msgid.link/20260713023708.9245-13-ebiggers@kernel.org Signed-off-by: Eric Biggers --- fs/crypto/inline_crypt.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/fs/crypto/inline_crypt.c b/fs/crypto/inline_crypt.c index 500ee9079a2a..6b90fd5e8fee 100644 --- a/fs/crypto/inline_crypt.c +++ b/fs/crypto/inline_crypt.c @@ -216,8 +216,8 @@ static void fscrypt_generate_dun(const struct fscrypt_inode_info *ci, * @gfp_mask: memory allocation flags - these must be a waiting mask so that * bio_crypt_set_ctx can't fail. * - * If the contents of the file should be encrypted (or decrypted) with inline - * encryption, then assign the appropriate encryption context to the bio. + * If the contents of the file should be encrypted (or decrypted), then assign + * the appropriate encryption context to the bio. * * Normally the bio should be newly allocated (i.e. no pages added yet), as * otherwise fscrypt_mergeable_bio() won't work as intended. @@ -230,7 +230,7 @@ void fscrypt_set_bio_crypt_ctx(struct bio *bio, const struct inode *inode, const struct fscrypt_inode_info *ci; u64 dun[BLK_CRYPTO_DUN_ARRAY_SIZE]; - if (!fscrypt_inode_uses_inline_crypto(inode)) + if (!fscrypt_needs_contents_encryption(inode)) return; ci = fscrypt_get_inode_info_raw(inode); @@ -245,12 +245,12 @@ EXPORT_SYMBOL_GPL(fscrypt_set_bio_crypt_ctx); * @inode: the inode for the next part of the I/O * @pos: the next file position (in bytes) in the I/O * - * When building a bio which may contain data which should undergo inline - * encryption (or decryption) via fscrypt, filesystems should call this function - * to ensure that the resulting bio contains only contiguous data unit numbers. - * This will return false if the next part of the I/O cannot be merged with the - * bio because either the encryption key would be different or the encryption - * data unit numbers would be discontiguous. + * When building a bio which may contain data which should undergo encryption + * (or decryption) via fscrypt, filesystems should call this function to ensure + * that the resulting bio contains only contiguous data unit numbers. This will + * return false if the next part of the I/O cannot be merged with the bio + * because either the encryption key would be different or the encryption data + * unit numbers would be discontiguous. * * fscrypt_set_bio_crypt_ctx() must have already been called on the bio. * @@ -267,7 +267,7 @@ bool fscrypt_mergeable_bio(struct bio *bio, const struct inode *inode, const struct fscrypt_inode_info *ci; u64 next_dun[BLK_CRYPTO_DUN_ARRAY_SIZE]; - if (!!bc != fscrypt_inode_uses_inline_crypto(inode)) + if (!!bc != fscrypt_needs_contents_encryption(inode)) return false; if (!bc) return true; @@ -325,7 +325,7 @@ bool fscrypt_dio_supported(struct inode *inode) */ return false; } - return fscrypt_inode_uses_inline_crypto(inode); + return true; } EXPORT_SYMBOL_GPL(fscrypt_dio_supported); @@ -354,7 +354,7 @@ u64 fscrypt_limit_io_blocks(const struct inode *inode, u64 lblk, u64 nr_blocks) const struct fscrypt_inode_info *ci; u32 dun; - if (!fscrypt_inode_uses_inline_crypto(inode)) + if (!fscrypt_needs_contents_encryption(inode)) return nr_blocks; if (nr_blocks <= 1) From 35f440660fd53e2aa2e5be4aa5ccd87d388385ea Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sun, 12 Jul 2026 22:37:04 -0400 Subject: [PATCH 17/29] fscrypt: Remove fscrypt_dio_supported() On block-based filesystems, fscrypt file contents encryption is now always implemented using blk-crypto. This implementation supports direct I/O. Therefore, fscrypt_dio_supported() now always returns true, except in the edge case where statx(STATX_DIOALIGN) is called on an encrypted regular file that hasn't had its key set up. But that was really a workaround rather than the desired behavior, so we can disregard it. Thus, fscrypt_dio_supported() is no longer needed. Remove it. Reviewed-by: Christoph Hellwig Link: https://patch.msgid.link/20260713023708.9245-14-ebiggers@kernel.org Signed-off-by: Eric Biggers --- fs/crypto/inline_crypt.c | 43 ---------------------------------------- fs/ext4/inode.c | 11 ++-------- fs/f2fs/file.c | 6 +----- include/linux/fscrypt.h | 7 ------- 4 files changed, 3 insertions(+), 64 deletions(-) diff --git a/fs/crypto/inline_crypt.c b/fs/crypto/inline_crypt.c index 6b90fd5e8fee..57f3496dd36e 100644 --- a/fs/crypto/inline_crypt.c +++ b/fs/crypto/inline_crypt.c @@ -286,49 +286,6 @@ bool fscrypt_mergeable_bio(struct bio *bio, const struct inode *inode, } EXPORT_SYMBOL_GPL(fscrypt_mergeable_bio); -/** - * fscrypt_dio_supported() - check whether DIO (direct I/O) is supported on an - * inode, as far as encryption is concerned - * @inode: the inode in question - * - * Return: %true if there are no encryption constraints that prevent DIO from - * being supported; %false if DIO is unsupported. (Note that in the - * %true case, the filesystem might have other, non-encryption-related - * constraints that prevent DIO from actually being supported. Also, on - * encrypted files the filesystem is still responsible for only allowing - * DIO when requests are filesystem-block-aligned.) - */ -bool fscrypt_dio_supported(struct inode *inode) -{ - int err; - - /* If the file is unencrypted, no veto from us. */ - if (!fscrypt_needs_contents_encryption(inode)) - return true; - - /* - * We only support DIO with inline crypto, not fs-layer crypto. - * - * To determine whether the inode is using inline crypto, we have to set - * up the key if it wasn't already done. This is because in the current - * design of fscrypt, the decision of whether to use inline crypto or - * not isn't made until the inode's encryption key is being set up. In - * the DIO read/write case, the key will always be set up already, since - * the file will be open. But in the case of statx(), the key might not - * be set up yet, as the file might not have been opened yet. - */ - err = fscrypt_require_key(inode); - if (err) { - /* - * Key unavailable or couldn't be set up. This edge case isn't - * worth worrying about; just report that DIO is unsupported. - */ - return false; - } - return true; -} -EXPORT_SYMBOL_GPL(fscrypt_dio_supported); - /** * fscrypt_limit_io_blocks() - limit I/O blocks to avoid discontiguous DUNs * @inode: the file on which I/O is being done diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 567d62032bc5..0e21a2a0faa7 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -6147,11 +6147,8 @@ u32 ext4_dio_alignment(struct inode *inode) return 0; if (ext4_has_inline_data(inode)) return 0; - if (IS_ENCRYPTED(inode)) { - if (!fscrypt_dio_supported(inode)) - return 0; + if (IS_ENCRYPTED(inode)) return i_blocksize(inode); - } return 1; /* use the iomap defaults */ } @@ -6170,11 +6167,7 @@ int ext4_getattr(struct mnt_idmap *idmap, const struct path *path, stat->btime.tv_nsec = ei->i_crtime.tv_nsec; } - /* - * Return the DIO alignment restrictions if requested. We only return - * this information when requested, since on encrypted files it might - * take a fair bit of work to get if the file wasn't opened recently. - */ + /* Return the DIO alignment restrictions if requested. */ if ((request_mask & STATX_DIOALIGN) && S_ISREG(inode->i_mode)) { u32 dio_align = ext4_dio_alignment(inode); diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 4b52c56d71f0..089759366cdc 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -950,8 +950,6 @@ static bool f2fs_force_buffered_io(struct inode *inode, int rw) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); - if (!fscrypt_dio_supported(inode)) - return true; if (fsverity_active(inode)) return true; if (f2fs_compressed_file(inode)) @@ -996,9 +994,7 @@ int f2fs_getattr(struct mnt_idmap *idmap, const struct path *path, } /* - * Return the DIO alignment restrictions if requested. We only return - * this information when requested, since on encrypted files it might - * take a fair bit of work to get if the file wasn't opened recently. + * Return the DIO alignment restrictions if requested. * * f2fs sometimes supports DIO reads but not DIO writes. STATX_DIOALIGN * cannot represent that, so in that case we report no DIO support. diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index f57aa9b4faca..96c7af05424c 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -872,8 +872,6 @@ void fscrypt_set_bio_crypt_ctx(struct bio *bio, const struct inode *inode, bool fscrypt_mergeable_bio(struct bio *bio, const struct inode *inode, loff_t pos); -bool fscrypt_dio_supported(struct inode *inode); - u64 fscrypt_limit_io_blocks(const struct inode *inode, u64 lblk, u64 nr_blocks); #else /* CONFIG_FS_ENCRYPTION_INLINE_CRYPT */ @@ -889,11 +887,6 @@ static inline bool fscrypt_mergeable_bio(struct bio *bio, return true; } -static inline bool fscrypt_dio_supported(struct inode *inode) -{ - return !fscrypt_needs_contents_encryption(inode); -} - static inline u64 fscrypt_limit_io_blocks(const struct inode *inode, u64 lblk, u64 nr_blocks) { From 8c2c9c4063d1cf8ed00ba32ab3677725559f8334 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sun, 12 Jul 2026 22:37:05 -0400 Subject: [PATCH 18/29] fscrypt: Remove fs-layer zeroout code Now that fscrypt's file contents en/decryption is always implemented using blk-crypto when the filesystem is block-based, the fs-layer zeroout code in fs/crypto/bio.c is unused code. Remove it, then fold fscrypt_zeroout_range_inline_crypt() into fscrypt_zeroout_range(). Then make fscrypt_alloc_bounce_page() and fscrypt_crypt_data_unit() static, since they're no longer called from any other file. Reviewed-by: Christoph Hellwig Link: https://patch.msgid.link/20260713023708.9245-15-ebiggers@kernel.org Signed-off-by: Eric Biggers --- fs/crypto/bio.c | 134 +++++++----------------------------- fs/crypto/crypto.c | 14 ++-- fs/crypto/fscrypt_private.h | 5 -- 3 files changed, 32 insertions(+), 121 deletions(-) diff --git a/fs/crypto/bio.c b/fs/crypto/bio.c index d07740680602..58b6b13eeedd 100644 --- a/fs/crypto/bio.c +++ b/fs/crypto/bio.c @@ -69,16 +69,36 @@ static void fscrypt_zeroout_range_end_io(struct bio *bio) bio_put(bio); } -static int fscrypt_zeroout_range_inline_crypt(const struct inode *inode, - loff_t pos, sector_t sector, - u64 len) +/** + * fscrypt_zeroout_range() - zero out a range of blocks in an encrypted file + * @inode: the file's inode + * @pos: the first file position (in bytes) to zero out + * @sector: the first sector to zero out + * @len: bytes to zero out + * + * Zero out filesystem blocks in an encrypted regular file on-disk, i.e. write + * ciphertext blocks which decrypt to the all-zeroes block. The blocks must be + * both logically and physically contiguous. It's also assumed that the + * filesystem only uses a single block device, ->s_bdev. @len must be a + * multiple of the file system logical block size. + * + * Note that since each block uses a different IV, this involves writing a + * different ciphertext to each block; we can't simply reuse the same one. + * + * Return: 0 on success; -errno on failure. + */ +int fscrypt_zeroout_range(const struct inode *inode, loff_t pos, + sector_t sector, u64 len) { struct fscrypt_zero_done done = { .pending = ATOMIC_INIT(1), .done = COMPLETION_INITIALIZER_ONSTACK(done.done), }; - while (len) { + if (len == 0) + return 0; + + do { struct bio *bio; unsigned int n; @@ -102,115 +122,11 @@ static int fscrypt_zeroout_range_inline_crypt(const struct inode *inode, atomic_inc(&done.pending); blk_crypto_submit_bio(bio); - } + } while (len); fscrypt_zeroout_range_done(&done); wait_for_completion(&done.done); return blk_status_to_errno(done.status); } - -/** - * fscrypt_zeroout_range() - zero out a range of blocks in an encrypted file - * @inode: the file's inode - * @pos: the first file position (in bytes) to zero out - * @sector: the first sector to zero out - * @len: bytes to zero out - * - * Zero out filesystem blocks in an encrypted regular file on-disk, i.e. write - * ciphertext blocks which decrypt to the all-zeroes block. The blocks must be - * both logically and physically contiguous. It's also assumed that the - * filesystem only uses a single block device, ->s_bdev. @len must be a - * multiple of the file system logical block size. - * - * Note that since each block uses a different IV, this involves writing a - * different ciphertext to each block; we can't simply reuse the same one. - * - * Return: 0 on success; -errno on failure. - */ -int fscrypt_zeroout_range(const struct inode *inode, loff_t pos, - sector_t sector, u64 len) -{ - const struct fscrypt_inode_info *ci = fscrypt_get_inode_info_raw(inode); - const unsigned int du_bits = ci->ci_data_unit_bits; - const unsigned int du_size = 1U << du_bits; - const unsigned int du_per_page_bits = PAGE_SHIFT - du_bits; - const unsigned int du_per_page = 1U << du_per_page_bits; - u64 du_index = pos >> du_bits; - u64 du_remaining = len >> du_bits; - struct page *pages[16]; /* write up to 16 pages at a time */ - unsigned int nr_pages; - unsigned int i; - unsigned int offset; - struct bio *bio; - int ret, err; - - if (len == 0) - return 0; - - if (fscrypt_inode_uses_inline_crypto(inode)) - return fscrypt_zeroout_range_inline_crypt(inode, pos, sector, - len); - - BUILD_BUG_ON(ARRAY_SIZE(pages) > BIO_MAX_VECS); - nr_pages = min_t(u64, ARRAY_SIZE(pages), - (du_remaining + du_per_page - 1) >> du_per_page_bits); - - /* - * We need at least one page for ciphertext. Allocate the first one - * from a mempool, with __GFP_DIRECT_RECLAIM set so that it can't fail. - * - * Any additional page allocations are allowed to fail, as they only - * help performance, and waiting on the mempool for them could deadlock. - */ - for (i = 0; i < nr_pages; i++) { - pages[i] = fscrypt_alloc_bounce_page(i == 0 ? GFP_NOFS : - GFP_NOWAIT); - if (!pages[i]) - break; - } - nr_pages = i; - if (WARN_ON_ONCE(nr_pages <= 0)) - return -EINVAL; - - /* This always succeeds since __GFP_DIRECT_RECLAIM is set. */ - bio = bio_alloc(inode->i_sb->s_bdev, nr_pages, REQ_OP_WRITE, GFP_NOFS); - - do { - bio->bi_iter.bi_sector = sector; - - i = 0; - offset = 0; - do { - err = fscrypt_crypt_data_unit(ci, FS_ENCRYPT, du_index, - ZERO_PAGE(0), pages[i], - du_size, offset); - if (err) - goto out; - du_index++; - sector += 1U << (du_bits - SECTOR_SHIFT); - du_remaining--; - offset += du_size; - if (offset == PAGE_SIZE || du_remaining == 0) { - ret = bio_add_page(bio, pages[i++], offset, 0); - if (WARN_ON_ONCE(ret != offset)) { - err = -EIO; - goto out; - } - offset = 0; - } - } while (i != nr_pages && du_remaining != 0); - - err = submit_bio_wait(bio); - if (err) - goto out; - bio_reset(bio, inode->i_sb->s_bdev, REQ_OP_WRITE); - } while (du_remaining != 0); - err = 0; -out: - bio_put(bio); - for (i = 0; i < nr_pages; i++) - fscrypt_free_bounce_page(pages[i]); - return err; -} EXPORT_SYMBOL(fscrypt_zeroout_range); diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c index 94dd6c89ddcd..8c4660429418 100644 --- a/fs/crypto/crypto.c +++ b/fs/crypto/crypto.c @@ -49,7 +49,7 @@ void fscrypt_enqueue_decrypt_work(struct work_struct *work) } EXPORT_SYMBOL(fscrypt_enqueue_decrypt_work); -struct page *fscrypt_alloc_bounce_page(gfp_t gfp_flags) +static struct page *fscrypt_alloc_bounce_page(gfp_t gfp_flags) { if (WARN_ON_ONCE(!fscrypt_bounce_page_pool)) { /* @@ -65,8 +65,7 @@ struct page *fscrypt_alloc_bounce_page(gfp_t gfp_flags) * fscrypt_free_bounce_page() - free a ciphertext bounce page * @bounce_page: the bounce page to free, or NULL * - * Free a bounce page that was allocated by fscrypt_encrypt_pagecache_blocks(), - * or by fscrypt_alloc_bounce_page() directly. + * Free a bounce page that was allocated by fscrypt_encrypt_pagecache_blocks(). */ void fscrypt_free_bounce_page(struct page *bounce_page) { @@ -107,10 +106,11 @@ void fscrypt_generate_iv(union fscrypt_iv *iv, u64 index, } /* Encrypt or decrypt a single "data unit" of file contents. */ -int fscrypt_crypt_data_unit(const struct fscrypt_inode_info *ci, - fscrypt_direction_t rw, u64 index, - struct page *src_page, struct page *dest_page, - unsigned int len, unsigned int offs) +static int fscrypt_crypt_data_unit(const struct fscrypt_inode_info *ci, + fscrypt_direction_t rw, u64 index, + struct page *src_page, + struct page *dest_page, unsigned int len, + unsigned int offs) { struct crypto_sync_skcipher *tfm = ci->ci_enc_key.tfm; SYNC_SKCIPHER_REQUEST_ON_STACK(req, tfm); diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index 57b7ae2cfafc..da9040407d4a 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -329,11 +329,6 @@ typedef enum { /* crypto.c */ extern struct kmem_cache *fscrypt_inode_info_cachep; int fscrypt_initialize(struct super_block *sb); -int fscrypt_crypt_data_unit(const struct fscrypt_inode_info *ci, - fscrypt_direction_t rw, u64 index, - struct page *src_page, struct page *dest_page, - unsigned int len, unsigned int offs); -struct page *fscrypt_alloc_bounce_page(gfp_t gfp_flags); void __printf(3, 4) __cold fscrypt_msg(const struct inode *inode, const char *level, const char *fmt, ...); From a43ea998fa57eb31cc4b25c9b8b96ed107898f19 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sun, 12 Jul 2026 22:37:06 -0400 Subject: [PATCH 19/29] fscrypt: Remove unused functions and workqueue Remove functions that are no longer used: - fscrypt_decrypt_bio() - fscrypt_decrypt_pagecache_blocks() - fscrypt_inode_uses_fs_layer_crypto() - fscrypt_inode_uses_inline_crypto() - fscrypt_enqueue_decrypt_work() This makes the decryption workqueue unused, so remove it too. Reviewed-by: Christoph Hellwig Link: https://patch.msgid.link/20260713023708.9245-16-ebiggers@kernel.org Signed-off-by: Eric Biggers --- fs/crypto/bio.c | 32 -------------------- fs/crypto/crypto.c | 65 ----------------------------------------- include/linux/fscrypt.h | 47 ----------------------------- 3 files changed, 144 deletions(-) diff --git a/fs/crypto/bio.c b/fs/crypto/bio.c index 58b6b13eeedd..db095258cfca 100644 --- a/fs/crypto/bio.c +++ b/fs/crypto/bio.c @@ -15,38 +15,6 @@ #include "fscrypt_private.h" -/** - * fscrypt_decrypt_bio() - decrypt the contents of a bio - * @bio: the bio to decrypt - * - * Decrypt the contents of a "read" bio following successful completion of the - * underlying disk read. The bio must be reading a whole number of blocks of an - * encrypted file directly into the page cache. If the bio is reading the - * ciphertext into bounce pages instead of the page cache (for example, because - * the file is also compressed, so decompression is required after decryption), - * then this function isn't applicable. This function may sleep, so it must be - * called from a workqueue rather than from the bio's bi_end_io callback. - * - * Return: %true on success; %false on failure. On failure, bio->bi_status is - * also set to an error status. - */ -bool fscrypt_decrypt_bio(struct bio *bio) -{ - struct folio_iter fi; - - bio_for_each_folio_all(fi, bio) { - int err = fscrypt_decrypt_pagecache_blocks(fi.folio, fi.length, - fi.offset); - - if (err) { - bio->bi_status = errno_to_blk_status(err); - return false; - } - } - return true; -} -EXPORT_SYMBOL(fscrypt_decrypt_bio); - struct fscrypt_zero_done { atomic_t pending; blk_status_t status; diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c index 8c4660429418..27663f4d8705 100644 --- a/fs/crypto/crypto.c +++ b/fs/crypto/crypto.c @@ -38,17 +38,10 @@ MODULE_PARM_DESC(num_prealloc_crypto_pages, static mempool_t *fscrypt_bounce_page_pool = NULL; -static struct workqueue_struct *fscrypt_read_workqueue; static DEFINE_MUTEX(fscrypt_init_mutex); struct kmem_cache *fscrypt_inode_info_cachep; -void fscrypt_enqueue_decrypt_work(struct work_struct *work) -{ - queue_work(fscrypt_read_workqueue, work); -} -EXPORT_SYMBOL(fscrypt_enqueue_decrypt_work); - static struct page *fscrypt_alloc_bounce_page(gfp_t gfp_flags) { if (WARN_ON_ONCE(!fscrypt_bounce_page_pool)) { @@ -238,50 +231,6 @@ int fscrypt_encrypt_block_inplace(const struct inode *inode, struct page *page, } EXPORT_SYMBOL(fscrypt_encrypt_block_inplace); -/** - * fscrypt_decrypt_pagecache_blocks() - Decrypt data from a pagecache folio - * @folio: the pagecache folio containing the data to decrypt - * @len: size of the data to decrypt, in bytes - * @offs: offset within @folio of the data to decrypt, in bytes - * - * Decrypt data that has just been read from an encrypted file. The data must - * be located in a pagecache folio that is still locked and not yet uptodate. - * The length and offset of the data must be aligned to the file's crypto data - * unit size. Alignment to the filesystem block size fulfills this requirement, - * as the filesystem block size is always a multiple of the data unit size. - * - * Return: 0 on success; -errno on failure - */ -int fscrypt_decrypt_pagecache_blocks(struct folio *folio, size_t len, - size_t offs) -{ - const struct inode *inode = folio->mapping->host; - const struct fscrypt_inode_info *ci = fscrypt_get_inode_info_raw(inode); - const unsigned int du_bits = ci->ci_data_unit_bits; - const unsigned int du_size = 1U << du_bits; - u64 index = ((u64)folio->index << (PAGE_SHIFT - du_bits)) + - (offs >> du_bits); - size_t i; - int err; - - if (WARN_ON_ONCE(!folio_test_locked(folio))) - return -EINVAL; - - if (WARN_ON_ONCE(len <= 0 || !IS_ALIGNED(len | offs, du_size))) - return -EINVAL; - - for (i = offs; i < offs + len; i += du_size, index++) { - struct page *page = folio_page(folio, i >> PAGE_SHIFT); - - err = fscrypt_crypt_data_unit(ci, FS_DECRYPT, index, page, - page, du_size, i & ~PAGE_MASK); - if (err) - return err; - } - return 0; -} -EXPORT_SYMBOL(fscrypt_decrypt_pagecache_blocks); - /** * fscrypt_decrypt_block_inplace() - Decrypt a filesystem block in-place * @inode: The inode to which this block belongs @@ -371,20 +320,6 @@ void fscrypt_msg(const struct inode *inode, const char *level, static int __init fscrypt_init(void) { - /* - * Use an unbound workqueue to allow bios to be decrypted in parallel - * even when they happen to complete on the same CPU. This sacrifices - * locality, but it's worthwhile since decryption is CPU-intensive. - * - * Also use a high-priority workqueue to prioritize decryption work, - * which blocks reads from completing, over regular application tasks. - */ - fscrypt_read_workqueue = alloc_workqueue("fscrypt_read_queue", - WQ_UNBOUND | WQ_HIGHPRI, - num_online_cpus()); - if (!fscrypt_read_workqueue) - panic("failed to allocate fscrypt_read_queue"); - fscrypt_inode_info_cachep = KMEM_CACHE(fscrypt_inode_info, SLAB_RECLAIM_ACCOUNT | SLAB_PANIC); diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index 96c7af05424c..03c0f50bb6f6 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -345,7 +345,6 @@ static inline void fscrypt_prepare_dentry(struct dentry *dentry, } /* crypto.c */ -void fscrypt_enqueue_decrypt_work(struct work_struct *); struct page *fscrypt_encrypt_pagecache_blocks(struct folio *folio, size_t len, size_t offs, gfp_t gfp_flags); @@ -353,8 +352,6 @@ int fscrypt_encrypt_block_inplace(const struct inode *inode, struct page *page, unsigned int len, unsigned int offs, u64 lblk_num); -int fscrypt_decrypt_pagecache_blocks(struct folio *folio, size_t len, - size_t offs); int fscrypt_decrypt_block_inplace(const struct inode *inode, struct page *page, unsigned int len, unsigned int offs, u64 lblk_num); @@ -452,7 +449,6 @@ bool fscrypt_match_name(const struct fscrypt_name *fname, u64 fscrypt_fname_siphash(const struct inode *dir, const struct qstr *name); /* bio.c */ -bool fscrypt_decrypt_bio(struct bio *bio); int fscrypt_zeroout_range(const struct inode *inode, loff_t pos, sector_t sector, u64 len); @@ -512,9 +508,6 @@ static inline void fscrypt_prepare_dentry(struct dentry *dentry, } /* crypto.c */ -static inline void fscrypt_enqueue_decrypt_work(struct work_struct *work) -{ -} static inline struct page *fscrypt_encrypt_pagecache_blocks(struct folio *folio, size_t len, size_t offs, gfp_t gfp_flags) @@ -530,12 +523,6 @@ static inline int fscrypt_encrypt_block_inplace(const struct inode *inode, return -EOPNOTSUPP; } -static inline int fscrypt_decrypt_pagecache_blocks(struct folio *folio, - size_t len, size_t offs) -{ - return -EOPNOTSUPP; -} - static inline int fscrypt_decrypt_block_inplace(const struct inode *inode, struct page *page, unsigned int len, @@ -753,10 +740,6 @@ static inline int fscrypt_d_revalidate(struct inode *dir, const struct qstr *nam } /* bio.c */ -static inline bool fscrypt_decrypt_bio(struct bio *bio) -{ - return true; -} static inline int fscrypt_zeroout_range(const struct inode *inode, loff_t pos, sector_t sector, u64 len) @@ -894,36 +877,6 @@ static inline u64 fscrypt_limit_io_blocks(const struct inode *inode, u64 lblk, } #endif /* !CONFIG_FS_ENCRYPTION_INLINE_CRYPT */ -/** - * fscrypt_inode_uses_inline_crypto() - test whether an inode uses inline - * encryption - * @inode: an inode. If encrypted, its key must be set up. - * - * Return: true if the inode requires file contents encryption and if the - * encryption should be done in the block layer via blk-crypto rather - * than in the filesystem layer. - */ -static inline bool fscrypt_inode_uses_inline_crypto(const struct inode *inode) -{ - return fscrypt_needs_contents_encryption(inode) && - inode->i_sb->s_cop->is_block_based; -} - -/** - * fscrypt_inode_uses_fs_layer_crypto() - test whether an inode uses fs-layer - * encryption - * @inode: an inode. If encrypted, its key must be set up. - * - * Return: true if the inode requires file contents encryption and if the - * encryption should be done in the filesystem layer rather than in the - * block layer via blk-crypto. - */ -static inline bool fscrypt_inode_uses_fs_layer_crypto(const struct inode *inode) -{ - return fscrypt_needs_contents_encryption(inode) && - !inode->i_sb->s_cop->is_block_based; -} - /** * fscrypt_has_encryption_key() - check whether an inode has had its key set up * @inode: the inode to check From 1944540c405083a966f898e9096c36888887a49e Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sun, 12 Jul 2026 22:37:07 -0400 Subject: [PATCH 20/29] fscrypt: Merge bio.c and inline_crypt.c into block.c Now that fscrypt always uses blk-crypto on block-based filesystems, there's no meaningful difference between bio.c and inline_crypt.c. Therefore merge the two files into one named block.c. Note: I didn't carry over bio.c's "Copyright (C) 2015, Motorola Mobility", as none of the code that applied to remained. Reviewed-by: Christoph Hellwig Link: https://patch.msgid.link/20260713023708.9245-17-ebiggers@kernel.org Signed-off-by: Eric Biggers --- fs/crypto/Makefile | 3 +- fs/crypto/bio.c | 100 -------------------------- fs/crypto/{inline_crypt.c => block.c} | 96 +++++++++++++++++++++++-- fs/crypto/fscrypt_private.h | 2 +- include/linux/fscrypt.h | 22 +++--- 5 files changed, 101 insertions(+), 122 deletions(-) delete mode 100644 fs/crypto/bio.c rename fs/crypto/{inline_crypt.c => block.c} (79%) diff --git a/fs/crypto/Makefile b/fs/crypto/Makefile index 652c7180ec6d..b03e02f0f09d 100644 --- a/fs/crypto/Makefile +++ b/fs/crypto/Makefile @@ -10,5 +10,4 @@ fscrypto-y := crypto.o \ keysetup_v1.o \ policy.o -fscrypto-$(CONFIG_BLOCK) += bio.o -fscrypto-$(CONFIG_FS_ENCRYPTION_INLINE_CRYPT) += inline_crypt.o +fscrypto-$(CONFIG_BLOCK) += block.o diff --git a/fs/crypto/bio.c b/fs/crypto/bio.c deleted file mode 100644 index db095258cfca..000000000000 --- a/fs/crypto/bio.c +++ /dev/null @@ -1,100 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Utility functions for file contents encryption/decryption on - * block device-based filesystems. - * - * Copyright (C) 2015, Google, Inc. - * Copyright (C) 2015, Motorola Mobility - */ - -#include -#include -#include -#include -#include - -#include "fscrypt_private.h" - -struct fscrypt_zero_done { - atomic_t pending; - blk_status_t status; - struct completion done; -}; - -static void fscrypt_zeroout_range_done(struct fscrypt_zero_done *done) -{ - if (atomic_dec_and_test(&done->pending)) - complete(&done->done); -} - -static void fscrypt_zeroout_range_end_io(struct bio *bio) -{ - struct fscrypt_zero_done *done = bio->bi_private; - - if (bio->bi_status) - cmpxchg(&done->status, 0, bio->bi_status); - fscrypt_zeroout_range_done(done); - bio_put(bio); -} - -/** - * fscrypt_zeroout_range() - zero out a range of blocks in an encrypted file - * @inode: the file's inode - * @pos: the first file position (in bytes) to zero out - * @sector: the first sector to zero out - * @len: bytes to zero out - * - * Zero out filesystem blocks in an encrypted regular file on-disk, i.e. write - * ciphertext blocks which decrypt to the all-zeroes block. The blocks must be - * both logically and physically contiguous. It's also assumed that the - * filesystem only uses a single block device, ->s_bdev. @len must be a - * multiple of the file system logical block size. - * - * Note that since each block uses a different IV, this involves writing a - * different ciphertext to each block; we can't simply reuse the same one. - * - * Return: 0 on success; -errno on failure. - */ -int fscrypt_zeroout_range(const struct inode *inode, loff_t pos, - sector_t sector, u64 len) -{ - struct fscrypt_zero_done done = { - .pending = ATOMIC_INIT(1), - .done = COMPLETION_INITIALIZER_ONSTACK(done.done), - }; - - if (len == 0) - return 0; - - do { - struct bio *bio; - unsigned int n; - - bio = bio_alloc(inode->i_sb->s_bdev, BIO_MAX_VECS, REQ_OP_WRITE, - GFP_NOFS); - bio->bi_iter.bi_sector = sector; - bio->bi_private = &done; - bio->bi_end_io = fscrypt_zeroout_range_end_io; - fscrypt_set_bio_crypt_ctx(bio, inode, pos, GFP_NOFS); - - for (n = 0; n < BIO_MAX_VECS; n++) { - unsigned int bytes_this_page = min(len, PAGE_SIZE); - - __bio_add_page(bio, ZERO_PAGE(0), bytes_this_page, 0); - len -= bytes_this_page; - pos += bytes_this_page; - sector += (bytes_this_page >> SECTOR_SHIFT); - if (!len || !fscrypt_mergeable_bio(bio, inode, pos)) - break; - } - - atomic_inc(&done.pending); - blk_crypto_submit_bio(bio); - } while (len); - - fscrypt_zeroout_range_done(&done); - - wait_for_completion(&done.done); - return blk_status_to_errno(done.status); -} -EXPORT_SYMBOL(fscrypt_zeroout_range); diff --git a/fs/crypto/inline_crypt.c b/fs/crypto/block.c similarity index 79% rename from fs/crypto/inline_crypt.c rename to fs/crypto/block.c index 57f3496dd36e..5193f8ba3ee0 100644 --- a/fs/crypto/inline_crypt.c +++ b/fs/crypto/block.c @@ -1,20 +1,20 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Inline encryption support for fscrypt + * File contents en/decryption on block-based filesystems * * Copyright 2019 Google LLC */ /* - * With "inline encryption", the block layer handles the decryption/encryption - * as part of the bio, instead of the filesystem doing the crypto itself via - * crypto API. See Documentation/block/inline-encryption.rst. fscrypt still - * provides the key and IV to use. + * This file implements fscrypt's file contents en/decryption using blk-crypto + * (Documentation/block/inline-encryption.rst). fscrypt assigns a bio_crypt_ctx + * with a key and IV to each bio, and the block layer does the en/decryption. + * + * This file's exported functions are called only by block-based filesystems. */ #include #include -#include #include #include #include @@ -329,3 +329,87 @@ u64 fscrypt_limit_io_blocks(const struct inode *inode, u64 lblk, u64 nr_blocks) return min_t(u64, nr_blocks, (u64)U32_MAX + 1 - dun); } EXPORT_SYMBOL_GPL(fscrypt_limit_io_blocks); + +struct fscrypt_zero_done { + atomic_t pending; + blk_status_t status; + struct completion done; +}; + +static void fscrypt_zeroout_range_done(struct fscrypt_zero_done *done) +{ + if (atomic_dec_and_test(&done->pending)) + complete(&done->done); +} + +static void fscrypt_zeroout_range_end_io(struct bio *bio) +{ + struct fscrypt_zero_done *done = bio->bi_private; + + if (bio->bi_status) + cmpxchg(&done->status, 0, bio->bi_status); + fscrypt_zeroout_range_done(done); + bio_put(bio); +} + +/** + * fscrypt_zeroout_range() - zero out a range of blocks in an encrypted file + * @inode: the file's inode + * @pos: the first file position (in bytes) to zero out + * @sector: the first sector to zero out + * @len: bytes to zero out + * + * Zero out filesystem blocks in an encrypted regular file on-disk, i.e. write + * ciphertext blocks which decrypt to the all-zeroes block. The blocks must be + * both logically and physically contiguous. It's also assumed that the + * filesystem only uses a single block device, ->s_bdev. @len must be a + * multiple of the file system logical block size. + * + * Note that since each block uses a different IV, this involves writing a + * different ciphertext to each block; we can't simply reuse the same one. + * + * Return: 0 on success; -errno on failure. + */ +int fscrypt_zeroout_range(const struct inode *inode, loff_t pos, + sector_t sector, u64 len) +{ + struct fscrypt_zero_done done = { + .pending = ATOMIC_INIT(1), + .done = COMPLETION_INITIALIZER_ONSTACK(done.done), + }; + + if (len == 0) + return 0; + + do { + struct bio *bio; + unsigned int n; + + bio = bio_alloc(inode->i_sb->s_bdev, BIO_MAX_VECS, REQ_OP_WRITE, + GFP_NOFS); + bio->bi_iter.bi_sector = sector; + bio->bi_private = &done; + bio->bi_end_io = fscrypt_zeroout_range_end_io; + fscrypt_set_bio_crypt_ctx(bio, inode, pos, GFP_NOFS); + + for (n = 0; n < BIO_MAX_VECS; n++) { + unsigned int bytes_this_page = min(len, PAGE_SIZE); + + __bio_add_page(bio, ZERO_PAGE(0), bytes_this_page, 0); + len -= bytes_this_page; + pos += bytes_this_page; + sector += (bytes_this_page >> SECTOR_SHIFT); + if (!len || !fscrypt_mergeable_bio(bio, inode, pos)) + break; + } + + atomic_inc(&done.pending); + blk_crypto_submit_bio(bio); + } while (len); + + fscrypt_zeroout_range_done(&done); + + wait_for_completion(&done.done); + return blk_status_to_errno(done.status); +} +EXPORT_SYMBOL(fscrypt_zeroout_range); diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index da9040407d4a..74329e0953d1 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -395,7 +395,7 @@ void fscrypt_hkdf_expand(const struct hmac_sha512_key *hkdf, u8 context, const u8 *info, unsigned int infolen, u8 *okm, unsigned int okmlen); -/* inline_crypt.c */ +/* block.c */ #ifdef CONFIG_FS_ENCRYPTION_INLINE_CRYPT static inline bool fscrypt_using_inline_encryption(const struct fscrypt_inode_info *ci) diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index 03c0f50bb6f6..e9da70ce3e86 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -448,10 +448,6 @@ bool fscrypt_match_name(const struct fscrypt_name *fname, const u8 *de_name, u32 de_name_len); u64 fscrypt_fname_siphash(const struct inode *dir, const struct qstr *name); -/* bio.c */ -int fscrypt_zeroout_range(const struct inode *inode, loff_t pos, - sector_t sector, u64 len); - /* hooks.c */ int fscrypt_file_open(struct inode *inode, struct file *filp); int __fscrypt_prepare_link(struct inode *inode, struct inode *dir, @@ -739,14 +735,6 @@ static inline int fscrypt_d_revalidate(struct inode *dir, const struct qstr *nam return 1; } -/* bio.c */ - -static inline int fscrypt_zeroout_range(const struct inode *inode, loff_t pos, - sector_t sector, u64 len) -{ - return -EOPNOTSUPP; -} - /* hooks.c */ static inline int fscrypt_file_open(struct inode *inode, struct file *filp) @@ -846,7 +834,7 @@ static inline void fscrypt_set_ops(struct super_block *sb, #endif /* !CONFIG_FS_ENCRYPTION */ -/* inline_crypt.c */ +/* block.c */ #ifdef CONFIG_FS_ENCRYPTION_INLINE_CRYPT void fscrypt_set_bio_crypt_ctx(struct bio *bio, const struct inode *inode, @@ -856,6 +844,8 @@ bool fscrypt_mergeable_bio(struct bio *bio, const struct inode *inode, loff_t pos); u64 fscrypt_limit_io_blocks(const struct inode *inode, u64 lblk, u64 nr_blocks); +int fscrypt_zeroout_range(const struct inode *inode, loff_t pos, + sector_t sector, u64 len); #else /* CONFIG_FS_ENCRYPTION_INLINE_CRYPT */ @@ -875,6 +865,12 @@ static inline u64 fscrypt_limit_io_blocks(const struct inode *inode, u64 lblk, { return nr_blocks; } + +static inline int fscrypt_zeroout_range(const struct inode *inode, loff_t pos, + sector_t sector, u64 len) +{ + return -EOPNOTSUPP; +} #endif /* !CONFIG_FS_ENCRYPTION_INLINE_CRYPT */ /** From b3aa5b308ce562df94bda927c4c29c552357d1ab Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sun, 12 Jul 2026 22:37:08 -0400 Subject: [PATCH 21/29] fscrypt: Add safety checks to non-block-based en/decryption fscrypt_encrypt_pagecache_blocks(), fscrypt_encrypt_block_inplace(), fscrypt_decrypt_block_inplace() would dereference a NULL fscrypt_inode_info pointer if they were to be called on a file that hasn't been opened yet or on a block-based filesystem. Since they have the ability to report errors anyway, add WARN_ON_ONCE checks for this. Reviewed-by: Christoph Hellwig Link: https://patch.msgid.link/20260713023708.9245-18-ebiggers@kernel.org Signed-off-by: Eric Biggers --- fs/crypto/crypto.c | 61 +++++++++++++++++++++++++++++----------------- 1 file changed, 39 insertions(+), 22 deletions(-) diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c index 27663f4d8705..c91eda62f9a4 100644 --- a/fs/crypto/crypto.c +++ b/fs/crypto/crypto.c @@ -105,12 +105,17 @@ static int fscrypt_crypt_data_unit(const struct fscrypt_inode_info *ci, struct page *dest_page, unsigned int len, unsigned int offs) { - struct crypto_sync_skcipher *tfm = ci->ci_enc_key.tfm; - SYNC_SKCIPHER_REQUEST_ON_STACK(req, tfm); + struct crypto_sync_skcipher *tfm; union fscrypt_iv iv; struct scatterlist dst, src; int err; + if (WARN_ON_ONCE(ci == NULL)) /* File hasn't been opened yet? */ + return -ENOKEY; + tfm = ci->ci_enc_key.tfm; + if (WARN_ON_ONCE(tfm == NULL)) /* Called on block-based filesystem? */ + return -ENOKEY; + if (WARN_ON_ONCE(len <= 0)) return -EINVAL; if (WARN_ON_ONCE(len % FSCRYPT_CONTENTS_ALIGNMENT != 0)) @@ -118,18 +123,22 @@ static int fscrypt_crypt_data_unit(const struct fscrypt_inode_info *ci, fscrypt_generate_iv(&iv, index, ci); - skcipher_request_set_callback( - req, CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP, - NULL, NULL); - sg_init_table(&dst, 1); - sg_set_page(&dst, dest_page, len, offs); - sg_init_table(&src, 1); - sg_set_page(&src, src_page, len, offs); - skcipher_request_set_crypt(req, &src, &dst, len, &iv); - if (rw == FS_DECRYPT) - err = crypto_skcipher_decrypt(req); - else - err = crypto_skcipher_encrypt(req); + { + SYNC_SKCIPHER_REQUEST_ON_STACK(req, tfm); + skcipher_request_set_callback(req, + CRYPTO_TFM_REQ_MAY_BACKLOG | + CRYPTO_TFM_REQ_MAY_SLEEP, + NULL, NULL); + sg_init_table(&dst, 1); + sg_set_page(&dst, dest_page, len, offs); + sg_init_table(&src, 1); + sg_set_page(&src, src_page, len, offs); + skcipher_request_set_crypt(req, &src, &dst, len, &iv); + if (rw == FS_DECRYPT) + err = crypto_skcipher_decrypt(req); + else + err = crypto_skcipher_encrypt(req); + } if (err) fscrypt_err(ci->ci_inode, "%scryption failed for data unit %llu: %d", @@ -153,7 +162,7 @@ static int fscrypt_crypt_data_unit(const struct fscrypt_inode_info *ci, * which the plaintext data was located in the source page. Any other parts of * the bounce page will be left uninitialized. * - * This is for use by the filesystem's ->writepages() method. + * This is for use by the ->writepages() method of non-block-based filesystems. * * The bounce page allocation is mempool-backed, so it will always succeed when * @gfp_flags includes __GFP_DIRECT_RECLAIM, e.g. when it's GFP_NOFS. However, @@ -167,14 +176,20 @@ struct page *fscrypt_encrypt_pagecache_blocks(struct folio *folio, { const struct inode *inode = folio->mapping->host; const struct fscrypt_inode_info *ci = fscrypt_get_inode_info_raw(inode); - const unsigned int du_bits = ci->ci_data_unit_bits; - const unsigned int du_size = 1U << du_bits; + unsigned int du_bits; + unsigned int du_size; struct page *ciphertext_page; - u64 index = ((u64)folio->index << (PAGE_SHIFT - du_bits)) + - (offs >> du_bits); + u64 index; unsigned int i; int err; + if (WARN_ON_ONCE(ci == NULL)) /* File hasn't been opened yet? */ + return ERR_PTR(-ENOKEY); + + du_bits = ci->ci_data_unit_bits; + du_size = 1U << du_bits; + index = (folio_pos(folio) + offs) >> du_bits; + VM_BUG_ON_FOLIO(folio_test_large(folio), folio); if (WARN_ON_ONCE(!folio_test_locked(folio))) return ERR_PTR(-EINVAL); @@ -215,7 +230,8 @@ EXPORT_SYMBOL(fscrypt_encrypt_pagecache_blocks); * arbitrary page, not necessarily in the original pagecache page. The @inode * and @lblk_num must be specified, as they can't be determined from @page. * - * This is not compatible with fscrypt_operations::supports_subblock_data_units. + * This function only supports non-block-based filesystems that don't support + * sub-block data units (as indicated by the fscrypt_operations fields). * * Return: 0 on success; -errno on failure */ @@ -245,7 +261,8 @@ EXPORT_SYMBOL(fscrypt_encrypt_block_inplace); * arbitrary page, not necessarily in the original pagecache page. The @inode * and @lblk_num must be specified, as they can't be determined from @page. * - * This is not compatible with fscrypt_operations::supports_subblock_data_units. + * This function only supports non-block-based filesystems that don't support + * sub-block data units (as indicated by the fscrypt_operations fields). * * Return: 0 on success; -errno on failure */ @@ -275,7 +292,7 @@ int fscrypt_initialize(struct super_block *sb) mempool_t *pool; /* pairs with smp_store_release() below */ - if (likely(smp_load_acquire(&fscrypt_bounce_page_pool))) + if (smp_load_acquire(&fscrypt_bounce_page_pool)) return 0; /* No need to allocate a bounce page pool if this FS won't use it. */ From f5bd17a8b558173f54d39b465998b07c05de50ca Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sat, 18 Jul 2026 13:56:06 -0700 Subject: [PATCH 22/29] fscrypt: Replace some variable-size memsets with fixed-size For zeroing IVs and raw keys, remove the misguided optimization of zeroing the actual size used (typically 16 and 64 bytes respectively) instead of the max size (32 and 64 bytes respectively). Using a compile-time constant size allows the compiler to specialize the memset for that size (typically by inlining a few 'mov' instructions), which is more important than zeroing a few extra bytes with these small sizes. Link: https://patch.msgid.link/20260718205606.50713-1-ebiggers@kernel.org Signed-off-by: Eric Biggers --- fs/crypto/crypto.c | 2 +- fs/crypto/keysetup.c | 4 ++-- fs/crypto/keysetup_v1.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c index c91eda62f9a4..5286a124b0d9 100644 --- a/fs/crypto/crypto.c +++ b/fs/crypto/crypto.c @@ -83,7 +83,7 @@ void fscrypt_generate_iv(union fscrypt_iv *iv, u64 index, { u8 flags = fscrypt_policy_flags(&ci->ci_policy); - memset(iv, 0, ci->ci_mode->ivsize); + memset(iv, 0, sizeof(*iv)); if (flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64) { WARN_ON_ONCE(index > U32_MAX); diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c index c9041f245246..892044ebcaca 100644 --- a/fs/crypto/keysetup.c +++ b/fs/crypto/keysetup.c @@ -267,7 +267,7 @@ static int setup_per_mode_enc_key(struct fscrypt_inode_info *ci, hkdf_info, hkdf_infolen, raw_mode_key, mode->keysize); err = fscrypt_prepare_key(prep_key, raw_mode_key, ci); - memzero_explicit(raw_mode_key, mode->keysize); + memzero_explicit(raw_mode_key, sizeof(raw_mode_key)); } if (err) { kfree(new_node); @@ -397,7 +397,7 @@ static int fscrypt_setup_v2_file_key(struct fscrypt_inode_info *ci, ci->ci_nonce, FSCRYPT_FILE_NONCE_SIZE, derived_key, ci->ci_mode->keysize); err = fscrypt_set_per_file_enc_key(ci, derived_key); - memzero_explicit(derived_key, ci->ci_mode->keysize); + memzero_explicit(derived_key, sizeof(derived_key)); } if (err) return err; diff --git a/fs/crypto/keysetup_v1.c b/fs/crypto/keysetup_v1.c index 7e3a58dc4b56..87fe13ccb253 100644 --- a/fs/crypto/keysetup_v1.c +++ b/fs/crypto/keysetup_v1.c @@ -251,7 +251,7 @@ static int setup_v1_file_key_derived(struct fscrypt_inode_info *ci, err = fscrypt_set_per_file_enc_key(ci, derived_key); - memzero_explicit(derived_key, derived_keysize); + memzero_explicit(derived_key, sizeof(derived_key)); /* No need to zeroize 'aes', as its key is not secret. */ return err; } From 7880dbd1198569b4037db58cf58755f5ef891457 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sat, 18 Jul 2026 14:25:26 -0700 Subject: [PATCH 23/29] fscrypt: Update encryption policy version docs Update the wording of the documentation to put v1 encryption policies a bit more firmly in the past, explicitly calling them "deprecated" (which is consistent with the warning message the kernel has printed ever since v5.4). Do the same for FS_IOC_GET_ENCRYPTION_POLICY which supports only v1 policies, and remove the explicit recommendation to fall back to it. Also clarify that reusing master keys across policies isn't the best practice or normal usage, even though it's technically allowed for v2. Link: https://patch.msgid.link/20260718212526.53943-1-ebiggers@kernel.org Signed-off-by: Eric Biggers --- Documentation/filesystems/fscrypt.rst | 40 ++++++++++++--------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/Documentation/filesystems/fscrypt.rst b/Documentation/filesystems/fscrypt.rst index 5f1b5b53aa16..d7b71c5ce286 100644 --- a/Documentation/filesystems/fscrypt.rst +++ b/Documentation/filesystems/fscrypt.rst @@ -188,8 +188,8 @@ attacks: - Non-root users cannot securely remove encryption keys. All the above problems are fixed with v2 encryption policies. For -this reason among others, it is recommended to use v2 encryption -policies on all new encrypted directories. +this reason among others, v1 encryption policies are deprecated. Use +v2 encryption policies on all new encrypted directories. Key hierarchy ============= @@ -305,7 +305,8 @@ included in the IV. Moreover: - For v2 encryption policies, the encryption is done with a per-mode key derived using the KDF. Users may use the same master key for - other v2 encryption policies. + other v2 encryption policies. However, using a distinct master key + for each policy is still the best practice and normal usage. IV_INO_LBLK_64 policies ----------------------- @@ -604,7 +605,9 @@ This structure must be initialized as follows: struct fscrypt_policy_v1 is used or FSCRYPT_POLICY_V2 (2) if struct fscrypt_policy_v2 is used. (Note: we refer to the original policy version as "v1", though its version code is really 0.) - For new encrypted directories, use v2 policies. + For new encrypted directories, use v2 policies, which are supported + since Linux v5.4. v1 policies are deprecated and have several + usability and security problems. - ``contents_encryption_mode`` and ``filenames_encryption_mode`` must be set to constants from ```` which identify the @@ -739,17 +742,6 @@ FS_IOC_SET_ENCRYPTION_POLICY can fail with the following errors: Getting an encryption policy ---------------------------- -Two ioctls are available to get a file's encryption policy: - -- `FS_IOC_GET_ENCRYPTION_POLICY_EX`_ -- `FS_IOC_GET_ENCRYPTION_POLICY`_ - -The extended (_EX) version of the ioctl is more general and is -recommended to use when possible. However, on older kernels only the -original ioctl is available. Applications should try the extended -version, and if it fails with ENOTTY fall back to the original -version. - FS_IOC_GET_ENCRYPTION_POLICY_EX ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -783,7 +775,6 @@ FS_IOC_GET_ENCRYPTION_POLICY_EX can fail with the following errors: - ``ENODATA``: the file is not encrypted - ``ENOTTY``: this type of filesystem does not implement encryption, or this kernel is too old to support FS_IOC_GET_ENCRYPTION_POLICY_EX - (try FS_IOC_GET_ENCRYPTION_POLICY instead) - ``EOPNOTSUPP``: the kernel was not configured with encryption support for this filesystem, or the filesystem superblock has not had encryption enabled on it @@ -799,12 +790,13 @@ check for STATX_ATTR_ENCRYPTED in stx_attributes. FS_IOC_GET_ENCRYPTION_POLICY ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The FS_IOC_GET_ENCRYPTION_POLICY ioctl can also retrieve the -encryption policy, if any, for a directory or regular file. However, -unlike `FS_IOC_GET_ENCRYPTION_POLICY_EX`_, -FS_IOC_GET_ENCRYPTION_POLICY only supports the original policy -version. It takes in a pointer directly to struct fscrypt_policy_v1 -rather than struct fscrypt_get_policy_ex_arg. +The FS_IOC_GET_ENCRYPTION_POLICY ioctl is deprecated. It supports +only v1 encryption policies, which themselves are deprecated. Use +`FS_IOC_GET_ENCRYPTION_POLICY_EX`_ instead. + +FS_IOC_GET_ENCRYPTION_POLICY retrieves the encryption policy for a +directory or regular file, but only if it uses a v1 policy. It takes +in a pointer directly to struct fscrypt_policy_v1. The error codes for FS_IOC_GET_ENCRYPTION_POLICY are the same as those for FS_IOC_GET_ENCRYPTION_POLICY_EX, except that @@ -887,6 +879,10 @@ as follows: To add this type of key, the calling process must have the CAP_SYS_ADMIN capability in the initial user namespace. + (Note that v1 encryption policies are deprecated. The ability to + add a key for v1 encryption policies remains only for compatibility + with existing encrypted directories.) + Alternatively, if the key is being added for use by v2 encryption policies, then ``key_spec.type`` must contain FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER, and ``key_spec.u.identifier`` is From bb6d6487826e0cdefd11faa6a20104a2f0d076af Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sat, 18 Jul 2026 14:46:50 -0700 Subject: [PATCH 24/29] fs: Update outdated comment for SB_INLINECRYPT Update the comment for SB_INLINECRYPT to match the latest code, where SB_INLINECRYPT now controls whether blk-crypto uses inline encryption hardware rather than whether blk-crypto is used. Reviewed-by: Christoph Hellwig Link: https://patch.msgid.link/20260718214655.63186-2-ebiggers@kernel.org Signed-off-by: Eric Biggers --- include/linux/fs/super_types.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/fs/super_types.h b/include/linux/fs/super_types.h index ef7941e9dc79..3bdd7f7fb9e5 100644 --- a/include/linux/fs/super_types.h +++ b/include/linux/fs/super_types.h @@ -300,7 +300,7 @@ struct super_block { #define SB_NODIRATIME BIT(11) /* Do not update directory access times */ #define SB_SILENT BIT(15) #define SB_POSIXACL BIT(16) /* Supports POSIX ACLs */ -#define SB_INLINECRYPT BIT(17) /* Use blk-crypto for encrypted files */ +#define SB_INLINECRYPT BIT(17) /* Use inline crypto hardware if available */ #define SB_KERNMOUNT BIT(22) /* this is a kern_mount call */ #define SB_I_VERSION BIT(23) /* Update inode I_version field */ #define SB_LAZYTIME BIT(25) /* Update the on-disk [acm]times lazily */ From a31b8fee3f7f15dad295bff842b7ca402cf712ef Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sat, 18 Jul 2026 14:46:51 -0700 Subject: [PATCH 25/29] f2fs: Update outdated comment in f2fs_write_begin() Refer to f2fs_set_bio_crypt_ctx() instead of the removed function f2fs_encrypt_one_page(). Reviewed-by: Christoph Hellwig Link: https://patch.msgid.link/20260718214655.63186-3-ebiggers@kernel.org Signed-off-by: Eric Biggers --- fs/f2fs/data.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 65117dd2e123..a88ed125d266 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -3956,7 +3956,7 @@ static int f2fs_write_begin(const struct kiocb *iocb, /* * Although the block may be stored in the COW inode, the folio * belongs to @inode and its data was encrypted (or not) using - * @inode's context (see f2fs_encrypt_one_page()). Read with + * @inode's context (see f2fs_set_bio_crypt_ctx()). Read with * @inode so the post-read decryption decision matches the * folio's owner; otherwise an unencrypted @inode whose COW inode * is encrypted hits a NULL ->i_crypt_info on decryption. From 23395358450b6ff94d09395b2e386dd76841d7ba Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sat, 18 Jul 2026 14:46:52 -0700 Subject: [PATCH 26/29] fscrypt: Remove unused function fscrypt_finalize_bounce_page() fscrypt_finalize_bounce_page() is no longer called, so remove it. Reviewed-by: Christoph Hellwig Link: https://patch.msgid.link/20260718214655.63186-4-ebiggers@kernel.org Signed-off-by: Eric Biggers --- include/linux/fscrypt.h | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index e9da70ce3e86..28f2108e29f3 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -1059,15 +1059,4 @@ static inline int fscrypt_encrypt_symlink(struct inode *inode, return 0; } -/* If *pagep is a bounce page, free it and set *pagep to the pagecache page */ -static inline void fscrypt_finalize_bounce_page(struct page **pagep) -{ - struct page *page = *pagep; - - if (fscrypt_is_bounce_page(page)) { - *pagep = fscrypt_pagecache_page(page); - fscrypt_free_bounce_page(page); - } -} - #endif /* _LINUX_FSCRYPT_H */ From 4f049ede0e0b59c1ee46ae9d07ff13652a36a5f4 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sat, 18 Jul 2026 14:46:53 -0700 Subject: [PATCH 27/29] fscrypt: Update docs for data path Update the "Data path changes" section to accurately document and elaborate on the current implementation of file contents en/decryption. Reviewed-by: Christoph Hellwig Link: https://patch.msgid.link/20260718214655.63186-5-ebiggers@kernel.org Signed-off-by: Eric Biggers --- Documentation/filesystems/fscrypt.rst | 50 ++++++++++++++++++--------- 1 file changed, 34 insertions(+), 16 deletions(-) diff --git a/Documentation/filesystems/fscrypt.rst b/Documentation/filesystems/fscrypt.rst index d7b71c5ce286..f309337fe110 100644 --- a/Documentation/filesystems/fscrypt.rst +++ b/Documentation/filesystems/fscrypt.rst @@ -1471,25 +1471,43 @@ keys`_ and `DIRECT_KEY policies`_. Data path changes ----------------- -When inline encryption is used, filesystems just need to associate -encryption contexts with bios to specify how the block layer or the -inline encryption hardware will encrypt/decrypt the file contents. +The block-based filesystems that support fscrypt, such as ext4 and +f2fs, use blk-crypto (:ref:`inline_encryption`) to implement file +contents encryption and decryption. With blk-crypto, the filesystem +assigns an encryption context to each I/O request it issues to the +contents of an encrypted file. The encryption (for writes) or +decryption (for reads) is handled by the block layer transparently to +the filesystem, using either the CPU or inline encryption hardware. -When inline encryption isn't used, filesystems must encrypt/decrypt -the file contents themselves, as described below: +Non-block-based filesystems can't use blk-crypto, so they make the +calls to the cryptographic algorithms at the filesystem layer instead. -For the read path (->read_folio()) of regular files, filesystems can -read the ciphertext into the page cache and decrypt it in-place. The -folio lock must be held until decryption has finished, to prevent the -folio from becoming visible to userspace prematurely. +Regardless of the layer in which they occur (blk-crypto-fallback or the +filesystem), for CPU-based encryption and decryption of file contents: -For the write path (->writepages()) of regular files, filesystems -cannot encrypt data in-place in the page cache, since the cached -plaintext must be preserved. Instead, filesystems must encrypt into a -temporary buffer or "bounce page", then write out the temporary -buffer. Some filesystems, such as UBIFS, already use temporary -buffers regardless of encryption. Other filesystems, such as ext4 and -F2FS, have to allocate bounce pages specially for encryption. +- For reads, the ciphertext data is read from the storage backend + (block device, network, UBI device, etc.) into the destination + buffers, then decrypted in-place. The destination buffers are + pagecache folios for buffered reads, or application-provided buffers + for direct reads. In either case, the filesystem reports success + only after decryption has successfully completed. + +- For writes, the plaintext data is encrypted from the source buffers + (which cannot be modified) into bounce buffers. Then, the + ciphertext in the bounce buffers is written to the storage backend. + + The source buffers are usually pagecache folios for buffered writes, + or application-provided buffers for direct writes. There are also + some cases (all files on UBIFS, and compressed files on f2fs) where + the filesystem already uses bounce buffers for writes for other + reasons; in these cases the source plaintext data is already in + bounce buffers. UBIFS optimizes this case by encrypting the data + in-place in its existing bounce buffers. + +When inline encryption hardware is used instead of the CPU, reads from +the storage backend logically return plaintext data, and writes accept +plaintext data. In that case the flow is simplified: there's no +scheduling of decryption work, and no bounce buffers are used. Filename hashing and encoding ----------------------------- From fae2c34252cf4aea40d0d74fb41261bdcc581dbb Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sat, 18 Jul 2026 14:46:54 -0700 Subject: [PATCH 28/29] blk-crypto: Remove unused function blk_crypto_config_supported() blk_crypto_config_supported() is no longer called, so remove it. Reviewed-by: Christoph Hellwig Link: https://patch.msgid.link/20260718214655.63186-6-ebiggers@kernel.org Signed-off-by: Eric Biggers --- Documentation/block/inline-encryption.rst | 29 ++++++++--------------- block/blk-crypto.c | 14 ----------- include/linux/blk-crypto.h | 2 -- 3 files changed, 10 insertions(+), 35 deletions(-) diff --git a/Documentation/block/inline-encryption.rst b/Documentation/block/inline-encryption.rst index cae23949a626..0052c7011b48 100644 --- a/Documentation/block/inline-encryption.rst +++ b/Documentation/block/inline-encryption.rst @@ -185,20 +185,12 @@ blk-crypto-fallback is optional and is controlled by the API presented to users of the block layer ========================================= -``blk_crypto_config_supported()`` allows users to check ahead of time whether -inline encryption with particular crypto settings will work on a particular -block_device -- either via hardware or via blk-crypto-fallback. This function -takes in a ``struct blk_crypto_config`` which is like blk_crypto_key, but omits -the actual bytes of the key and instead just contains the algorithm, data unit -size, etc. This function can be useful if blk-crypto-fallback is disabled. - ``blk_crypto_init_key()`` allows users to initialize a blk_crypto_key. Users must call ``blk_crypto_start_using_key()`` before actually starting to use -a blk_crypto_key on a block_device (even if ``blk_crypto_config_supported()`` -was called earlier). This is needed to initialize blk-crypto-fallback if it -will be needed. This must not be called from the data path, as this may have to -allocate resources, which may deadlock in that case. +a blk_crypto_key on a block_device. This is needed to initialize +blk-crypto-fallback if it will be needed. This must not be called from the data +path, as this may have to allocate resources, which may deadlock in that case. Next, to attach an encryption context to a bio, users should call ``bio_crypt_set_ctx()``. This function allocates a bio_crypt_ctx and attaches @@ -220,16 +212,15 @@ any kernel data structures it may be linked into. In summary, for users of the block layer, the lifecycle of a blk_crypto_key is as follows: -1. ``blk_crypto_config_supported()`` (optional) -2. ``blk_crypto_init_key()`` -3. ``blk_crypto_start_using_key()`` -4. ``bio_crypt_set_ctx()`` (potentially many times) -5. ``blk_crypto_evict_key()`` (after all I/O has completed) -6. Zeroize the blk_crypto_key (this has no dedicated function) +1. ``blk_crypto_init_key()`` +2. ``blk_crypto_start_using_key()`` +3. ``bio_crypt_set_ctx()`` (potentially many times) +4. ``blk_crypto_evict_key()`` (after all I/O has completed) +5. Zeroize the blk_crypto_key (this has no dedicated function) If a blk_crypto_key is being used on multiple block_devices, then -``blk_crypto_config_supported()`` (if used), ``blk_crypto_start_using_key()``, -and ``blk_crypto_evict_key()`` must be called on each block_device. +``blk_crypto_start_using_key()`` and ``blk_crypto_evict_key()`` must be called +on each block_device. API presented to device drivers =============================== diff --git a/block/blk-crypto.c b/block/blk-crypto.c index 0fe6ef0eea1d..bc3a9f59574b 100644 --- a/block/blk-crypto.c +++ b/block/blk-crypto.c @@ -386,20 +386,6 @@ bool blk_crypto_config_supported_natively(struct block_device *bdev, return true; } -/* - * Check if bios with @cfg can be en/decrypted by blk-crypto (i.e. either the - * block_device it's submitted to supports inline crypto, or the - * blk-crypto-fallback is enabled and supports the cfg). - */ -bool blk_crypto_config_supported(struct block_device *bdev, - const struct blk_crypto_config *cfg) -{ - if (IS_ENABLED(CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK) && - cfg->key_type == BLK_CRYPTO_KEY_TYPE_RAW) - return true; - return blk_crypto_config_supported_natively(bdev, cfg); -} - /** * blk_crypto_start_using_key() - Start using a blk_crypto_key on a device * @bdev: block device to operate on diff --git a/include/linux/blk-crypto.h b/include/linux/blk-crypto.h index 5f40821f99cd..938ff536838c 100644 --- a/include/linux/blk-crypto.h +++ b/include/linux/blk-crypto.h @@ -171,8 +171,6 @@ void blk_crypto_evict_key(struct block_device *bdev, bool blk_crypto_config_supported_natively(struct block_device *bdev, const struct blk_crypto_config *cfg); -bool blk_crypto_config_supported(struct block_device *bdev, - const struct blk_crypto_config *cfg); int blk_crypto_derive_sw_secret(struct block_device *bdev, const u8 *eph_key, size_t eph_key_size, From 7dd38d9dd7a05329825fe2324d4d8e27ad4b3cec Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sat, 18 Jul 2026 14:46:55 -0700 Subject: [PATCH 29/29] blk-crypto: Update docs for blk-crypto-fallback motivation The "Objective" section of inline-encryption.rst suggests that blk-crypto-fallback is just for testing. That's no longer accurate, so update it accordingly. Also fix a typo later in the document. Reviewed-by: Christoph Hellwig Link: https://patch.msgid.link/20260718214655.63186-7-ebiggers@kernel.org Signed-off-by: Eric Biggers --- Documentation/block/inline-encryption.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Documentation/block/inline-encryption.rst b/Documentation/block/inline-encryption.rst index 0052c7011b48..0df964507f76 100644 --- a/Documentation/block/inline-encryption.rst +++ b/Documentation/block/inline-encryption.rst @@ -37,12 +37,12 @@ initialization vector for each sector, and can be tested for correctness. Objective ========= -We want to support inline encryption in the kernel. To make testing easier, we -also want support for falling back to the kernel crypto API when actual inline -encryption hardware is absent. We also want inline encryption to work with -layered devices like device-mapper and loopback (i.e. we want to be able to use -the inline encryption hardware of the underlying devices if present, or else -fall back to crypto API en/decryption). +We want to support inline encryption hardware in the kernel. The API for using +such hardware should also support a fallback to the CPU, so that users only need +to use a single API and more of the code can be tested without actual hardware. +We also want inline encryption to work with layered devices like device-mapper +and loopback (i.e. we want to be able to use the inline encryption hardware of +the underlying devices if present, or else fall back to the CPU). Constraints and notes ===================== @@ -295,7 +295,7 @@ hardware implementations might not implement both features together correctly, and disallow the combination for now. Whenever a device supports integrity, the kernel will pretend that the device does not support hardware inline encryption (by setting the blk_crypto_profile in the request_queue of the device to NULL). -When the crypto API fallback is enabled, this means that all bios with and +When the crypto API fallback is enabled, this means that all bios with an encryption context will use the fallback, and IO will complete as usual. When the fallback is disabled, a bio with an encryption context will be failed.