crypto: algif_compression - User-space interface for compression

This patch adds af_alg plugin for compression algorithms of type scomp/acomp
registered to the kernel crypto layer.

The user needs to set operation (compression/decompression) as a control
message to sendmsg, identical to selecting the cipher operation type in case of
ciphers. Once a sendmsg call occurs, no further writes can be made to the
socket until all previous data has been processed and read. Therefore the
interface only supports one request at a time.

The interface is completely synchronous; all operations are carried out in
recvmsg and will complete prior to the system call returning.

The sendmsg and recvmsg interface supports directly reading/writing to 
user-space without additional copying, i.e., the kernel crypto interface will
receive the user-space address as its input/output SG list. The scomp interface
or crypto drivers may copy the data as required.

Signed-off-by: Abed Kamaluddin <akam...@cavium.com>
Signed-off-by: Mahipal Challa <mahipal.cha...@cavium.com>

---
 crypto/Kconfig              |  11 ++
 crypto/Makefile             |   1 +
 crypto/algif_compression.c  | 272 ++++++++++++++++++++++++++++++++++++++++++++
 include/uapi/linux/if_alg.h |   2 +
 4 files changed, 286 insertions(+)

diff --git a/crypto/Kconfig b/crypto/Kconfig
index f37e9cc..13b03ba 100644
--- a/crypto/Kconfig
+++ b/crypto/Kconfig
@@ -1741,6 +1741,17 @@ config CRYPTO_USER_API_AEAD
          This option enables the user-spaces interface for AEAD
          cipher algorithms.
 
+config CRYPTO_USER_API_COMPRESSION
+       tristate "User-space interface for compression algorithms"
+       depends on NET
+       select CRYPTO_ACOMP
+       select CRYPTO_USER_API
+       help
+         This option enables the user-space interface for compression
+         algorithms. Enable this option for access to compression algorithms
+         of type scomp/acomp exported by the kernel crypto layer through
+         AF_ALG interface.
+
 config CRYPTO_HASH_INFO
        bool
 
diff --git a/crypto/Makefile b/crypto/Makefile
index 8a44057..1469e06 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -134,6 +134,7 @@ obj-$(CONFIG_CRYPTO_TEST) += tcrypt.o
 obj-$(CONFIG_CRYPTO_GHASH) += ghash-generic.o
 obj-$(CONFIG_CRYPTO_USER_API) += af_alg.o
 obj-$(CONFIG_CRYPTO_USER_API_HASH) += algif_hash.o
+obj-$(CONFIG_CRYPTO_USER_API_COMPRESSION) += algif_compression.o
 obj-$(CONFIG_CRYPTO_USER_API_SKCIPHER) += algif_skcipher.o
 obj-$(CONFIG_CRYPTO_USER_API_RNG) += algif_rng.o
 obj-$(CONFIG_CRYPTO_USER_API_AEAD) += algif_aead.o
diff --git a/crypto/algif_compression.c b/crypto/algif_compression.c
new file mode 100644
index 0000000..0ba6d1e
--- /dev/null
+++ b/crypto/algif_compression.c
@@ -0,0 +1,272 @@
+/*
+ * algif_compression: User-space interface for COMPRESSION algorithms
+ *
+ * This file provides user-space API support for compression algorithms
+ * registered through the kernel crypto layer.
+ *
+ * Copyright (C) 2017 Cavium, Inc.
+ *
+ * Original Authors: Abed Kamaluddin <akamalud...@cavium.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <crypto/acompress.h>
+#include <crypto/if_alg.h>
+#include <linux/crypto.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/net.h>
+#include <net/sock.h>
+#include <linux/scatterlist.h>
+
+/* scomp scratch is currently 128KB */
+#define COMP_BUFFER_SIZE       65535
+
+struct comp_ctx {
+       struct af_alg_sgl tsgl;
+       struct af_alg_sgl rsgl;
+       struct af_alg_completion completion;
+       unsigned int clen;
+       unsigned int slen;
+       unsigned int dlen;
+       bool comp;
+       bool used;
+       struct acomp_req *acomp_req;
+};
+
+struct comp_tfm {
+       struct crypto_acomp *acomp;
+};
+
+static int comp_sendmsg(struct socket *sock, struct msghdr *msg,
+                       size_t ignored)
+{
+       struct sock *sk = sock->sk;
+       struct alg_sock *ask = alg_sk(sk);
+       struct comp_ctx *ctx = ask->private;
+       struct af_alg_control con = {};
+       int limit = COMP_BUFFER_SIZE;
+       int len;
+       int err = -EINVAL;
+
+       if (msg->msg_controllen) {
+               err = af_alg_cmsg_send(msg, &con);
+               if (err)
+                       return err;
+
+               switch (con.op) {
+               case ALG_OP_COMPRESS:
+                       ctx->comp = 1;
+                       break;
+
+               case ALG_OP_DECOMPRESS:
+                       ctx->comp = 0;
+                       break;
+
+               default:
+                       return -EINVAL;
+               }
+       }
+
+       lock_sock(sk);
+
+       /* One request at a time supported, data submitted for comp/decomp will
+        * be processed at subsequent recvmsg
+        */
+       if (ctx->used) {
+               err = -EAGAIN;
+               goto unlock;
+       }
+
+       len = msg_data_left(msg);
+
+       if (len > limit)
+               len = limit;
+
+       len = af_alg_make_sg(&ctx->tsgl, &msg->msg_iter, len);
+
+       if (len < 0) {
+               err = len;
+               goto unlock;
+       }
+
+       ctx->slen = len;
+       ctx->used = 1;
+
+unlock:
+       release_sock(sk);
+
+       return err ?: len;
+}
+
+static int comp_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
+                       int flags)
+{
+       struct sock *sk = sock->sk;
+       struct alg_sock *ask = alg_sk(sk);
+       struct comp_ctx *ctx = ask->private;
+       int rlen = ctx->dlen;
+       int err;
+
+       if (len > rlen)
+               len = rlen;
+
+       lock_sock(sk);
+
+       if (!ctx->used) {
+               err = -EAGAIN;
+               goto unlock;
+       }
+
+       len = af_alg_make_sg(&ctx->rsgl, &msg->msg_iter, len);
+       if (len < 0) {
+               err = len;
+               goto unlock;
+       }
+
+       acomp_request_set_params(ctx->acomp_req, ctx->tsgl.sg, ctx->rsgl.sg,
+                                ctx->slen, len);
+
+       /* Synchronous completion of comp/decomp requests */
+       err = af_alg_wait_for_completion(
+                       ctx->comp ?
+                               crypto_acomp_compress(ctx->acomp_req) :
+                               crypto_acomp_decompress(ctx->acomp_req),
+                               &ctx->completion);
+
+       /* Add acomp req wrapper for dlen */
+       len = (ctx->acomp_req)->dlen;
+
+       af_alg_free_sg(&ctx->tsgl);
+       af_alg_free_sg(&ctx->rsgl);
+
+unlock:
+       ctx->used = 0;
+       release_sock(sk);
+
+       return err ?: len;
+}
+
+static struct proto_ops algif_comp_ops = {
+       .family         =       PF_ALG,
+
+       .connect        =       sock_no_connect,
+       .socketpair     =       sock_no_socketpair,
+       .getname        =       sock_no_getname,
+       .ioctl          =       sock_no_ioctl,
+       .listen         =       sock_no_listen,
+       .shutdown       =       sock_no_shutdown,
+       .getsockopt     =       sock_no_getsockopt,
+       .mmap           =       sock_no_mmap,
+       .bind           =       sock_no_bind,
+       .setsockopt     =       sock_no_setsockopt,
+       .poll           =       sock_no_poll,
+
+       .release        =       af_alg_release,
+       .sendmsg        =       comp_sendmsg,
+       .recvmsg        =       comp_recvmsg,
+       .sendpage       =       sock_no_sendpage,
+       .accept         =       sock_no_accept,
+};
+
+static void *comp_bind(const char *name, u32 type, u32 mask)
+{
+       struct comp_tfm *tfm;
+       struct crypto_acomp *acomp;
+
+       tfm = kzalloc(sizeof(*tfm), GFP_KERNEL);
+       if (!tfm)
+               return ERR_PTR(-ENOMEM);
+
+       acomp = crypto_alloc_acomp(name, type, mask);
+       if (IS_ERR_OR_NULL(acomp)) {
+               kfree(tfm);
+               return ERR_PTR(-ENOMEM);
+       }
+       tfm->acomp = acomp;
+
+       return tfm;
+}
+
+static void comp_release(void *private)
+{
+       struct comp_tfm *tfm = private;
+
+       crypto_free_acomp(tfm->acomp);
+       kfree(tfm);
+}
+
+static void comp_sock_destruct(struct sock *sk)
+{
+       struct alg_sock *ask = alg_sk(sk);
+       struct comp_ctx *ctx = ask->private;
+
+       acomp_request_free(ctx->acomp_req);
+       sock_kfree_s(sk, ctx, ctx->clen);
+       af_alg_release_parent(sk);
+}
+
+static int comp_accept_parent(void *private, struct sock *sk)
+{
+       struct comp_ctx *ctx;
+       struct alg_sock *ask = alg_sk(sk);
+       struct comp_tfm *tfm = private;
+       struct crypto_acomp *acomp = tfm->acomp;
+       unsigned int len = sizeof(*ctx);
+
+       ctx = sock_kmalloc(sk, len, GFP_KERNEL);
+       if (!ctx)
+               return -ENOMEM;
+
+       ctx->used = 0;
+       ctx->clen = len;
+       ctx->dlen = COMP_BUFFER_SIZE;
+       ctx->slen = COMP_BUFFER_SIZE;
+
+       af_alg_init_completion(&ctx->completion);
+
+       ctx->acomp_req = acomp_request_alloc(acomp);
+       if (!ctx->acomp_req) {
+               sock_kfree_s(sk, ctx, ctx->clen);
+               return -ENOMEM;
+       }
+
+       acomp_request_set_callback(ctx->acomp_req, CRYPTO_TFM_REQ_MAY_BACKLOG,
+                                  af_alg_complete, &ctx->completion);
+
+       ask->private = ctx;
+       sk->sk_destruct = comp_sock_destruct;
+
+       return 0;
+}
+
+static const struct af_alg_type algif_type_comp = {
+       .bind           =       comp_bind,
+       .release        =       comp_release,
+       .accept         =       comp_accept_parent,
+       .ops            =       &algif_comp_ops,
+       .name           =       "compression",
+       .owner          =       THIS_MODULE
+};
+
+static int __init algif_comp_init(void)
+{
+       return af_alg_register_type(&algif_type_comp);
+}
+
+static void __exit algif_comp_exit(void)
+{
+       int err = af_alg_unregister_type(&algif_type_comp);
+
+       BUG_ON(err);
+}
+
+module_init(algif_comp_init);
+module_exit(algif_comp_exit);
+MODULE_LICENSE("GPL");
diff --git a/include/uapi/linux/if_alg.h b/include/uapi/linux/if_alg.h
index f2acd2f..5cca9eb 100644
--- a/include/uapi/linux/if_alg.h
+++ b/include/uapi/linux/if_alg.h
@@ -38,5 +38,7 @@ struct af_alg_iv {
 /* Operations */
 #define ALG_OP_DECRYPT                 0
 #define ALG_OP_ENCRYPT                 1
+#define ALG_OP_DECOMPRESS              0
+#define ALG_OP_COMPRESS                1
 
 #endif /* _LINUX_IF_ALG_H */
-- 
2.7.4

Reply via email to