#define CRYPTO_ALG_TYPE_AHASH_MASK     0x0000000e
 
-static int shash_async_setkey(struct crypto_ahash *tfm, const u8 *key,
-                             unsigned int keylen)
+static inline struct crypto_istat_hash *ahash_get_stat(struct ahash_alg *alg)
 {
-       struct crypto_shash **ctx = crypto_ahash_ctx(tfm);
+       return hash_get_stat(&alg->halg);
+}
+
+static inline int crypto_ahash_errstat(struct ahash_alg *alg, int err)
+{
+       if (!IS_ENABLED(CONFIG_CRYPTO_STATS))
+               return err;
 
-       return crypto_shash_setkey(*ctx, key, keylen);
+       if (err && err != -EINPROGRESS && err != -EBUSY)
+               atomic64_inc(&ahash_get_stat(alg)->err_cnt);
+
+       return err;
 }
 
-static int shash_async_init(struct ahash_request *req)
+/*
+ * For an ahash tfm that is using an shash algorithm (instead of an ahash
+ * algorithm), this returns the underlying shash tfm.
+ */
+static inline struct crypto_shash *ahash_to_shash(struct crypto_ahash *tfm)
 {
-       struct crypto_shash **ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req));
-       struct shash_desc *desc = ahash_request_ctx(req);
+       return *(struct crypto_shash **)crypto_ahash_ctx(tfm);
+}
 
-       desc->tfm = *ctx;
+static inline struct shash_desc *prepare_shash_desc(struct ahash_request *req,
+                                                   struct crypto_ahash *tfm)
+{
+       struct shash_desc *desc = ahash_request_ctx(req);
 
-       return crypto_shash_init(desc);
+       desc->tfm = ahash_to_shash(tfm);
+       return desc;
 }
 
 int shash_ahash_update(struct ahash_request *req, struct shash_desc *desc)
 }
 EXPORT_SYMBOL_GPL(shash_ahash_update);
 
-static int shash_async_update(struct ahash_request *req)
-{
-       return shash_ahash_update(req, ahash_request_ctx(req));
-}
-
-static int shash_async_final(struct ahash_request *req)
-{
-       return crypto_shash_final(ahash_request_ctx(req), req->result);
-}
-
 int shash_ahash_finup(struct ahash_request *req, struct shash_desc *desc)
 {
        struct crypto_hash_walk walk;
 }
 EXPORT_SYMBOL_GPL(shash_ahash_finup);
 
-static int shash_async_finup(struct ahash_request *req)
-{
-       struct crypto_shash **ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req));
-       struct shash_desc *desc = ahash_request_ctx(req);
-
-       desc->tfm = *ctx;
-
-       return shash_ahash_finup(req, desc);
-}
-
 int shash_ahash_digest(struct ahash_request *req, struct shash_desc *desc)
 {
        unsigned int nbytes = req->nbytes;
 }
 EXPORT_SYMBOL_GPL(shash_ahash_digest);
 
-static int shash_async_digest(struct ahash_request *req)
-{
-       struct crypto_shash **ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req));
-       struct shash_desc *desc = ahash_request_ctx(req);
-
-       desc->tfm = *ctx;
-
-       return shash_ahash_digest(req, desc);
-}
-
-static int shash_async_export(struct ahash_request *req, void *out)
-{
-       return crypto_shash_export(ahash_request_ctx(req), out);
-}
-
-static int shash_async_import(struct ahash_request *req, const void *in)
-{
-       struct crypto_shash **ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req));
-       struct shash_desc *desc = ahash_request_ctx(req);
-
-       desc->tfm = *ctx;
-
-       return crypto_shash_import(desc, in);
-}
-
-static void crypto_exit_shash_ops_async(struct crypto_tfm *tfm)
+static void crypto_exit_ahash_using_shash(struct crypto_tfm *tfm)
 {
        struct crypto_shash **ctx = crypto_tfm_ctx(tfm);
 
        crypto_free_shash(*ctx);
 }
 
-static int crypto_init_shash_ops_async(struct crypto_tfm *tfm)
+static int crypto_init_ahash_using_shash(struct crypto_tfm *tfm)
 {
        struct crypto_alg *calg = tfm->__crt_alg;
-       struct shash_alg *alg = __crypto_shash_alg(calg);
        struct crypto_ahash *crt = __crypto_ahash_cast(tfm);
        struct crypto_shash **ctx = crypto_tfm_ctx(tfm);
        struct crypto_shash *shash;
                return PTR_ERR(shash);
        }
 
+       crt->using_shash = true;
        *ctx = shash;
-       tfm->exit = crypto_exit_shash_ops_async;
-
-       crt->init = shash_async_init;
-       crt->update = shash_async_update;
-       crt->final = shash_async_final;
-       crt->finup = shash_async_finup;
-       crt->digest = shash_async_digest;
-       if (crypto_shash_alg_has_setkey(alg))
-               crt->setkey = shash_async_setkey;
+       tfm->exit = crypto_exit_ahash_using_shash;
 
        crypto_ahash_set_flags(crt, crypto_shash_get_flags(shash) &
                                    CRYPTO_TFM_NEED_KEY);
-
-       crt->export = shash_async_export;
-       crt->import = shash_async_import;
-
        crt->reqsize = sizeof(struct shash_desc) + crypto_shash_descsize(shash);
 
        return 0;
 }
 
-static struct crypto_ahash *
-crypto_clone_shash_ops_async(struct crypto_ahash *nhash,
-                            struct crypto_ahash *hash)
-{
-       struct crypto_shash **nctx = crypto_ahash_ctx(nhash);
-       struct crypto_shash **ctx = crypto_ahash_ctx(hash);
-       struct crypto_shash *shash;
-
-       shash = crypto_clone_shash(*ctx);
-       if (IS_ERR(shash)) {
-               crypto_free_ahash(nhash);
-               return ERR_CAST(shash);
-       }
-
-       *nctx = shash;
-
-       return nhash;
-}
-
 static int hash_walk_next(struct crypto_hash_walk *walk)
 {
        unsigned int offset = walk->offset;
        return -ENOSYS;
 }
 
-static void ahash_set_needkey(struct crypto_ahash *tfm)
+static void ahash_set_needkey(struct crypto_ahash *tfm, struct ahash_alg *alg)
 {
-       const struct hash_alg_common *alg = crypto_hash_alg_common(tfm);
-
-       if (tfm->setkey != ahash_nosetkey &&
-           !(alg->base.cra_flags & CRYPTO_ALG_OPTIONAL_KEY))
+       if (alg->setkey != ahash_nosetkey &&
+           !(alg->halg.base.cra_flags & CRYPTO_ALG_OPTIONAL_KEY))
                crypto_ahash_set_flags(tfm, CRYPTO_TFM_NEED_KEY);
 }
 
 int crypto_ahash_setkey(struct crypto_ahash *tfm, const u8 *key,
                        unsigned int keylen)
 {
-       int err = tfm->setkey(tfm, key, keylen);
+       if (likely(tfm->using_shash)) {
+               struct crypto_shash *shash = ahash_to_shash(tfm);
+               int err;
 
-       if (unlikely(err)) {
-               ahash_set_needkey(tfm);
-               return err;
+               err = crypto_shash_setkey(shash, key, keylen);
+               if (unlikely(err)) {
+                       crypto_ahash_set_flags(tfm,
+                                              crypto_shash_get_flags(shash) &
+                                              CRYPTO_TFM_NEED_KEY);
+                       return err;
+               }
+       } else {
+               struct ahash_alg *alg = crypto_ahash_alg(tfm);
+               int err;
+
+               err = alg->setkey(tfm, key, keylen);
+               if (unlikely(err)) {
+                       ahash_set_needkey(tfm, alg);
+                       return err;
+               }
        }
-
        crypto_ahash_clear_flags(tfm, CRYPTO_TFM_NEED_KEY);
        return 0;
 }
 EXPORT_SYMBOL_GPL(crypto_ahash_setkey);
 
+int crypto_ahash_init(struct ahash_request *req)
+{
+       struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+
+       if (likely(tfm->using_shash))
+               return crypto_shash_init(prepare_shash_desc(req, tfm));
+       if (crypto_ahash_get_flags(tfm) & CRYPTO_TFM_NEED_KEY)
+               return -ENOKEY;
+       return crypto_ahash_alg(tfm)->init(req);
+}
+EXPORT_SYMBOL_GPL(crypto_ahash_init);
+
 static int ahash_save_req(struct ahash_request *req, crypto_completion_t cplt,
                          bool has_state)
 {
        kfree_sensitive(subreq);
 }
 
-int crypto_ahash_final(struct ahash_request *req)
+int crypto_ahash_update(struct ahash_request *req)
 {
        struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
-       struct hash_alg_common *alg = crypto_hash_alg_common(tfm);
+       struct ahash_alg *alg;
 
+       if (likely(tfm->using_shash))
+               return shash_ahash_update(req, ahash_request_ctx(req));
+
+       alg = crypto_ahash_alg(tfm);
        if (IS_ENABLED(CONFIG_CRYPTO_STATS))
-               atomic64_inc(&hash_get_stat(alg)->hash_cnt);
+               atomic64_add(req->nbytes, &ahash_get_stat(alg)->hash_tlen);
+       return crypto_ahash_errstat(alg, alg->update(req));
+}
+EXPORT_SYMBOL_GPL(crypto_ahash_update);
+
+int crypto_ahash_final(struct ahash_request *req)
+{
+       struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+       struct ahash_alg *alg;
+
+       if (likely(tfm->using_shash))
+               return crypto_shash_final(ahash_request_ctx(req), req->result);
 
-       return crypto_hash_errstat(alg, tfm->final(req));
+       alg = crypto_ahash_alg(tfm);
+       if (IS_ENABLED(CONFIG_CRYPTO_STATS))
+               atomic64_inc(&ahash_get_stat(alg)->hash_cnt);
+       return crypto_ahash_errstat(alg, alg->final(req));
 }
 EXPORT_SYMBOL_GPL(crypto_ahash_final);
 
 int crypto_ahash_finup(struct ahash_request *req)
 {
        struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
-       struct hash_alg_common *alg = crypto_hash_alg_common(tfm);
+       struct ahash_alg *alg;
+
+       if (likely(tfm->using_shash))
+               return shash_ahash_finup(req, ahash_request_ctx(req));
 
+       alg = crypto_ahash_alg(tfm);
        if (IS_ENABLED(CONFIG_CRYPTO_STATS)) {
-               struct crypto_istat_hash *istat = hash_get_stat(alg);
+               struct crypto_istat_hash *istat = ahash_get_stat(alg);
 
                atomic64_inc(&istat->hash_cnt);
                atomic64_add(req->nbytes, &istat->hash_tlen);
        }
-
-       return crypto_hash_errstat(alg, tfm->finup(req));
+       return crypto_ahash_errstat(alg, alg->finup(req));
 }
 EXPORT_SYMBOL_GPL(crypto_ahash_finup);
 
 int crypto_ahash_digest(struct ahash_request *req)
 {
        struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
-       struct hash_alg_common *alg = crypto_hash_alg_common(tfm);
+       struct ahash_alg *alg;
        int err;
 
+       if (likely(tfm->using_shash))
+               return shash_ahash_digest(req, prepare_shash_desc(req, tfm));
+
+       alg = crypto_ahash_alg(tfm);
        if (IS_ENABLED(CONFIG_CRYPTO_STATS)) {
-               struct crypto_istat_hash *istat = hash_get_stat(alg);
+               struct crypto_istat_hash *istat = ahash_get_stat(alg);
 
                atomic64_inc(&istat->hash_cnt);
                atomic64_add(req->nbytes, &istat->hash_tlen);
        if (crypto_ahash_get_flags(tfm) & CRYPTO_TFM_NEED_KEY)
                err = -ENOKEY;
        else
-               err = tfm->digest(req);
+               err = alg->digest(req);
 
-       return crypto_hash_errstat(alg, err);
+       return crypto_ahash_errstat(alg, err);
 }
 EXPORT_SYMBOL_GPL(crypto_ahash_digest);
 
 
        subreq->base.complete = ahash_def_finup_done2;
 
-       err = crypto_ahash_reqtfm(req)->final(subreq);
+       err = crypto_ahash_alg(crypto_ahash_reqtfm(req))->final(subreq);
        if (err == -EINPROGRESS || err == -EBUSY)
                return err;
 
        if (err)
                return err;
 
-       err = tfm->update(req->priv);
+       err = crypto_ahash_alg(tfm)->update(req->priv);
        if (err == -EINPROGRESS || err == -EBUSY)
                return err;
 
        return ahash_def_finup_finish1(req, err);
 }
 
+int crypto_ahash_export(struct ahash_request *req, void *out)
+{
+       struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+
+       if (likely(tfm->using_shash))
+               return crypto_shash_export(ahash_request_ctx(req), out);
+       return crypto_ahash_alg(tfm)->export(req, out);
+}
+EXPORT_SYMBOL_GPL(crypto_ahash_export);
+
+int crypto_ahash_import(struct ahash_request *req, const void *in)
+{
+       struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+
+       if (likely(tfm->using_shash))
+               return crypto_shash_import(prepare_shash_desc(req, tfm), in);
+       if (crypto_ahash_get_flags(tfm) & CRYPTO_TFM_NEED_KEY)
+               return -ENOKEY;
+       return crypto_ahash_alg(tfm)->import(req, in);
+}
+EXPORT_SYMBOL_GPL(crypto_ahash_import);
+
 static void crypto_ahash_exit_tfm(struct crypto_tfm *tfm)
 {
        struct crypto_ahash *hash = __crypto_ahash_cast(tfm);
        struct crypto_ahash *hash = __crypto_ahash_cast(tfm);
        struct ahash_alg *alg = crypto_ahash_alg(hash);
 
-       hash->setkey = ahash_nosetkey;
-
        crypto_ahash_set_statesize(hash, alg->halg.statesize);
 
        if (tfm->__crt_alg->cra_type == &crypto_shash_type)
-               return crypto_init_shash_ops_async(tfm);
-
-       hash->init = alg->init;
-       hash->update = alg->update;
-       hash->final = alg->final;
-       hash->finup = alg->finup ?: ahash_def_finup;
-       hash->digest = alg->digest;
-       hash->export = alg->export;
-       hash->import = alg->import;
-
-       if (alg->setkey) {
-               hash->setkey = alg->setkey;
-               ahash_set_needkey(hash);
-       }
+               return crypto_init_ahash_using_shash(tfm);
+
+       ahash_set_needkey(hash, alg);
 
        if (alg->exit_tfm)
                tfm->exit = crypto_ahash_exit_tfm;
        if (IS_ERR(nhash))
                return nhash;
 
-       nhash->init = hash->init;
-       nhash->update = hash->update;
-       nhash->final = hash->final;
-       nhash->finup = hash->finup;
-       nhash->digest = hash->digest;
-       nhash->export = hash->export;
-       nhash->import = hash->import;
-       nhash->setkey = hash->setkey;
        nhash->reqsize = hash->reqsize;
        nhash->statesize = hash->statesize;
 
-       if (tfm->__crt_alg->cra_type != &crypto_ahash_type)
-               return crypto_clone_shash_ops_async(nhash, hash);
+       if (likely(hash->using_shash)) {
+               struct crypto_shash **nctx = crypto_ahash_ctx(nhash);
+               struct crypto_shash *shash;
+
+               shash = crypto_clone_shash(ahash_to_shash(hash));
+               if (IS_ERR(shash)) {
+                       err = PTR_ERR(shash);
+                       goto out_free_nhash;
+               }
+               *nctx = shash;
+               return nhash;
+       }
 
        err = -ENOSYS;
        alg = crypto_ahash_alg(hash);
        base->cra_type = &crypto_ahash_type;
        base->cra_flags |= CRYPTO_ALG_TYPE_AHASH;
 
+       if (!alg->finup)
+               alg->finup = ahash_def_finup;
+       if (!alg->setkey)
+               alg->setkey = ahash_nosetkey;
+
        return 0;
 }
 
        if (alg->cra_type == &crypto_shash_type)
                return crypto_shash_alg_has_setkey(__crypto_shash_alg(alg));
 
-       return __crypto_ahash_alg(alg)->setkey != NULL;
+       return __crypto_ahash_alg(alg)->setkey != ahash_nosetkey;
 }
 EXPORT_SYMBOL_GPL(crypto_hash_alg_has_setkey);
 
 
 #undef HASH_ALG_COMMON_STAT
 
 struct crypto_ahash {
-       int (*init)(struct ahash_request *req);
-       int (*update)(struct ahash_request *req);
-       int (*final)(struct ahash_request *req);
-       int (*finup)(struct ahash_request *req);
-       int (*digest)(struct ahash_request *req);
-       int (*export)(struct ahash_request *req, void *out);
-       int (*import)(struct ahash_request *req, const void *in);
-       int (*setkey)(struct crypto_ahash *tfm, const u8 *key,
-                     unsigned int keylen);
-
+       bool using_shash; /* Underlying algorithm is shash, not ahash */
        unsigned int statesize;
        unsigned int reqsize;
        struct crypto_tfm base;
  *
  * Return: 0 if the export was successful; < 0 if an error occurred
  */
-static inline int crypto_ahash_export(struct ahash_request *req, void *out)
-{
-       return crypto_ahash_reqtfm(req)->export(req, out);
-}
+int crypto_ahash_export(struct ahash_request *req, void *out);
 
 /**
  * crypto_ahash_import() - import message digest state
  *
  * Return: 0 if the import was successful; < 0 if an error occurred
  */
-static inline int crypto_ahash_import(struct ahash_request *req, const void *in)
-{
-       struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
-
-       if (crypto_ahash_get_flags(tfm) & CRYPTO_TFM_NEED_KEY)
-               return -ENOKEY;
-
-       return tfm->import(req, in);
-}
+int crypto_ahash_import(struct ahash_request *req, const void *in);
 
 /**
  * crypto_ahash_init() - (re)initialize message digest handle
  *
  * Return: see crypto_ahash_final()
  */
-static inline int crypto_ahash_init(struct ahash_request *req)
-{
-       struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
-
-       if (crypto_ahash_get_flags(tfm) & CRYPTO_TFM_NEED_KEY)
-               return -ENOKEY;
-
-       return tfm->init(req);
-}
-
-static inline struct crypto_istat_hash *hash_get_stat(
-       struct hash_alg_common *alg)
-{
-#ifdef CONFIG_CRYPTO_STATS
-       return &alg->stat;
-#else
-       return NULL;
-#endif
-}
-
-static inline int crypto_hash_errstat(struct hash_alg_common *alg, int err)
-{
-       if (!IS_ENABLED(CONFIG_CRYPTO_STATS))
-               return err;
-
-       if (err && err != -EINPROGRESS && err != -EBUSY)
-               atomic64_inc(&hash_get_stat(alg)->err_cnt);
-
-       return err;
-}
+int crypto_ahash_init(struct ahash_request *req);
 
 /**
  * crypto_ahash_update() - add data to message digest for processing
  *
  * Return: see crypto_ahash_final()
  */
-static inline int crypto_ahash_update(struct ahash_request *req)
-{
-       struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
-       struct hash_alg_common *alg = crypto_hash_alg_common(tfm);
-
-       if (IS_ENABLED(CONFIG_CRYPTO_STATS))
-               atomic64_add(req->nbytes, &hash_get_stat(alg)->hash_tlen);
-
-       return crypto_hash_errstat(alg, tfm->update(req));
-}
+int crypto_ahash_update(struct ahash_request *req);
 
 /**
  * DOC: Asynchronous Hash Request Handle