From: Harald Freudenberger <freude@linux.ibm.com>
To: agk@redhat.com, snitzer@kernel.org, mpatocka@redhat.com,
ifranzki@linux.ibm.com
Cc: linux-s390@vger.kernel.org, dm-devel@lists.linux.dev,
herbert@gondor.apana.org.au, dengler@linux.ibm.com
Subject: [PATCH v1 1/1] dm-integrity: Implement asynch digest support
Date: Wed, 15 Jan 2025 17:46:57 +0100 [thread overview]
Message-ID: <20250115164657.84650-2-freude@linux.ibm.com> (raw)
In-Reply-To: <20250115164657.84650-1-freude@linux.ibm.com>
Use the async digest in-kernel crypto API instead of the
synchronous digest API. This has the advantage of being able
to use synchronous as well as asynchronous digest implementations
as the in-kernel API has an automatic wrapping mechanism
to provide all synchronous digests via the asynch API.
Tested with crc32, sha256, hmac-sha256 and the s390 specific
implementations for hmac-sha256 and protected key phmac-sha256.
Signed-off-by: Harald Freudenberger <freude@linux.ibm.com>
---
drivers/md/dm-integrity.c | 220 ++++++++++++++++++++++++--------------
1 file changed, 138 insertions(+), 82 deletions(-)
diff --git a/drivers/md/dm-integrity.c b/drivers/md/dm-integrity.c
index ee9f7cecd78e..1504db9276d1 100644
--- a/drivers/md/dm-integrity.c
+++ b/drivers/md/dm-integrity.c
@@ -195,7 +195,7 @@ struct dm_integrity_c {
struct scatterlist **journal_io_scatterlist;
struct skcipher_request **sk_requests;
- struct crypto_shash *journal_mac;
+ struct crypto_ahash *journal_mac;
struct journal_node *journal_tree;
struct rb_root journal_tree_root;
@@ -221,7 +221,7 @@ struct dm_integrity_c {
int failed;
- struct crypto_shash *internal_hash;
+ struct crypto_ahash *internal_hash;
struct dm_target *ti;
@@ -488,11 +488,14 @@ static void sb_set_version(struct dm_integrity_c *ic)
static int sb_mac(struct dm_integrity_c *ic, bool wr)
{
- SHASH_DESC_ON_STACK(desc, ic->journal_mac);
- int r;
- unsigned int mac_size = crypto_shash_digestsize(ic->journal_mac);
+ unsigned int mac_size = crypto_ahash_digestsize(ic->journal_mac);
__u8 *sb = (__u8 *)ic->sb;
__u8 *mac = sb + (1 << SECTOR_SHIFT) - mac_size;
+ struct ahash_request *req;
+ DECLARE_CRYPTO_WAIT(wait);
+ struct scatterlist sg;
+ unsigned int nbytes;
+ int r;
if (sizeof(struct superblock) + mac_size > 1 << SECTOR_SHIFT ||
mac_size > HASH_MAX_DIGESTSIZE) {
@@ -500,29 +503,44 @@ static int sb_mac(struct dm_integrity_c *ic, bool wr)
return -EINVAL;
}
- desc->tfm = ic->journal_mac;
+ req = ahash_request_alloc(ic->journal_mac, GFP_KERNEL);
+ if (unlikely(!req)) {
+ dm_integrity_io_error(ic, "ahash_request_alloc", -ENOMEM);
+ return -ENOMEM;
+ }
+ ahash_request_set_callback(req, 0, crypto_req_done, &wait);
+
+ sg_init_table(&sg, 1);
+ nbytes = mac - sb;
+ sg_set_buf(&sg, sb, nbytes);
if (likely(wr)) {
- r = crypto_shash_digest(desc, sb, mac - sb, mac);
- if (unlikely(r < 0)) {
- dm_integrity_io_error(ic, "crypto_shash_digest", r);
+ ahash_request_set_crypt(req, &sg, mac, nbytes);
+ r = crypto_wait_req(crypto_ahash_digest(req), &wait);
+ if (unlikely(r)) {
+ dm_integrity_io_error(ic, "crypto_ahash_digest", r);
+ ahash_request_free(req);
return r;
}
} else {
__u8 actual_mac[HASH_MAX_DIGESTSIZE];
- r = crypto_shash_digest(desc, sb, mac - sb, actual_mac);
- if (unlikely(r < 0)) {
- dm_integrity_io_error(ic, "crypto_shash_digest", r);
+ ahash_request_set_crypt(req, &sg, actual_mac, nbytes);
+ r = crypto_wait_req(crypto_ahash_digest(req), &wait);
+ if (unlikely(r)) {
+ dm_integrity_io_error(ic, "crypto_ahash_digest", r);
+ ahash_request_free(req);
return r;
}
if (memcmp(mac, actual_mac, mac_size)) {
dm_integrity_io_error(ic, "superblock mac", -EILSEQ);
dm_audit_log_target(DM_MSG_PREFIX, "mac-superblock", ic->ti, 0);
+ ahash_request_free(req);
return -EILSEQ;
}
}
+ ahash_request_free(req);
return 0;
}
@@ -775,51 +793,62 @@ static struct journal_sector *access_journal_data(struct dm_integrity_c *ic, uns
static void section_mac(struct dm_integrity_c *ic, unsigned int section, __u8 result[JOURNAL_MAC_SIZE])
{
- SHASH_DESC_ON_STACK(desc, ic->journal_mac);
+ unsigned int j, size, nsg, nbytes = 0;
+ struct scatterlist *sg = NULL, *s;
+ struct ahash_request *req = NULL;
+ DECLARE_CRYPTO_WAIT(wait);
+ __le64 *section_le = NULL;
int r;
- unsigned int j, size;
- desc->tfm = ic->journal_mac;
+ req = ahash_request_alloc(ic->journal_mac, GFP_KERNEL);
+ if (unlikely(!req)) {
+ dm_integrity_io_error(ic, "ahash_request_alloc", -ENOMEM);
+ goto err;
+ }
+ ahash_request_set_callback(req, 0, crypto_req_done, &wait);
- r = crypto_shash_init(desc);
- if (unlikely(r < 0)) {
- dm_integrity_io_error(ic, "crypto_shash_init", r);
+ nsg = ic->journal_section_entries;
+ if (ic->sb->flags & cpu_to_le32(SB_FLAG_FIXED_HMAC))
+ nsg += 2;
+ sg = kmalloc_array(nsg, sizeof(*sg), GFP_KERNEL);
+ if (unlikely(!sg)) {
+ dm_integrity_io_error(ic, "kmalloc_array", -ENOMEM);
goto err;
}
+ sg_init_table(sg, nsg);
+ s = sg;
if (ic->sb->flags & cpu_to_le32(SB_FLAG_FIXED_HMAC)) {
- __le64 section_le;
-
- r = crypto_shash_update(desc, (__u8 *)&ic->sb->salt, SALT_SIZE);
- if (unlikely(r < 0)) {
- dm_integrity_io_error(ic, "crypto_shash_update", r);
- goto err;
- }
+ sg_set_buf(s, (__u8 *)&ic->sb->salt, SALT_SIZE);
+ nbytes += SALT_SIZE;
+ s++;
- section_le = cpu_to_le64(section);
- r = crypto_shash_update(desc, (__u8 *)§ion_le, sizeof(section_le));
- if (unlikely(r < 0)) {
- dm_integrity_io_error(ic, "crypto_shash_update", r);
+ section_le = kmalloc(sizeof(__le64), GFP_KERNEL);
+ if (unlikely(!section_le)) {
+ dm_integrity_io_error(ic, "kmalloc(sizeof(__le64))", -ENOMEM);
goto err;
}
+ *section_le = cpu_to_le64(section);
+ sg_set_buf(s, (__u8 *)section_le, sizeof(*section_le));
+ nbytes += sizeof(*section_le);
+ s++;
}
for (j = 0; j < ic->journal_section_entries; j++) {
struct journal_entry *je = access_journal_entry(ic, section, j);
- r = crypto_shash_update(desc, (__u8 *)&je->u.sector, sizeof(je->u.sector));
- if (unlikely(r < 0)) {
- dm_integrity_io_error(ic, "crypto_shash_update", r);
- goto err;
- }
+ sg_set_buf(s, (__u8 *)&je->u.sector, sizeof(je->u.sector));
+ nbytes += sizeof(je->u.sector);
+ s++;
}
- size = crypto_shash_digestsize(ic->journal_mac);
+ size = crypto_ahash_digestsize(ic->journal_mac);
if (likely(size <= JOURNAL_MAC_SIZE)) {
- r = crypto_shash_final(desc, result);
- if (unlikely(r < 0)) {
- dm_integrity_io_error(ic, "crypto_shash_final", r);
+ ahash_request_set_crypt(req, sg, result, nbytes);
+ r = crypto_wait_req(crypto_ahash_digest(req), &wait);
+ if (unlikely(r)) {
+ dm_integrity_io_error(ic, "crypto_ahash_digest", r);
goto err;
}
memset(result + size, 0, JOURNAL_MAC_SIZE - size);
@@ -830,16 +859,24 @@ static void section_mac(struct dm_integrity_c *ic, unsigned int section, __u8 re
dm_integrity_io_error(ic, "digest_size", -EINVAL);
goto err;
}
- r = crypto_shash_final(desc, digest);
- if (unlikely(r < 0)) {
- dm_integrity_io_error(ic, "crypto_shash_final", r);
+ ahash_request_set_crypt(req, sg, digest, nbytes);
+ r = crypto_wait_req(crypto_ahash_digest(req), &wait);
+ if (unlikely(r)) {
+ dm_integrity_io_error(ic, "crypto_ahash_digest", r);
goto err;
}
memcpy(result, digest, JOURNAL_MAC_SIZE);
}
+ ahash_request_free(req);
+ kfree(section_le);
+ kfree(sg);
return;
+
err:
+ ahash_request_free(req);
+ kfree(section_le);
+ kfree(sg);
memset(result, 0, JOURNAL_MAC_SIZE);
}
@@ -1637,53 +1674,65 @@ static void integrity_end_io(struct bio *bio)
static void integrity_sector_checksum(struct dm_integrity_c *ic, sector_t sector,
const char *data, char *result)
{
- __le64 sector_le = cpu_to_le64(sector);
- SHASH_DESC_ON_STACK(req, ic->internal_hash);
- int r;
+ struct ahash_request *req = NULL;
+ struct scatterlist sg[3], *s;
+ DECLARE_CRYPTO_WAIT(wait);
+ __le64 *sector_le = NULL;
unsigned int digest_size;
+ unsigned int nbytes = 0;
+ int r;
- req->tfm = ic->internal_hash;
-
- r = crypto_shash_init(req);
- if (unlikely(r < 0)) {
- dm_integrity_io_error(ic, "crypto_shash_init", r);
+ req = ahash_request_alloc(ic->internal_hash, GFP_KERNEL);
+ if (unlikely(!req)) {
+ dm_integrity_io_error(ic, "ahash_request_alloc", -ENOMEM);
goto failed;
}
+ ahash_request_set_callback(req, 0, crypto_req_done, &wait);
+ s = sg;
if (ic->sb->flags & cpu_to_le32(SB_FLAG_FIXED_HMAC)) {
- r = crypto_shash_update(req, (__u8 *)&ic->sb->salt, SALT_SIZE);
- if (unlikely(r < 0)) {
- dm_integrity_io_error(ic, "crypto_shash_update", r);
- goto failed;
- }
+ sg_init_table(sg, 3);
+ sg_set_buf(s, (const __u8 *)&ic->sb->salt, SALT_SIZE);
+ nbytes += SALT_SIZE;
+ s++;
+ } else {
+ sg_init_table(sg, 2);
}
- r = crypto_shash_update(req, (const __u8 *)§or_le, sizeof(sector_le));
- if (unlikely(r < 0)) {
- dm_integrity_io_error(ic, "crypto_shash_update", r);
+ sector_le = kmalloc(sizeof(__le64), GFP_KERNEL);
+ if (unlikely(!sector_le)) {
+ dm_integrity_io_error(ic, "kmalloc(sizeof(__le64))", -ENOMEM);
goto failed;
}
+ *sector_le = cpu_to_le64(sector);
+ sg_set_buf(s, (const __u8 *)sector_le, sizeof(*sector_le));
+ nbytes += sizeof(*sector_le);
+ s++;
- r = crypto_shash_update(req, data, ic->sectors_per_block << SECTOR_SHIFT);
- if (unlikely(r < 0)) {
- dm_integrity_io_error(ic, "crypto_shash_update", r);
- goto failed;
- }
+ sg_set_buf(s, data, ic->sectors_per_block << SECTOR_SHIFT);
+ nbytes += ic->sectors_per_block << SECTOR_SHIFT;
+
+ ahash_request_set_crypt(req, sg, result, nbytes);
- r = crypto_shash_final(req, result);
- if (unlikely(r < 0)) {
- dm_integrity_io_error(ic, "crypto_shash_final", r);
+ r = crypto_wait_req(crypto_ahash_digest(req), &wait);
+ if (r) {
+ dm_integrity_io_error(ic, "crypto_ahash_digest", r);
goto failed;
}
- digest_size = crypto_shash_digestsize(ic->internal_hash);
+ digest_size = crypto_ahash_digestsize(ic->internal_hash);
if (unlikely(digest_size < ic->tag_size))
memset(result + digest_size, 0, ic->tag_size - digest_size);
+ ahash_request_free(req);
+ kfree(sector_le);
+
return;
failed:
/* this shouldn't happen anyway, the hash functions have no reason to fail */
+ ahash_request_free(req);
+ kfree(sector_le);
get_random_bytes(result, ic->tag_size);
}
@@ -1776,7 +1825,7 @@ static void integrity_metadata(struct work_struct *w)
if (ic->internal_hash) {
struct bvec_iter iter;
struct bio_vec bv;
- unsigned int digest_size = crypto_shash_digestsize(ic->internal_hash);
+ unsigned int digest_size = crypto_ahash_digestsize(ic->internal_hash);
struct bio *bio = dm_bio_from_per_bio_data(dio, sizeof(struct dm_integrity_io));
char *checksums;
unsigned int extra_space = unlikely(digest_size > ic->tag_size) ? digest_size - ic->tag_size : 0;
@@ -2124,7 +2173,7 @@ static bool __journal_read_write(struct dm_integrity_io *dio, struct bio *bio,
} while (++s < ic->sectors_per_block);
if (ic->internal_hash) {
- unsigned int digest_size = crypto_shash_digestsize(ic->internal_hash);
+ unsigned int digest_size = crypto_ahash_digestsize(ic->internal_hash);
if (unlikely(digest_size > ic->tag_size)) {
char checksums_onstack[HASH_MAX_DIGESTSIZE];
@@ -2428,7 +2477,7 @@ static int dm_integrity_map_inline(struct dm_integrity_io *dio, bool from_map)
if (!dio->integrity_payload) {
unsigned digest_size, extra_size;
dio->payload_len = ic->tuple_size * (bio_sectors(bio) >> ic->sb->log2_sectors_per_block);
- digest_size = crypto_shash_digestsize(ic->internal_hash);
+ digest_size = crypto_ahash_digestsize(ic->internal_hash);
extra_size = unlikely(digest_size > ic->tag_size) ? digest_size - ic->tag_size : 0;
dio->payload_len += extra_size;
dio->integrity_payload = kmalloc(dio->payload_len, GFP_NOIO | __GFP_NORETRY | __GFP_NOMEMALLOC | __GFP_NOWARN);
@@ -2595,7 +2644,7 @@ static void dm_integrity_inline_recheck(struct work_struct *w)
bio_put(outgoing_bio);
integrity_sector_checksum(ic, dio->bio_details.bi_iter.bi_sector, outgoing_data, digest);
- if (unlikely(memcmp(digest, dio->integrity_payload, min(crypto_shash_digestsize(ic->internal_hash), ic->tag_size)))) {
+ if (unlikely(memcmp(digest, dio->integrity_payload, min(crypto_ahash_digestsize(ic->internal_hash), ic->tag_size)))) {
DMERR_LIMIT("%pg: Checksum failed at sector 0x%llx",
ic->dev->bdev, dio->bio_details.bi_iter.bi_sector);
atomic64_inc(&ic->number_of_mismatches);
@@ -2635,7 +2684,7 @@ static int dm_integrity_end_io(struct dm_target *ti, struct bio *bio, blk_status
//memset(mem, 0xff, ic->sectors_per_block << SECTOR_SHIFT);
integrity_sector_checksum(ic, dio->bio_details.bi_iter.bi_sector, mem, digest);
if (unlikely(memcmp(digest, dio->integrity_payload + pos,
- min(crypto_shash_digestsize(ic->internal_hash), ic->tag_size)))) {
+ min(crypto_ahash_digestsize(ic->internal_hash), ic->tag_size)))) {
kunmap_local(mem);
dm_integrity_free_payload(dio);
INIT_WORK(&dio->work, dm_integrity_inline_recheck);
@@ -3017,8 +3066,8 @@ static void integrity_recalc(struct work_struct *w)
goto free_ret;
}
recalc_tags_size = (recalc_sectors >> ic->sb->log2_sectors_per_block) * ic->tag_size;
- if (crypto_shash_digestsize(ic->internal_hash) > ic->tag_size)
- recalc_tags_size += crypto_shash_digestsize(ic->internal_hash) - ic->tag_size;
+ if (crypto_ahash_digestsize(ic->internal_hash) > ic->tag_size)
+ recalc_tags_size += crypto_ahash_digestsize(ic->internal_hash) - ic->tag_size;
recalc_tags = kvmalloc(recalc_tags_size, GFP_NOIO);
if (!recalc_tags) {
vfree(recalc_buffer);
@@ -3177,8 +3226,8 @@ static void integrity_recalc_inline(struct work_struct *w)
}
recalc_tags_size = (recalc_sectors >> ic->sb->log2_sectors_per_block) * ic->tuple_size;
- if (crypto_shash_digestsize(ic->internal_hash) > ic->tuple_size)
- recalc_tags_size += crypto_shash_digestsize(ic->internal_hash) - ic->tuple_size;
+ if (crypto_ahash_digestsize(ic->internal_hash) > ic->tuple_size)
+ recalc_tags_size += crypto_ahash_digestsize(ic->internal_hash) - ic->tuple_size;
recalc_tags = kmalloc(recalc_tags_size, GFP_NOIO | __GFP_NOWARN);
if (!recalc_tags) {
kfree(recalc_buffer);
@@ -4187,6 +4236,8 @@ static int get_alg_and_key(const char *arg, struct alg_spec *a, char **error, ch
if (!a->alg_string)
goto nomem;
+ DEBUG_print("%s: alg_string=%s\n", __func__, a->alg_string);
+
k = strchr(a->alg_string, ':');
if (k) {
*k = 0;
@@ -4198,6 +4249,9 @@ static int get_alg_and_key(const char *arg, struct alg_spec *a, char **error, ch
a->key = kmalloc(a->key_size, GFP_KERNEL);
if (!a->key)
goto nomem;
+
+ DEBUG_print("%s: key=%s\n", __func__, a->key_string);
+
if (hex2bin(a->key, a->key_string, a->key_size))
goto inval;
}
@@ -4211,13 +4265,15 @@ static int get_alg_and_key(const char *arg, struct alg_spec *a, char **error, ch
return -ENOMEM;
}
-static int get_mac(struct crypto_shash **hash, struct alg_spec *a, char **error,
+static int get_mac(struct crypto_ahash **hash, struct alg_spec *a, char **error,
char *error_alg, char *error_key)
{
int r;
+ DEBUG_print(">%s\n", __func__);
+
if (a->alg_string) {
- *hash = crypto_alloc_shash(a->alg_string, 0, CRYPTO_ALG_ALLOCATES_MEMORY);
+ *hash = crypto_alloc_ahash(a->alg_string, 0, CRYPTO_ALG_ALLOCATES_MEMORY);
if (IS_ERR(*hash)) {
*error = error_alg;
r = PTR_ERR(*hash);
@@ -4226,12 +4282,12 @@ static int get_mac(struct crypto_shash **hash, struct alg_spec *a, char **error,
}
if (a->key) {
- r = crypto_shash_setkey(*hash, a->key, a->key_size);
+ r = crypto_ahash_setkey(*hash, a->key, a->key_size);
if (r) {
*error = error_key;
return r;
}
- } else if (crypto_shash_get_flags(*hash) & CRYPTO_TFM_NEED_KEY) {
+ } else if (crypto_ahash_get_flags(*hash) & CRYPTO_TFM_NEED_KEY) {
*error = error_key;
return -ENOKEY;
}
@@ -4707,7 +4763,7 @@ static int dm_integrity_ctr(struct dm_target *ti, unsigned int argc, char **argv
r = -EINVAL;
goto bad;
}
- ic->tag_size = crypto_shash_digestsize(ic->internal_hash);
+ ic->tag_size = crypto_ahash_digestsize(ic->internal_hash);
}
if (ic->tag_size > MAX_TAG_SIZE) {
ti->error = "Too big tag size";
@@ -5226,7 +5282,7 @@ static void dm_integrity_dtr(struct dm_target *ti)
free_pages_exact(ic->sb, SB_SECTORS << SECTOR_SHIFT);
if (ic->internal_hash)
- crypto_free_shash(ic->internal_hash);
+ crypto_free_ahash(ic->internal_hash);
free_alg(&ic->internal_hash_alg);
if (ic->journal_crypt)
@@ -5234,7 +5290,7 @@ static void dm_integrity_dtr(struct dm_target *ti)
free_alg(&ic->journal_crypt_alg);
if (ic->journal_mac)
- crypto_free_shash(ic->journal_mac);
+ crypto_free_ahash(ic->journal_mac);
free_alg(&ic->journal_mac_alg);
kfree(ic);
--
2.43.0
next prev parent reply other threads:[~2025-01-15 16:47 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-01-15 16:46 [PATCH v1 0/1] dm-integrity: Implement asynch digest support Harald Freudenberger
2025-01-15 16:46 ` Harald Freudenberger [this message]
2025-01-15 17:29 ` [PATCH v1 1/1] " Mikulas Patocka
2025-01-17 13:31 ` Harald Freudenberger
2025-01-22 17:00 ` Harald Freudenberger
2025-01-27 17:57 ` Mikulas Patocka
2025-01-15 17:37 ` Eric Biggers
2025-01-16 7:33 ` Harald Freudenberger
2025-01-16 8:03 ` Eric Biggers
2025-01-16 9:00 ` Harald Freudenberger
2025-01-16 9:12 ` Herbert Xu
2025-01-16 17:54 ` Eric Biggers
2025-01-17 6:21 ` Herbert Xu
2025-01-17 7:57 ` Eric Biggers
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=20250115164657.84650-2-freude@linux.ibm.com \
--to=freude@linux.ibm.com \
--cc=agk@redhat.com \
--cc=dengler@linux.ibm.com \
--cc=dm-devel@lists.linux.dev \
--cc=herbert@gondor.apana.org.au \
--cc=ifranzki@linux.ibm.com \
--cc=linux-s390@vger.kernel.org \
--cc=mpatocka@redhat.com \
--cc=snitzer@kernel.org \
/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