Re: [PATCH v9 02/11] block: Inline encryption support for blk-mq
From: Eric Biggers <ebiggers@kernel.org>
Date: 2020-03-26 20:05:15
Also in:
linux-ext4, linux-f2fs-devel, linux-fscrypt, linux-fsdevel, linux-scsi
On Wed, Mar 25, 2020 at 08:06:53PM -0700, Satya Tangirala wrote:
quoted hunk ↗ jump to hunk
diff --git a/block/blk-crypto-internal.h b/block/blk-crypto-internal.h new file mode 100644 index 0000000000000..1e6d339fe1429 --- /dev/null +++ b/block/blk-crypto-internal.h@@ -0,0 +1,172 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2019 Google LLC + */ + +#ifndef __LINUX_BLK_CRYPTO_INTERNAL_H +#define __LINUX_BLK_CRYPTO_INTERNAL_H + +#include <linux/bio.h> +#include <linux/blkdev.h> + +/* Represents a crypto mode supported by blk-crypto */ +struct blk_crypto_mode { + const char *cipher_str; /* crypto API name (for fallback case) */
Technically, cipher_str shouldn't be added until the patch that adds blk-crypto-fallback.
+ unsigned int keysize; /* key size in bytes */
+ unsigned int ivsize; /* iv size in bytes */
+};
+
+#ifdef CONFIG_BLK_INLINE_ENCRYPTION
+
+static inline const struct blk_crypto_key *bio_crypt_key(struct bio *bio)
+{
+ if (!bio_has_crypt_ctx(bio))
+ return NULL;
+ return bio->bi_crypt_context->bc_key;
+}I don't believe bio_crypt_key() is necessary -- see my comment below regarding blk_mq_make_request().
+bool bio_crypt_dun_is_contiguous(const struct bio_crypt_ctx *bc, + unsigned int bytes, + u64 next_dun[BLK_CRYPTO_DUN_ARRAY_SIZE])
next_dun should be const.
+{
+ int i = 0;
+ unsigned int inc = bytes >> bc->bc_key->data_unit_size_bits;
+
+ while (i < BLK_CRYPTO_DUN_ARRAY_SIZE) {
+ if (bc->bc_dun[i] + inc != next_dun[i])
+ return false;
+ /*
+ * If addition of inc to the current entry caused an overflow,
+ * then we have to carry "1" for the next entry - so inc
+ * needs to be "1" for the next loop iteration). Otherwise,
+ * we need inc to be 0 for the next loop iteration. Since
+ * overflow can be determined by (bc->bc_dun[i] + inc) < inc
+ * we can do the following.
+ */
+ inc = ((bc->bc_dun[i] + inc) < inc);
+ i++;
+ }
This comment is verbose but doesn't really explain what's going on.
I think it would be much more useful to add comments like:
/*
* Returns true if @bc_dun plus @bytes converted to data units is equal to
* @next_dun, treating the DUNs as multi-limb integers.
*/
bool bio_crypt_dun_is_contiguous(const struct bio_crypt_ctx *bc,
unsigned int bytes,
const u64 next_dun[BLK_CRYPTO_DUN_ARRAY_SIZE])
And:
/*
* If the addition in this limb overflowed, then the carry bit
* into the next limb is 1. Else the carry bit is 0.
*/
inc = ((bc->bc_dun[i] + inc) < inc);
+ + /* + * After going through all the entries in the dun, inc must be 0 for + * the duns to be contiguous. + */ + return !inc; +}
This is new in v9; before it just returned true. It would be more helpful if this comment was like: /* If the DUN wrapped through 0, don't treat it as contiguous. */
+/**
+ * __blk_crypto_init_request - Initializes the request's crypto fields based on
+ * the blk_crypto_key for a bio to be added to the
+ * request, and prepares it for hardware inline
+ * encryption.
+ *
+ * @rq: The request to init
+ * @key: The blk_crypto_key of bios that will (eventually) be added to @rq.
+ *
+ * Initializes the request's crypto fields to appropriate default values and
+ * tries to get a keyslot for the blk_crypto_key.
+ *
+ * Return: BLK_STATUS_OK on success, and negative error code otherwise.
+ */
+blk_status_t __blk_crypto_init_request(struct request *rq,
+ const struct blk_crypto_key *key)
+{
+ return blk_ksm_get_slot_for_key(rq->q->ksm, key, &rq->crypt_keyslot);
+}The comment of this function seems outdated. All it does it get a keyslot, but the comment talks about initializing "crypto fields" (plural).
+/**
+ * blk_crypto_evict_key() - Evict a key from any inline encryption hardware
+ * it may have been programmed into
+ * @q: The request queue who's keyslot manager this key might have been
+ * programmed into
+ * @key: The key to evict
+ *
+ * Upper layers (filesystems) should call this function to ensure that a key
+ * is evicted from hardware that it might have been programmed into. This
+ * will call blk_ksm_evict_key on the queue's keyslot manager, if one
+ * exists, and supports the crypto algorithm with the specified data unit size.
+ *
+ * Return: 0 on success or if key is not present in the q's ksm, -err on error.
+ */
+int blk_crypto_evict_key(struct request_queue *q,
+ const struct blk_crypto_key *key)
+{
+ if (q->ksm && blk_ksm_crypto_key_supported(q->ksm, key))
+ return blk_ksm_evict_key(q->ksm, key);
+
+ return 0;
+}The keyslot manager is really an implementation detail of the block layer. Therefore when documenting functions like blk_crypto_evict_key() that are the API for use by upper layers like filesystems, we shouldn't really talk about the keyslot manager, but rather describe the behavior in higher-level terms like "evict a key from the inline encryption hardware".
quoted hunk ↗ jump to hunk
@@ -1971,6 +1974,7 @@ static blk_qc_t blk_mq_make_request(struct request_queue *q, struct bio *bio) struct request *same_queue_rq = NULL; unsigned int nr_segs; blk_qc_t cookie; + blk_status_t ret; blk_queue_bounce(q, &bio); __blk_queue_split(q, &bio, &nr_segs);@@ -2002,6 +2006,14 @@ static blk_qc_t blk_mq_make_request(struct request_queue *q, struct bio *bio) cookie = request_to_qc_t(data.hctx, rq); + ret = blk_crypto_init_request(rq, bio_crypt_key(bio)); + if (ret != BLK_STS_OK) { + bio->bi_status = ret; + bio_endio(bio); + blk_mq_free_request(rq); + return BLK_QC_T_NONE; + } + blk_mq_bio_to_request(rq, bio, nr_segs);
Wouldn't it make a lot more sense to do blk_crypto_init_request() after blk_mq_bio_to_request() rather than before? I.e., initialize request::crypt_ctx first, *then* get the keyslot. Not the other way around. That would allow removing the second argument to blk_crypto_init_request() and removing bio_crypt_key(). blk_crypto_init_request() would only need to take in the struct request.
quoted hunk ↗ jump to hunk
diff --git a/include/linux/blk-crypto.h b/include/linux/blk-crypto.h index b8d54eca1c0d8..4add2e8846fea 100644 --- a/include/linux/blk-crypto.h +++ b/include/linux/blk-crypto.h@@ -6,6 +6,8 @@ #ifndef __LINUX_BLK_CRYPTO_H #define __LINUX_BLK_CRYPTO_H +#include <linux/types.h> + enum blk_crypto_mode_num { BLK_ENCRYPTION_MODE_INVALID, BLK_ENCRYPTION_MODE_AES_256_XTS,@@ -24,7 +26,7 @@ enum blk_crypto_mode_num { * ciphertext. This is always a power of 2. It might be e.g. the * filesystem block size or the disk sector size. * @data_unit_size_bits: log2 of data_unit_size - * @dun_bytes: the number of bytes of DUN used when using this key + * @dun_bytes: the maximum number of bytes of DUN used when using this key
This comment update should be folded into the patch that added this.
+/**
+ * struct bio_crypt_ctx - an inline encryption context
+ * @bc_key: the key, algorithm, and data unit size to use
+ * @bc_dun: the data unit number (starting IV) to use
+ * @bc_keyslot: the keyslot that has been assigned for this key in @bc_ksm,
+ * or -1 if no keyslot has been assigned yet.
+ * @bc_ksm: the keyslot manager into which the key has been programmed with
+ * @bc_keyslot, or NULL if this key hasn't yet been programmed.
+ *
+ * A bio_crypt_ctx specifies that the contents of the bio will be encrypted (for
+ * write requests) or decrypted (for read requests) inline by the storage device
+ * or controller.
+ */
+struct bio_crypt_ctx {
+ const struct blk_crypto_key *bc_key;
+ u64 bc_dun[BLK_CRYPTO_DUN_ARRAY_SIZE];
+};Kerneldoc needs to be updated; bc_keyslot and bc_ksm no longer exist. - Eric