12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21ab53a77SGiovanni Cabiddu /*
31ab53a77SGiovanni Cabiddu * Synchronous Compression operations
41ab53a77SGiovanni Cabiddu *
51ab53a77SGiovanni Cabiddu * Copyright 2015 LG Electronics Inc.
61ab53a77SGiovanni Cabiddu * Copyright (c) 2016, Intel Corporation
71ab53a77SGiovanni Cabiddu * Author: Giovanni Cabiddu <giovanni.cabiddu@intel.com>
81ab53a77SGiovanni Cabiddu */
90a742389SHerbert Xu
100a742389SHerbert Xu #include <crypto/internal/acompress.h>
110a742389SHerbert Xu #include <crypto/internal/scompress.h>
120a742389SHerbert Xu #include <crypto/scatterwalk.h>
130a742389SHerbert Xu #include <linux/cryptouser.h>
140a742389SHerbert Xu #include <linux/err.h>
151ab53a77SGiovanni Cabiddu #include <linux/kernel.h>
161ab53a77SGiovanni Cabiddu #include <linux/module.h>
170a742389SHerbert Xu #include <linux/scatterlist.h>
181ab53a77SGiovanni Cabiddu #include <linux/seq_file.h>
191ab53a77SGiovanni Cabiddu #include <linux/slab.h>
201ab53a77SGiovanni Cabiddu #include <linux/string.h>
211ab53a77SGiovanni Cabiddu #include <linux/vmalloc.h>
221ab53a77SGiovanni Cabiddu #include <net/netlink.h>
230a742389SHerbert Xu
240a742389SHerbert Xu #include "compress.h"
251ab53a77SGiovanni Cabiddu
2671052dcfSSebastian Andrzej Siewior struct scomp_scratch {
2771052dcfSSebastian Andrzej Siewior spinlock_t lock;
2871052dcfSSebastian Andrzej Siewior void *src;
2971052dcfSSebastian Andrzej Siewior void *dst;
3071052dcfSSebastian Andrzej Siewior };
3171052dcfSSebastian Andrzej Siewior
3271052dcfSSebastian Andrzej Siewior static DEFINE_PER_CPU(struct scomp_scratch, scomp_scratch) = {
3371052dcfSSebastian Andrzej Siewior .lock = __SPIN_LOCK_UNLOCKED(scomp_scratch.lock),
3471052dcfSSebastian Andrzej Siewior };
3571052dcfSSebastian Andrzej Siewior
361ab53a77SGiovanni Cabiddu static const struct crypto_type crypto_scomp_type;
371ab53a77SGiovanni Cabiddu static int scomp_scratch_users;
381ab53a77SGiovanni Cabiddu static DEFINE_MUTEX(scomp_lock);
391ab53a77SGiovanni Cabiddu
crypto_scomp_report(struct sk_buff * skb,struct crypto_alg * alg)40c0f9e01dSHerbert Xu static int __maybe_unused crypto_scomp_report(
41c0f9e01dSHerbert Xu struct sk_buff *skb, struct crypto_alg *alg)
421ab53a77SGiovanni Cabiddu {
431ab53a77SGiovanni Cabiddu struct crypto_report_comp rscomp;
441ab53a77SGiovanni Cabiddu
4537db69e0SEric Biggers memset(&rscomp, 0, sizeof(rscomp));
461ab53a77SGiovanni Cabiddu
4737db69e0SEric Biggers strscpy(rscomp.type, "scomp", sizeof(rscomp.type));
481ab53a77SGiovanni Cabiddu
4937db69e0SEric Biggers return nla_put(skb, CRYPTOCFGA_REPORT_COMPRESS,
5037db69e0SEric Biggers sizeof(rscomp), &rscomp);
511ab53a77SGiovanni Cabiddu }
521ab53a77SGiovanni Cabiddu
531ab53a77SGiovanni Cabiddu static void crypto_scomp_show(struct seq_file *m, struct crypto_alg *alg)
54d8c34b94SGideon Israel Dsouza __maybe_unused;
551ab53a77SGiovanni Cabiddu
crypto_scomp_show(struct seq_file * m,struct crypto_alg * alg)561ab53a77SGiovanni Cabiddu static void crypto_scomp_show(struct seq_file *m, struct crypto_alg *alg)
571ab53a77SGiovanni Cabiddu {
581ab53a77SGiovanni Cabiddu seq_puts(m, "type : scomp\n");
591ab53a77SGiovanni Cabiddu }
601ab53a77SGiovanni Cabiddu
crypto_scomp_free_scratches(void)6171052dcfSSebastian Andrzej Siewior static void crypto_scomp_free_scratches(void)
621ab53a77SGiovanni Cabiddu {
6371052dcfSSebastian Andrzej Siewior struct scomp_scratch *scratch;
641ab53a77SGiovanni Cabiddu int i;
651ab53a77SGiovanni Cabiddu
661ab53a77SGiovanni Cabiddu for_each_possible_cpu(i) {
678c3fffe3SSebastian Andrzej Siewior scratch = per_cpu_ptr(&scomp_scratch, i);
681ab53a77SGiovanni Cabiddu
6971052dcfSSebastian Andrzej Siewior vfree(scratch->src);
7071052dcfSSebastian Andrzej Siewior vfree(scratch->dst);
7171052dcfSSebastian Andrzej Siewior scratch->src = NULL;
7271052dcfSSebastian Andrzej Siewior scratch->dst = NULL;
7371052dcfSSebastian Andrzej Siewior }
7471052dcfSSebastian Andrzej Siewior }
7571052dcfSSebastian Andrzej Siewior
crypto_scomp_alloc_scratches(void)7671052dcfSSebastian Andrzej Siewior static int crypto_scomp_alloc_scratches(void)
7771052dcfSSebastian Andrzej Siewior {
7871052dcfSSebastian Andrzej Siewior struct scomp_scratch *scratch;
7971052dcfSSebastian Andrzej Siewior int i;
8071052dcfSSebastian Andrzej Siewior
8171052dcfSSebastian Andrzej Siewior for_each_possible_cpu(i) {
8271052dcfSSebastian Andrzej Siewior void *mem;
8371052dcfSSebastian Andrzej Siewior
848c3fffe3SSebastian Andrzej Siewior scratch = per_cpu_ptr(&scomp_scratch, i);
8571052dcfSSebastian Andrzej Siewior
8671052dcfSSebastian Andrzej Siewior mem = vmalloc_node(SCOMP_SCRATCH_SIZE, cpu_to_node(i));
8771052dcfSSebastian Andrzej Siewior if (!mem)
881ab53a77SGiovanni Cabiddu goto error;
8971052dcfSSebastian Andrzej Siewior scratch->src = mem;
9071052dcfSSebastian Andrzej Siewior mem = vmalloc_node(SCOMP_SCRATCH_SIZE, cpu_to_node(i));
9171052dcfSSebastian Andrzej Siewior if (!mem)
9271052dcfSSebastian Andrzej Siewior goto error;
9371052dcfSSebastian Andrzej Siewior scratch->dst = mem;
94cc4d110eSArd Biesheuvel }
951ab53a77SGiovanni Cabiddu return 0;
9671052dcfSSebastian Andrzej Siewior error:
9771052dcfSSebastian Andrzej Siewior crypto_scomp_free_scratches();
9871052dcfSSebastian Andrzej Siewior return -ENOMEM;
991ab53a77SGiovanni Cabiddu }
1001ab53a77SGiovanni Cabiddu
crypto_scomp_init_tfm(struct crypto_tfm * tfm)1016a8487a1SArd Biesheuvel static int crypto_scomp_init_tfm(struct crypto_tfm *tfm)
1026a8487a1SArd Biesheuvel {
10371052dcfSSebastian Andrzej Siewior int ret = 0;
1046a8487a1SArd Biesheuvel
1056a8487a1SArd Biesheuvel mutex_lock(&scomp_lock);
10671052dcfSSebastian Andrzej Siewior if (!scomp_scratch_users++)
10771052dcfSSebastian Andrzej Siewior ret = crypto_scomp_alloc_scratches();
1086a8487a1SArd Biesheuvel mutex_unlock(&scomp_lock);
1096a8487a1SArd Biesheuvel
1106a8487a1SArd Biesheuvel return ret;
1116a8487a1SArd Biesheuvel }
1126a8487a1SArd Biesheuvel
scomp_acomp_comp_decomp(struct acomp_req * req,int dir)1131ab53a77SGiovanni Cabiddu static int scomp_acomp_comp_decomp(struct acomp_req *req, int dir)
1141ab53a77SGiovanni Cabiddu {
1151ab53a77SGiovanni Cabiddu struct crypto_acomp *tfm = crypto_acomp_reqtfm(req);
1161ab53a77SGiovanni Cabiddu void **tfm_ctx = acomp_tfm_ctx(tfm);
1171ab53a77SGiovanni Cabiddu struct crypto_scomp *scomp = *tfm_ctx;
1181ab53a77SGiovanni Cabiddu void **ctx = acomp_request_ctx(req);
11971052dcfSSebastian Andrzej Siewior struct scomp_scratch *scratch;
120*7d9e5bedSChengming Zhou unsigned int dlen;
1211ab53a77SGiovanni Cabiddu int ret;
1221ab53a77SGiovanni Cabiddu
12371052dcfSSebastian Andrzej Siewior if (!req->src || !req->slen || req->slen > SCOMP_SCRATCH_SIZE)
12471052dcfSSebastian Andrzej Siewior return -EINVAL;
1251ab53a77SGiovanni Cabiddu
12671052dcfSSebastian Andrzej Siewior if (req->dst && !req->dlen)
12771052dcfSSebastian Andrzej Siewior return -EINVAL;
1281ab53a77SGiovanni Cabiddu
1291ab53a77SGiovanni Cabiddu if (!req->dlen || req->dlen > SCOMP_SCRATCH_SIZE)
1301ab53a77SGiovanni Cabiddu req->dlen = SCOMP_SCRATCH_SIZE;
1311ab53a77SGiovanni Cabiddu
132*7d9e5bedSChengming Zhou dlen = req->dlen;
133*7d9e5bedSChengming Zhou
13471052dcfSSebastian Andrzej Siewior scratch = raw_cpu_ptr(&scomp_scratch);
13571052dcfSSebastian Andrzej Siewior spin_lock(&scratch->lock);
13671052dcfSSebastian Andrzej Siewior
13771052dcfSSebastian Andrzej Siewior scatterwalk_map_and_copy(scratch->src, req->src, 0, req->slen, 0);
1381ab53a77SGiovanni Cabiddu if (dir)
13971052dcfSSebastian Andrzej Siewior ret = crypto_scomp_compress(scomp, scratch->src, req->slen,
14071052dcfSSebastian Andrzej Siewior scratch->dst, &req->dlen, *ctx);
1411ab53a77SGiovanni Cabiddu else
14271052dcfSSebastian Andrzej Siewior ret = crypto_scomp_decompress(scomp, scratch->src, req->slen,
14371052dcfSSebastian Andrzej Siewior scratch->dst, &req->dlen, *ctx);
1441ab53a77SGiovanni Cabiddu if (!ret) {
1451ab53a77SGiovanni Cabiddu if (!req->dst) {
1468cd579d2SBart Van Assche req->dst = sgl_alloc(req->dlen, GFP_ATOMIC, NULL);
1476a4d1b18SSebastian Andrzej Siewior if (!req->dst) {
1486a4d1b18SSebastian Andrzej Siewior ret = -ENOMEM;
1491ab53a77SGiovanni Cabiddu goto out;
1501ab53a77SGiovanni Cabiddu }
151*7d9e5bedSChengming Zhou } else if (req->dlen > dlen) {
152*7d9e5bedSChengming Zhou ret = -ENOSPC;
153*7d9e5bedSChengming Zhou goto out;
1546a4d1b18SSebastian Andrzej Siewior }
15571052dcfSSebastian Andrzej Siewior scatterwalk_map_and_copy(scratch->dst, req->dst, 0, req->dlen,
1561ab53a77SGiovanni Cabiddu 1);
1571ab53a77SGiovanni Cabiddu }
1581ab53a77SGiovanni Cabiddu out:
15971052dcfSSebastian Andrzej Siewior spin_unlock(&scratch->lock);
1601ab53a77SGiovanni Cabiddu return ret;
1611ab53a77SGiovanni Cabiddu }
1621ab53a77SGiovanni Cabiddu
scomp_acomp_compress(struct acomp_req * req)1631ab53a77SGiovanni Cabiddu static int scomp_acomp_compress(struct acomp_req *req)
1641ab53a77SGiovanni Cabiddu {
1651ab53a77SGiovanni Cabiddu return scomp_acomp_comp_decomp(req, 1);
1661ab53a77SGiovanni Cabiddu }
1671ab53a77SGiovanni Cabiddu
scomp_acomp_decompress(struct acomp_req * req)1681ab53a77SGiovanni Cabiddu static int scomp_acomp_decompress(struct acomp_req *req)
1691ab53a77SGiovanni Cabiddu {
1701ab53a77SGiovanni Cabiddu return scomp_acomp_comp_decomp(req, 0);
1711ab53a77SGiovanni Cabiddu }
1721ab53a77SGiovanni Cabiddu
crypto_exit_scomp_ops_async(struct crypto_tfm * tfm)1731ab53a77SGiovanni Cabiddu static void crypto_exit_scomp_ops_async(struct crypto_tfm *tfm)
1741ab53a77SGiovanni Cabiddu {
1751ab53a77SGiovanni Cabiddu struct crypto_scomp **ctx = crypto_tfm_ctx(tfm);
1761ab53a77SGiovanni Cabiddu
1771ab53a77SGiovanni Cabiddu crypto_free_scomp(*ctx);
1786a8487a1SArd Biesheuvel
1796a8487a1SArd Biesheuvel mutex_lock(&scomp_lock);
18071052dcfSSebastian Andrzej Siewior if (!--scomp_scratch_users)
18171052dcfSSebastian Andrzej Siewior crypto_scomp_free_scratches();
1826a8487a1SArd Biesheuvel mutex_unlock(&scomp_lock);
1831ab53a77SGiovanni Cabiddu }
1841ab53a77SGiovanni Cabiddu
crypto_init_scomp_ops_async(struct crypto_tfm * tfm)1851ab53a77SGiovanni Cabiddu int crypto_init_scomp_ops_async(struct crypto_tfm *tfm)
1861ab53a77SGiovanni Cabiddu {
1871ab53a77SGiovanni Cabiddu struct crypto_alg *calg = tfm->__crt_alg;
1881ab53a77SGiovanni Cabiddu struct crypto_acomp *crt = __crypto_acomp_tfm(tfm);
1891ab53a77SGiovanni Cabiddu struct crypto_scomp **ctx = crypto_tfm_ctx(tfm);
1901ab53a77SGiovanni Cabiddu struct crypto_scomp *scomp;
1911ab53a77SGiovanni Cabiddu
1921ab53a77SGiovanni Cabiddu if (!crypto_mod_get(calg))
1931ab53a77SGiovanni Cabiddu return -EAGAIN;
1941ab53a77SGiovanni Cabiddu
1951ab53a77SGiovanni Cabiddu scomp = crypto_create_tfm(calg, &crypto_scomp_type);
1961ab53a77SGiovanni Cabiddu if (IS_ERR(scomp)) {
1971ab53a77SGiovanni Cabiddu crypto_mod_put(calg);
1981ab53a77SGiovanni Cabiddu return PTR_ERR(scomp);
1991ab53a77SGiovanni Cabiddu }
2001ab53a77SGiovanni Cabiddu
2011ab53a77SGiovanni Cabiddu *ctx = scomp;
2021ab53a77SGiovanni Cabiddu tfm->exit = crypto_exit_scomp_ops_async;
2031ab53a77SGiovanni Cabiddu
2041ab53a77SGiovanni Cabiddu crt->compress = scomp_acomp_compress;
2051ab53a77SGiovanni Cabiddu crt->decompress = scomp_acomp_decompress;
2068cd579d2SBart Van Assche crt->dst_free = sgl_free;
2071ab53a77SGiovanni Cabiddu crt->reqsize = sizeof(void *);
2081ab53a77SGiovanni Cabiddu
2091ab53a77SGiovanni Cabiddu return 0;
2101ab53a77SGiovanni Cabiddu }
2111ab53a77SGiovanni Cabiddu
crypto_acomp_scomp_alloc_ctx(struct acomp_req * req)2121ab53a77SGiovanni Cabiddu struct acomp_req *crypto_acomp_scomp_alloc_ctx(struct acomp_req *req)
2131ab53a77SGiovanni Cabiddu {
2141ab53a77SGiovanni Cabiddu struct crypto_acomp *acomp = crypto_acomp_reqtfm(req);
2151ab53a77SGiovanni Cabiddu struct crypto_tfm *tfm = crypto_acomp_tfm(acomp);
2161ab53a77SGiovanni Cabiddu struct crypto_scomp **tfm_ctx = crypto_tfm_ctx(tfm);
2171ab53a77SGiovanni Cabiddu struct crypto_scomp *scomp = *tfm_ctx;
2181ab53a77SGiovanni Cabiddu void *ctx;
2191ab53a77SGiovanni Cabiddu
2201ab53a77SGiovanni Cabiddu ctx = crypto_scomp_alloc_ctx(scomp);
2211ab53a77SGiovanni Cabiddu if (IS_ERR(ctx)) {
2221ab53a77SGiovanni Cabiddu kfree(req);
2231ab53a77SGiovanni Cabiddu return NULL;
2241ab53a77SGiovanni Cabiddu }
2251ab53a77SGiovanni Cabiddu
2261ab53a77SGiovanni Cabiddu *req->__ctx = ctx;
2271ab53a77SGiovanni Cabiddu
2281ab53a77SGiovanni Cabiddu return req;
2291ab53a77SGiovanni Cabiddu }
2301ab53a77SGiovanni Cabiddu
crypto_acomp_scomp_free_ctx(struct acomp_req * req)2311ab53a77SGiovanni Cabiddu void crypto_acomp_scomp_free_ctx(struct acomp_req *req)
2321ab53a77SGiovanni Cabiddu {
2331ab53a77SGiovanni Cabiddu struct crypto_acomp *acomp = crypto_acomp_reqtfm(req);
2341ab53a77SGiovanni Cabiddu struct crypto_tfm *tfm = crypto_acomp_tfm(acomp);
2351ab53a77SGiovanni Cabiddu struct crypto_scomp **tfm_ctx = crypto_tfm_ctx(tfm);
2361ab53a77SGiovanni Cabiddu struct crypto_scomp *scomp = *tfm_ctx;
2371ab53a77SGiovanni Cabiddu void *ctx = *req->__ctx;
2381ab53a77SGiovanni Cabiddu
2391ab53a77SGiovanni Cabiddu if (ctx)
2401ab53a77SGiovanni Cabiddu crypto_scomp_free_ctx(scomp, ctx);
2411ab53a77SGiovanni Cabiddu }
2421ab53a77SGiovanni Cabiddu
2431ab53a77SGiovanni Cabiddu static const struct crypto_type crypto_scomp_type = {
2441ab53a77SGiovanni Cabiddu .extsize = crypto_alg_extsize,
2451ab53a77SGiovanni Cabiddu .init_tfm = crypto_scomp_init_tfm,
2461ab53a77SGiovanni Cabiddu #ifdef CONFIG_PROC_FS
2471ab53a77SGiovanni Cabiddu .show = crypto_scomp_show,
2481ab53a77SGiovanni Cabiddu #endif
249b8969a1bSOndrej Mosnacek #if IS_ENABLED(CONFIG_CRYPTO_USER)
2501ab53a77SGiovanni Cabiddu .report = crypto_scomp_report,
251c0f9e01dSHerbert Xu #endif
2520a742389SHerbert Xu #ifdef CONFIG_CRYPTO_STATS
2530a742389SHerbert Xu .report_stat = crypto_acomp_report_stat,
2540a742389SHerbert Xu #endif
2551ab53a77SGiovanni Cabiddu .maskclear = ~CRYPTO_ALG_TYPE_MASK,
2561ab53a77SGiovanni Cabiddu .maskset = CRYPTO_ALG_TYPE_MASK,
2571ab53a77SGiovanni Cabiddu .type = CRYPTO_ALG_TYPE_SCOMPRESS,
2581ab53a77SGiovanni Cabiddu .tfmsize = offsetof(struct crypto_scomp, base),
2591ab53a77SGiovanni Cabiddu };
2601ab53a77SGiovanni Cabiddu
crypto_register_scomp(struct scomp_alg * alg)2611ab53a77SGiovanni Cabiddu int crypto_register_scomp(struct scomp_alg *alg)
2621ab53a77SGiovanni Cabiddu {
2630a742389SHerbert Xu struct crypto_alg *base = &alg->calg.base;
2640a742389SHerbert Xu
2650a742389SHerbert Xu comp_prepare_alg(&alg->calg);
2661ab53a77SGiovanni Cabiddu
2671ab53a77SGiovanni Cabiddu base->cra_type = &crypto_scomp_type;
2681ab53a77SGiovanni Cabiddu base->cra_flags |= CRYPTO_ALG_TYPE_SCOMPRESS;
2691ab53a77SGiovanni Cabiddu
2706a8487a1SArd Biesheuvel return crypto_register_alg(base);
2711ab53a77SGiovanni Cabiddu }
2721ab53a77SGiovanni Cabiddu EXPORT_SYMBOL_GPL(crypto_register_scomp);
2731ab53a77SGiovanni Cabiddu
crypto_unregister_scomp(struct scomp_alg * alg)274c6d633a9SEric Biggers void crypto_unregister_scomp(struct scomp_alg *alg)
2751ab53a77SGiovanni Cabiddu {
276c6d633a9SEric Biggers crypto_unregister_alg(&alg->base);
2771ab53a77SGiovanni Cabiddu }
2781ab53a77SGiovanni Cabiddu EXPORT_SYMBOL_GPL(crypto_unregister_scomp);
2791ab53a77SGiovanni Cabiddu
crypto_register_scomps(struct scomp_alg * algs,int count)2803de4f5e1SGiovanni Cabiddu int crypto_register_scomps(struct scomp_alg *algs, int count)
2813de4f5e1SGiovanni Cabiddu {
2823de4f5e1SGiovanni Cabiddu int i, ret;
2833de4f5e1SGiovanni Cabiddu
2843de4f5e1SGiovanni Cabiddu for (i = 0; i < count; i++) {
2853de4f5e1SGiovanni Cabiddu ret = crypto_register_scomp(&algs[i]);
2863de4f5e1SGiovanni Cabiddu if (ret)
2873de4f5e1SGiovanni Cabiddu goto err;
2883de4f5e1SGiovanni Cabiddu }
2893de4f5e1SGiovanni Cabiddu
2903de4f5e1SGiovanni Cabiddu return 0;
2913de4f5e1SGiovanni Cabiddu
2923de4f5e1SGiovanni Cabiddu err:
2933de4f5e1SGiovanni Cabiddu for (--i; i >= 0; --i)
2943de4f5e1SGiovanni Cabiddu crypto_unregister_scomp(&algs[i]);
2953de4f5e1SGiovanni Cabiddu
2963de4f5e1SGiovanni Cabiddu return ret;
2973de4f5e1SGiovanni Cabiddu }
2983de4f5e1SGiovanni Cabiddu EXPORT_SYMBOL_GPL(crypto_register_scomps);
2993de4f5e1SGiovanni Cabiddu
crypto_unregister_scomps(struct scomp_alg * algs,int count)3003de4f5e1SGiovanni Cabiddu void crypto_unregister_scomps(struct scomp_alg *algs, int count)
3013de4f5e1SGiovanni Cabiddu {
3023de4f5e1SGiovanni Cabiddu int i;
3033de4f5e1SGiovanni Cabiddu
3043de4f5e1SGiovanni Cabiddu for (i = count - 1; i >= 0; --i)
3053de4f5e1SGiovanni Cabiddu crypto_unregister_scomp(&algs[i]);
3063de4f5e1SGiovanni Cabiddu }
3073de4f5e1SGiovanni Cabiddu EXPORT_SYMBOL_GPL(crypto_unregister_scomps);
3083de4f5e1SGiovanni Cabiddu
3091ab53a77SGiovanni Cabiddu MODULE_LICENSE("GPL");
3101ab53a77SGiovanni Cabiddu MODULE_DESCRIPTION("Synchronous compression type");
311