Linux block layer
 help / color / mirror / Atom feed
From: Baokun Li <libaokun@linux.alibaba.com>
To: linux-ext4@vger.kernel.org
Cc: linux-fscrypt@vger.kernel.org, linux-block@vger.kernel.org,
	linux-fsdevel@vger.kernel.org, linux-xfs@vger.kernel.org,
	ebiggers@kernel.org, jaegeuk@kernel.org, axboe@kernel.dk,
	hch@lst.de, djwong@kernel.org, brauner@kernel.org, tytso@mit.edu,
	berto@igalia.com, adilger.kernel@dilger.ca, jack@suse.cz,
	yi.zhang@huawei.com, ojaswin@linux.ibm.com,
	ritesh.list@gmail.com
Subject: [PATCH 04/13] blk-crypto: decrypt one data unit per crypto request
Date: Mon, 12 Oct 2026 01:09:49 +0800	[thread overview]
Message-ID: <20261011170958.1759230-5-libaokun@linux.alibaba.com> (raw)
In-Reply-To: <20261011170958.1759230-1-libaokun@linux.alibaba.com>

__blk_crypto_fallback_decrypt_bio() decrypts the bio one bio_vec at a
time: it walks the bio_vecs with __bio_for_each_segment(), requires each
bio_vec to be data unit aligned and a multiple of the data unit size, and
decrypts every data unit in it with a scatterlist that covers the whole
bio_vec.

That requires a data unit to be contiguous in memory, and cannot express
a data unit that spans more than a page.  In preparation for supporting
such data units, collect the bio_vecs that make up one data unit in the
new blk_crypto_du_bvecs(), and decrypt the data unit with a single crypto
request whose scatterlist spans those bio_vecs.

Add a struct blk_crypto_du_ctx to describe one data unit, with
blk_crypto_du_ctx_init() to initialize it and blk_crypto_du_bvecs() to
fill in its bio_vecs.  The array that holds them is sized for one
bio_vec and lives on the stack, so a data unit that needs more than
one bio_vec is rejected with BLK_STS_INVAL for now.

Also add a blk_crypto_crypt_du() helper that en/decrypts one data unit.
Only the decryption path uses it in this patch; the encryption path will
be restructured the same way in a follow-up patch, with the bounce page
scatterlist as the destination.

No functional change intended.

Signed-off-by: Baokun Li <libaokun@linux.alibaba.com>
---
 block/blk-crypto-fallback.c | 162 ++++++++++++++++++++++++++++++------
 1 file changed, 135 insertions(+), 27 deletions(-)

diff --git a/block/blk-crypto-fallback.c b/block/blk-crypto-fallback.c
index a75daadba4c8..e383444686fc 100644
--- a/block/blk-crypto-fallback.c
+++ b/block/blk-crypto-fallback.c
@@ -242,6 +242,116 @@ static void blk_crypto_dun_to_iv(const u64 dun[BLK_CRYPTO_DUN_ARRAY_SIZE],
 		iv->dun[i] = cpu_to_le64(dun[i]);
 }
 
+/*
+ * Describes one data unit: the bio_vecs that cover it and the
+ * scatterlists to en/decrypt it with.
+ */
+struct blk_crypto_du_ctx {
+	/* Backing store for bvecs, for the one-bio_vec case. */
+	struct bio_vec		stack_bvec;
+	/* Backing store for src_sg and dst_sg, one entry each. */
+	struct scatterlist	stack_sgs[2];
+
+	/* The data unit size in bytes. */
+	unsigned int		du_size;
+	/* Number of entries available in bvecs. */
+	unsigned int		max_bvecs;
+
+	/* The bio_vecs that cover the data unit. */
+	struct bio_vec		*bvecs;
+	/* The scatterlist for each side of the crypto request. */
+	struct scatterlist	*src_sg;
+	struct scatterlist	*dst_sg;
+
+	/* The crypto request to en/decrypt the data unit with. */
+	struct skcipher_request	*ciph_req;
+	/* The data unit's DUN, advanced on each successful crypt. */
+	u64			curr_dun[BLK_CRYPTO_DUN_ARRAY_SIZE];
+};
+
+static void blk_crypto_du_ctx_init(struct blk_crypto_du_ctx *du_ctx,
+				   struct skcipher_request *ciph_req,
+				   const struct bio_crypt_ctx *bc)
+{
+	du_ctx->ciph_req = ciph_req;
+	du_ctx->du_size = bc->bc_key->crypto_cfg.data_unit_size;
+	memcpy(du_ctx->curr_dun, bc->bc_dun, sizeof(bc->bc_dun));
+
+	du_ctx->max_bvecs = 1;
+	du_ctx->bvecs = &du_ctx->stack_bvec;
+	du_ctx->src_sg = &du_ctx->stack_sgs[0];
+	du_ctx->dst_sg = &du_ctx->stack_sgs[1];
+}
+
+/*
+ * En/decrypt one data unit: run the request over the data unit's
+ * scatterlists with its DUN as the IV, and advance the DUN on success.
+ */
+static int blk_crypto_crypt_du(struct blk_crypto_du_ctx *du_ctx, bool encrypt)
+{
+	union blk_crypto_iv iv;
+	int err;
+
+	blk_crypto_dun_to_iv(du_ctx->curr_dun, &iv);
+	skcipher_request_set_crypt(du_ctx->ciph_req,
+				   du_ctx->src_sg, du_ctx->dst_sg,
+				   du_ctx->du_size, iv.bytes);
+
+	if (encrypt)
+		err = crypto_skcipher_encrypt(du_ctx->ciph_req);
+	else
+		err = crypto_skcipher_decrypt(du_ctx->ciph_req);
+	if (err)
+		return -EIO;
+
+	bio_crypt_dun_increment(du_ctx->curr_dun, 1);
+	return 0;
+}
+
+/**
+ * blk_crypto_du_bvecs() - collect the bio_vecs that make up one data unit
+ * @bio: the bio to collect from
+ * @iter: the position in @bio to collect at.  Not modified.
+ * @du_ctx: the context to collect the data unit into.  Uses
+ *	    @du_ctx->du_size and @du_ctx->max_bvecs, and stores the
+ *	    bio_vecs in @du_ctx->bvecs
+ *
+ * Collect the bio_vecs that cover one data unit starting at @iter.  Each
+ * bio_vec is clamped to its page, so a data unit spans several bio_vecs if it
+ * covers several pages or if a bio_vec boundary splits it.
+ *
+ * Return: the number of bio_vecs collected, or 0 if @bio doesn't have a whole
+ *	   data unit at @iter, the data unit doesn't start at a
+ *	   min(@du_ctx->du_size, PAGE_SIZE) boundary, or the data unit
+ *	   needs more than @du_ctx->max_bvecs bio_vecs.
+ */
+static unsigned int blk_crypto_du_bvecs(struct bio *bio,
+					const struct bvec_iter *iter,
+					struct blk_crypto_du_ctx *du_ctx)
+{
+	const unsigned int align = min(du_ctx->du_size, PAGE_SIZE);
+	struct bvec_iter du_iter = *iter;
+	struct bio_vec bv;
+	unsigned int n = 0;
+
+	if (du_iter.bi_size < du_ctx->du_size)
+		return 0;
+
+	/* Walk a copy of the iterator that ends after one data unit. */
+	du_iter.bi_size = du_ctx->du_size;
+
+	__bio_for_each_segment(bv, bio, du_iter, du_iter) {
+		if (n == du_ctx->max_bvecs)
+			return 0;
+		if (!n && !IS_ALIGNED(bv.bv_offset, align))
+			return 0;
+
+		du_ctx->bvecs[n++] = bv;
+	}
+
+	return n;
+}
+
 static void __blk_crypto_fallback_encrypt_bio(struct bio *src_bio,
 		struct crypto_sync_skcipher *tfm)
 {
@@ -378,42 +488,40 @@ static blk_status_t __blk_crypto_fallback_decrypt_bio(struct bio *bio,
 		struct crypto_sync_skcipher *tfm)
 {
 	SYNC_SKCIPHER_REQUEST_ON_STACK(ciph_req, tfm);
-	u64 curr_dun[BLK_CRYPTO_DUN_ARRAY_SIZE];
-	union blk_crypto_iv iv;
-	struct scatterlist sg;
-	struct bio_vec bv;
-	const int data_unit_size = bc->bc_key->crypto_cfg.data_unit_size;
-	unsigned int i;
+	struct blk_crypto_du_ctx du_ctx;
+	blk_status_t status = BLK_STS_OK;
+	unsigned int i, n;
 
 	skcipher_request_set_callback(ciph_req,
 			CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
 			NULL, NULL);
+	blk_crypto_du_ctx_init(&du_ctx, ciph_req, bc);
+	/* Decrypt in place, so both sides use the same scatterlist. */
+	du_ctx.dst_sg = du_ctx.src_sg;
+
+	/* Decrypt each data unit in the bio, in place */
+	while (iter.bi_size) {
+		n = blk_crypto_du_bvecs(bio, &iter, &du_ctx);
+		if (!n) {
+			status = BLK_STS_INVAL;
+			break;
+		}
 
-	memcpy(curr_dun, bc->bc_dun, sizeof(curr_dun));
-	sg_init_table(&sg, 1);
-	skcipher_request_set_crypt(ciph_req, &sg, &sg, data_unit_size,
-				   iv.bytes);
-
-	/* Decrypt each segment in the bio */
-	__bio_for_each_segment(bv, bio, iter, iter) {
-		struct page *page = bv.bv_page;
-
-		if (!IS_ALIGNED(bv.bv_len | bv.bv_offset, data_unit_size))
-			return BLK_STS_INVAL;
-
-		sg_set_page(&sg, page, data_unit_size, bv.bv_offset);
+		sg_init_table(du_ctx.src_sg, n);
+		for (i = 0; i < n; i++)
+			sg_set_page(&du_ctx.src_sg[i], du_ctx.bvecs[i].bv_page,
+				    du_ctx.bvecs[i].bv_len,
+				    du_ctx.bvecs[i].bv_offset);
 
-		/* Decrypt each data unit in the segment */
-		for (i = 0; i < bv.bv_len; i += data_unit_size) {
-			blk_crypto_dun_to_iv(curr_dun, &iv);
-			if (crypto_skcipher_decrypt(ciph_req))
-				return BLK_STS_IOERR;
-			bio_crypt_dun_increment(curr_dun, 1);
-			sg.offset += data_unit_size;
+		if (blk_crypto_crypt_du(&du_ctx, false)) {
+			status = BLK_STS_IOERR;
+			break;
 		}
+
+		bio_advance_iter_single(bio, &iter, du_ctx.du_size);
 	}
 
-	return BLK_STS_OK;
+	return status;
 }
 
 /*
-- 
2.43.7


  parent reply	other threads:[~2026-10-11 17:10 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-11 17:09 [PATCH 00/13] block, ext4: allow encryption with a block size larger than the page size Baokun Li
2026-10-11 17:09 ` [PATCH 01/13] ext4: refuse encryption when block size > " Baokun Li
2026-10-11 17:09 ` [PATCH 02/13] ext4: reject encrypted inodes " Baokun Li
2026-10-11 17:09 ` [PATCH 03/13] blk-crypto: reject fallback data unit sizes it can't handle Baokun Li
     [not found]   ` <sashiko-outbox-167341@kernel.org>
2026-10-12  7:50     ` Baokun Li
2026-10-11 17:09 ` Baokun Li [this message]
2026-10-11 17:09 ` [PATCH 05/13] blk-crypto: encrypt one data unit per crypto request Baokun Li
2026-10-11 17:09 ` [PATCH 06/13] blk-crypto: support data units larger than a page Baokun Li
2026-10-11 17:09 ` [PATCH 07/13] iomap: align the direct I/O bios of encrypted files to the block size Baokun Li
2026-10-11 17:09 ` [PATCH 08/13] block: split encrypted bios at data unit boundaries Baokun Li
2026-10-11 17:09 ` [PATCH 09/13] block: warn about bios that can't be split Baokun Li
2026-10-11 17:09 ` [PATCH 10/13] block: don't require data unit aligned bio_vecs for encrypted bios Baokun Li
     [not found]   ` <sashiko-outbox-167346@kernel.org>
2026-10-12  8:09     ` Baokun Li
2026-10-11 17:09 ` [PATCH 11/13] ext4: allow encryption with a block size larger than the page size Baokun Li
2026-10-11 17:09 ` [PATCH 12/13] ext4: send folios that cover a wraparound to the buffer_head path Baokun Li
2026-10-11 17:09 ` [PATCH 13/13] ext4: allow large folios for encrypted files Baokun Li

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261011170958.1759230-5-libaokun@linux.alibaba.com \
    --to=libaokun@linux.alibaba.com \
    --cc=adilger.kernel@dilger.ca \
    --cc=axboe@kernel.dk \
    --cc=berto@igalia.com \
    --cc=brauner@kernel.org \
    --cc=djwong@kernel.org \
    --cc=ebiggers@kernel.org \
    --cc=hch@lst.de \
    --cc=jack@suse.cz \
    --cc=jaegeuk@kernel.org \
    --cc=linux-block@vger.kernel.org \
    --cc=linux-ext4@vger.kernel.org \
    --cc=linux-fscrypt@vger.kernel.org \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-xfs@vger.kernel.org \
    --cc=ojaswin@linux.ibm.com \
    --cc=ritesh.list@gmail.com \
    --cc=tytso@mit.edu \
    --cc=yi.zhang@huawei.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox