crypto - aesni: Add a synchronous version of gcm-aesni.

When we do IPsec NIC offloading, we need a fallback for
packets that were targeted to be IPsec offloaded but
rerouted to a device that does not support IPsec offload.
This patch adds a synchronous version of gcm-aesni so
that we don't need to fallback to a slow software
implementation of aes.

Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
diff --git a/arch/x86/crypto/aesni-intel_glue.c b/arch/x86/crypto/aesni-intel_glue.c
index 93de8ea..ee27e22 100644
--- a/arch/x86/crypto/aesni-intel_glue.c
+++ b/arch/x86/crypto/aesni-intel_glue.c
@@ -61,6 +61,11 @@
 	u8 nonce[4];
 };
 
+struct aesni_rfc4106_gcm_sync_ctx {
+	struct crypto_aead *child;
+	struct crypto_aead *fallback;
+};
+
 struct aesni_xts_ctx {
 	u8 raw_tweak_ctx[sizeof(struct crypto_aes_ctx)] AESNI_ALIGN_ATTR;
 	u8 raw_crypt_ctx[sizeof(struct crypto_aes_ctx)] AESNI_ALIGN_ATTR;
@@ -610,6 +615,47 @@
 				   aes_ctx(ctx->raw_crypt_ctx));
 }
 
+static int rfc4106_sync_init(struct crypto_aead *aead)
+{
+	unsigned int reqsize;
+	struct crypto_aead *child;
+	struct crypto_aead *fallback;
+	struct aesni_rfc4106_gcm_sync_ctx *ctx = crypto_aead_ctx(aead);
+
+	fallback = crypto_alloc_aead("rfc4106(gcm(aes-asm))",
+				     0,
+				     CRYPTO_ALG_ASYNC);
+	if (IS_ERR(fallback))
+		return PTR_ERR(fallback);
+
+	child = crypto_alloc_aead("__driver-gcm-aes-aesni",
+				  CRYPTO_ALG_INTERNAL,
+				  CRYPTO_ALG_INTERNAL | CRYPTO_ALG_ASYNC);
+	if (IS_ERR(child)) {
+		crypto_free_aead(fallback);
+		return PTR_ERR(child);
+	}
+
+	reqsize = max_t(unsigned int, crypto_aead_reqsize(child),
+			crypto_aead_reqsize(fallback));
+
+	ctx->child = child;
+	ctx->fallback = fallback;
+
+	crypto_aead_set_reqsize(aead, reqsize);
+	return 0;
+}
+
+static void rfc4106_sync_exit(struct crypto_aead *aead)
+{
+	struct aesni_rfc4106_gcm_sync_ctx *ctx = crypto_aead_ctx(aead);
+	struct crypto_aead *child = ctx->child;
+	struct crypto_aead *fallback = ctx->fallback;
+
+	crypto_free_aead(child);
+	crypto_free_aead(fallback);
+}
+
 static int rfc4106_init(struct crypto_aead *aead)
 {
 	struct cryptd_aead *cryptd_tfm;
@@ -677,6 +723,21 @@
 	       rfc4106_set_hash_subkey(ctx->hash_subkey, key, key_len);
 }
 
+static int rfc4106_sync_set_key(struct crypto_aead *parent, const u8 *key,
+				unsigned int key_len)
+{
+	int err;
+	struct aesni_rfc4106_gcm_sync_ctx *ctx = crypto_aead_ctx(parent);
+	struct crypto_aead *child = ctx->child;
+	struct crypto_aead *fallback = ctx->fallback;
+
+	err = crypto_aead_setkey(child, key, key_len);
+	if (err)
+		return err;
+
+	return crypto_aead_setkey(fallback, key, key_len);
+}
+
 static int rfc4106_set_key(struct crypto_aead *parent, const u8 *key,
 			   unsigned int key_len)
 {
@@ -712,6 +773,21 @@
 	return crypto_aead_setauthsize(&cryptd_tfm->base, authsize);
 }
 
+static int rfc4106_sync_set_authsize(struct crypto_aead *parent,
+				     unsigned int authsize)
+{
+	int err;
+	struct aesni_rfc4106_gcm_sync_ctx *ctx = crypto_aead_ctx(parent);
+	struct crypto_aead *child = ctx->child;
+	struct crypto_aead *fallback = ctx->fallback;
+
+	err = crypto_aead_setauthsize(child, authsize);
+	if (err)
+		return err;
+
+	return crypto_aead_setauthsize(fallback, authsize);
+}
+
 static int helper_rfc4106_encrypt(struct aead_request *req)
 {
 	u8 one_entry_in_sg = 0;
@@ -877,6 +953,30 @@
 	return retval;
 }
 
+static int rfc4106_sync_encrypt(struct aead_request *req)
+{
+	struct crypto_aead *aead = crypto_aead_reqtfm(req);
+	struct aesni_rfc4106_gcm_sync_ctx *ctx = crypto_aead_ctx(aead);
+	struct crypto_aead *child = ctx->child;
+	struct crypto_aead *fallback = ctx->fallback;
+
+	aead_request_set_tfm(req, irq_fpu_usable() ? child : fallback);
+
+	return crypto_aead_encrypt(req);
+}
+
+static int rfc4106_sync_decrypt(struct aead_request *req)
+{
+	struct crypto_aead *aead = crypto_aead_reqtfm(req);
+	struct aesni_rfc4106_gcm_sync_ctx *ctx = crypto_aead_ctx(aead);
+	struct crypto_aead *child = ctx->child;
+	struct crypto_aead *fallback = ctx->fallback;
+
+	aead_request_set_tfm(req, irq_fpu_usable() ? child : fallback);
+
+	return crypto_aead_decrypt(req);
+}
+
 static int rfc4106_encrypt(struct aead_request *req)
 {
 	struct crypto_aead *tfm = crypto_aead_reqtfm(req);
@@ -1069,6 +1169,24 @@
 		.cra_ctxsize		= sizeof(struct cryptd_aead *),
 		.cra_module		= THIS_MODULE,
 	},
+}, {
+	.init			= rfc4106_sync_init,
+	.exit			= rfc4106_sync_exit,
+	.setkey			= rfc4106_sync_set_key,
+	.setauthsize		= rfc4106_sync_set_authsize,
+	.encrypt		= rfc4106_sync_encrypt,
+	.decrypt		= rfc4106_sync_decrypt,
+	.ivsize			= 8,
+	.maxauthsize		= 16,
+	.base = {
+		.cra_name		= "rfc4106(gcm(aes))",
+		.cra_driver_name	= "rfc4106-gcm-aesni-sync",
+		.cra_priority		= 350,
+		.cra_flags		= 0,
+		.cra_blocksize		= 1,
+		.cra_ctxsize		= sizeof(struct aesni_rfc4106_gcm_sync_ctx),
+		.cra_module		= THIS_MODULE,
+	},
 } };
 #else
 static struct aead_alg aesni_aead_algs[0];