Add an internal method to the shash interface that permits templates to invoke it with a scatterlist. Drivers implementing the shash interface can opt into using this method, making it more straightforward for templates to pass down data provided via scatterlists without forcing the underlying shash to process each scatterlist entry with a discrete update() call. This will be used later in the SIMD accelerated Poly1305 to amortize SIMD begin()/end() calls over the entire input. Signed-off-by: Ard Biesheuvel <ard.biesheuvel@xxxxxxxxxx> --- crypto/ahash.c | 18 +++++++++++++++ crypto/shash.c | 24 ++++++++++++++++++++ include/crypto/hash.h | 3 +++ include/crypto/internal/hash.h | 19 ++++++++++++++++ 4 files changed, 64 insertions(+) diff --git a/crypto/ahash.c b/crypto/ahash.c index 3815b363a693..aecb48f0f50c 100644 --- a/crypto/ahash.c +++ b/crypto/ahash.c @@ -144,6 +144,24 @@ int crypto_hash_walk_first(struct ahash_request *req, } EXPORT_SYMBOL_GPL(crypto_hash_walk_first); +int crypto_shash_walk_sg(struct shash_desc *desc, struct scatterlist *sg, + int nbytes, struct crypto_hash_walk *walk, int flags) +{ + walk->total = nbytes; + + if (!walk->total) { + walk->entrylen = 0; + return 0; + } + + walk->alignmask = crypto_shash_alignmask(desc->tfm); + walk->sg = sg; + walk->flags = flags; + + return hash_walk_new_entry(walk); +} +EXPORT_SYMBOL_GPL(crypto_shash_walk_sg); + int crypto_ahash_walk_first(struct ahash_request *req, struct crypto_hash_walk *walk) { diff --git a/crypto/shash.c b/crypto/shash.c index e83c5124f6eb..b16ab5590dc4 100644 --- a/crypto/shash.c +++ b/crypto/shash.c @@ -121,6 +121,30 @@ int crypto_shash_update(struct shash_desc *desc, const u8 *data, } EXPORT_SYMBOL_GPL(crypto_shash_update); +int crypto_shash_update_from_sg(struct shash_desc *desc, struct scatterlist *sg, + unsigned int len, bool atomic) +{ + struct crypto_shash *tfm = desc->tfm; + struct shash_alg *shash = crypto_shash_alg(tfm); + struct crypto_hash_walk walk; + int flags = 0; + int nbytes; + + if (!atomic) + flags = CRYPTO_TFM_REQ_MAY_SLEEP; + + if (shash->update_from_sg) + return shash->update_from_sg(desc, sg, len, flags); + + for (nbytes = crypto_shash_walk_sg(desc, sg, len, &walk, flags); + nbytes > 0; + nbytes = crypto_hash_walk_done(&walk, nbytes)) + nbytes = crypto_shash_update(desc, walk.data, nbytes); + + return nbytes; +} +EXPORT_SYMBOL_GPL(crypto_shash_update_from_sg); + static int shash_final_unaligned(struct shash_desc *desc, u8 *out) { struct crypto_shash *tfm = desc->tfm; diff --git a/include/crypto/hash.h b/include/crypto/hash.h index ef10c370605a..0b83d85a3828 100644 --- a/include/crypto/hash.h +++ b/include/crypto/hash.h @@ -158,6 +158,7 @@ struct shash_desc { * struct shash_alg - synchronous message digest definition * @init: see struct ahash_alg * @update: see struct ahash_alg + * @update_from_sg: variant of update() taking a scatterlist as input [optional] * @final: see struct ahash_alg * @finup: see struct ahash_alg * @digest: see struct ahash_alg @@ -175,6 +176,8 @@ struct shash_alg { int (*init)(struct shash_desc *desc); int (*update)(struct shash_desc *desc, const u8 *data, unsigned int len); + int (*update_from_sg)(struct shash_desc *desc, struct scatterlist *sg, + unsigned int len, int flags); int (*final)(struct shash_desc *desc, u8 *out); int (*finup)(struct shash_desc *desc, const u8 *data, unsigned int len, u8 *out); diff --git a/include/crypto/internal/hash.h b/include/crypto/internal/hash.h index bfc9db7b100d..6f4bfa057bea 100644 --- a/include/crypto/internal/hash.h +++ b/include/crypto/internal/hash.h @@ -50,6 +50,8 @@ extern const struct crypto_type crypto_ahash_type; int crypto_hash_walk_done(struct crypto_hash_walk *walk, int err); int crypto_hash_walk_first(struct ahash_request *req, struct crypto_hash_walk *walk); +int crypto_shash_walk_sg(struct shash_desc *desc, struct scatterlist *sg, + int nbytes, struct crypto_hash_walk *walk, int flags); int crypto_ahash_walk_first(struct ahash_request *req, struct crypto_hash_walk *walk); @@ -242,5 +244,22 @@ static inline struct crypto_shash *__crypto_shash_cast(struct crypto_tfm *tfm) return container_of(tfm, struct crypto_shash, base); } +/** + * crypto_shash_update_from_sg() - add data from a scatterlist to message digest + * for processing + * @desc: operational state handle that is already initialized + * @data: scatterlist with input data to be added to the message digest + * @len: length of the input data + * @atomic: whether or not the call is permitted to sleep + * + * Updates the message digest state of the operational state handle. + * + * Context: Any context. + * Return: 0 if the message digest update was successful; < 0 if an error + * occurred + */ +int crypto_shash_update_from_sg(struct shash_desc *desc, struct scatterlist *sg, + unsigned int len, bool atomic); + #endif /* _CRYPTO_INTERNAL_HASH_H */ -- 2.20.1