diff --git a/arch/x86/crypto/aesni-intel_glue.c b/arch/x86/crypto/aesni-intel_glue.c
index 4f721760ebf1..57d149c223bd 100644
--- a/arch/x86/crypto/aesni-intel_glue.c
+++ b/arch/x86/crypto/aesni-intel_glue.c
@@ -17,6 +17,7 @@
  * Copyright 2024 Google LLC
  */
 
+#include <linux/bio.h>
 #include <linux/hardirq.h>
 #include <linux/types.h>
 #include <linux/module.h>
@@ -480,7 +481,7 @@ xts_crypt_slowpath(struct skcipher_request *req, xts_crypt_func crypt_func)
 
 /* __always_inline to avoid indirect call in fastpath */
 static __always_inline int
-xts_crypt(struct skcipher_request *req, xts_encrypt_iv_func encrypt_iv,
+xts_crypt_one(struct skcipher_request *req, xts_encrypt_iv_func encrypt_iv,
 	  xts_crypt_func crypt_func)
 {
 	struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
@@ -511,6 +512,42 @@ xts_crypt(struct skcipher_request *req, xts_encrypt_iv_func encrypt_iv,
 	return xts_crypt_slowpath(req, crypt_func);
 }
 
+static __always_inline int
+xts_crypt(struct skcipher_request *req, xts_encrypt_iv_func encrypt_iv,
+	  xts_crypt_func crypt_func)
+{
+	unsigned int du_bits = req->cryptlen;
+	unsigned int du_size = 1U << du_bits;
+	__le64 *iv = (void *)req->iv;
+	struct folio_iter fi;
+	struct bio *bio;
+	int err;
+
+	if (!(req->base.flags & CRYPTO_SKCIPHER_REQ_BIO))
+		return xts_crypt_one(req, encrypt_iv, crypt_func);
+
+	bio = (void *)req->src;
+
+	for (bio_first_folio(&fi, bio, 0); fi.folio; bio_next_folio(&fi, bio)) {
+		size_t i = fi.offset;
+
+		for (; i < fi.offset + fi.length; i += du_size) {
+			skcipher_request_set_folio(req, fi.folio, i, fi.folio, i, du_size, iv);
+			err = xts_crypt_one(req, encrypt_iv, crypt_func);
+			if (err)
+				goto out;
+
+			*iv = cpu_to_le64(le64_to_cpu(*iv) + 1);
+		}
+	}
+
+out:
+	req->src = (void *)bio;
+	req->dst = (void *)bio;
+	req->cryptlen = du_bits;
+	return err;
+}
+
 static void aesni_xts_encrypt_iv(const struct crypto_aes_ctx *tweak_key,
 				 u8 iv[AES_BLOCK_SIZE])
 {
diff --git a/fs/crypto/bio.c b/fs/crypto/bio.c
index 0ad8c30b8fa5..9f52dc7f7889 100644
--- a/fs/crypto/bio.c
+++ b/fs/crypto/bio.c
@@ -7,6 +7,7 @@
  * Copyright (C) 2015, Motorola Mobility
  */
 
+#include <crypto/skcipher.h>
 #include <linux/pagemap.h>
 #include <linux/module.h>
 #include <linux/bio.h>
@@ -30,16 +31,49 @@
  */
 bool fscrypt_decrypt_bio(struct bio *bio)
 {
+	struct folio *folio = bio_first_folio_all(bio);
+	const struct inode *inode = folio->mapping->host;
+	const struct fscrypt_inode_info *ci = inode->i_crypt_info;
+	const unsigned int du_bits = ci->ci_data_unit_bits;
+	struct crypto_skcipher *tfm = ci->ci_enc_key.tfm;
+	SKCIPHER_REQUEST_ON_STACK(req, tfm, sizeof(bio));
+	struct bio **ctx = skcipher_request_extra(req);
+	DECLARE_CRYPTO_WAIT(wait);
 	struct folio_iter fi;
+	union fscrypt_iv iv;
+	u64 index;
+	int err;
 
-	bio_for_each_folio_all(fi, bio) {
-		int err = fscrypt_decrypt_pagecache_blocks(fi.folio, fi.length,
-							   fi.offset);
+	*ctx = bio;
 
-		if (err) {
-			bio->bi_status = errno_to_blk_status(err);
-			return false;
-		}
+	bio_first_folio(&fi, bio, 0);
+	if (!fi.folio)
+		return true;
+
+	index = fi.offset;
+	index = ((u64)fi.folio->index << (PAGE_SHIFT - du_bits)) +
+		(index >> du_bits);
+	fscrypt_generate_iv(&iv, index, ci);
+
+	skcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG |
+					   CRYPTO_SKCIPHER_REQ_BIO,
+				      NULL, NULL);
+	skcipher_request_set_crypt(req, (struct scatterlist *)bio,
+				   (struct scatterlist *)bio, du_bits, &iv);
+
+	err = crypto_skcipher_decrypt(req);
+	if (err == -EAGAIN) {
+		req = SKCIPHER_REQUEST_CLONE(req, GFP_ATOMIC);
+		skcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG |
+						   CRYPTO_SKCIPHER_REQ_BIO,
+					      crypto_req_done, &wait);
+		err = crypto_skcipher_decrypt(req);
+	}
+	err = crypto_wait_req(err, &wait);
+	skcipher_request_free(req);
+	if (err) {
+		bio->bi_status = errno_to_blk_status(err);
+		return false;
 	}
 	return true;
 }
diff --git a/include/crypto/skcipher.h b/include/crypto/skcipher.h
index e159ea68124e..931585f864d1 100644
--- a/include/crypto/skcipher.h
+++ b/include/crypto/skcipher.h
@@ -26,6 +26,8 @@
 #define CRYPTO_SKCIPHER_REQ_CONT	0x00000001
 /* Set this bit if the skcipher operation is not final. */
 #define CRYPTO_SKCIPHER_REQ_NOTFINAL	0x00000002
+/* Set this bit if the skcipher is made of bio. */
+#define CRYPTO_SKCIPHER_REQ_BIO		0x00000004
 
 /**
  *	struct skcipher_request - Symmetric key cipher request
