linux/crypto/scompress.c
<<
>>
Prefs
   1/*
   2 * Synchronous Compression operations
   3 *
   4 * Copyright 2015 LG Electronics Inc.
   5 * Copyright (c) 2016, Intel Corporation
   6 * Author: Giovanni Cabiddu <giovanni.cabiddu@intel.com>
   7 *
   8 * This program is free software; you can redistribute it and/or modify it
   9 * under the terms of the GNU General Public License as published by the Free
  10 * Software Foundation; either version 2 of the License, or (at your option)
  11 * any later version.
  12 *
  13 */
  14#include <linux/errno.h>
  15#include <linux/kernel.h>
  16#include <linux/module.h>
  17#include <linux/seq_file.h>
  18#include <linux/slab.h>
  19#include <linux/string.h>
  20#include <linux/crypto.h>
  21#include <linux/compiler.h>
  22#include <linux/vmalloc.h>
  23#include <crypto/algapi.h>
  24#include <linux/cryptouser.h>
  25#include <net/netlink.h>
  26#include <linux/scatterlist.h>
  27#include <crypto/scatterwalk.h>
  28#include <crypto/internal/acompress.h>
  29#include <crypto/internal/scompress.h>
  30#include "internal.h"
  31
  32static const struct crypto_type crypto_scomp_type;
  33static void * __percpu *scomp_src_scratches;
  34static void * __percpu *scomp_dst_scratches;
  35static int scomp_scratch_users;
  36static DEFINE_MUTEX(scomp_lock);
  37
  38#ifdef CONFIG_NET
  39static int crypto_scomp_report(struct sk_buff *skb, struct crypto_alg *alg)
  40{
  41        struct crypto_report_comp rscomp;
  42
  43        strncpy(rscomp.type, "scomp", sizeof(rscomp.type));
  44
  45        if (nla_put(skb, CRYPTOCFGA_REPORT_COMPRESS,
  46                    sizeof(struct crypto_report_comp), &rscomp))
  47                goto nla_put_failure;
  48        return 0;
  49
  50nla_put_failure:
  51        return -EMSGSIZE;
  52}
  53#else
  54static int crypto_scomp_report(struct sk_buff *skb, struct crypto_alg *alg)
  55{
  56        return -ENOSYS;
  57}
  58#endif
  59
  60static void crypto_scomp_show(struct seq_file *m, struct crypto_alg *alg)
  61        __maybe_unused;
  62
  63static void crypto_scomp_show(struct seq_file *m, struct crypto_alg *alg)
  64{
  65        seq_puts(m, "type         : scomp\n");
  66}
  67
  68static void crypto_scomp_free_scratches(void * __percpu *scratches)
  69{
  70        int i;
  71
  72        if (!scratches)
  73                return;
  74
  75        for_each_possible_cpu(i)
  76                vfree(*per_cpu_ptr(scratches, i));
  77
  78        free_percpu(scratches);
  79}
  80
  81static void * __percpu *crypto_scomp_alloc_scratches(void)
  82{
  83        void * __percpu *scratches;
  84        int i;
  85
  86        scratches = alloc_percpu(void *);
  87        if (!scratches)
  88                return NULL;
  89
  90        for_each_possible_cpu(i) {
  91                void *scratch;
  92
  93                scratch = vmalloc_node(SCOMP_SCRATCH_SIZE, cpu_to_node(i));
  94                if (!scratch)
  95                        goto error;
  96                *per_cpu_ptr(scratches, i) = scratch;
  97        }
  98
  99        return scratches;
 100
 101error:
 102        crypto_scomp_free_scratches(scratches);
 103        return NULL;
 104}
 105
 106static void crypto_scomp_free_all_scratches(void)
 107{
 108        if (!--scomp_scratch_users) {
 109                crypto_scomp_free_scratches(scomp_src_scratches);
 110                crypto_scomp_free_scratches(scomp_dst_scratches);
 111                scomp_src_scratches = NULL;
 112                scomp_dst_scratches = NULL;
 113        }
 114}
 115
 116static int crypto_scomp_alloc_all_scratches(void)
 117{
 118        if (!scomp_scratch_users++) {
 119                scomp_src_scratches = crypto_scomp_alloc_scratches();
 120                if (!scomp_src_scratches)
 121                        return -ENOMEM;
 122                scomp_dst_scratches = crypto_scomp_alloc_scratches();
 123                if (!scomp_dst_scratches) {
 124                        crypto_scomp_free_scratches(scomp_src_scratches);
 125                        scomp_src_scratches = NULL;
 126                        return -ENOMEM;
 127                }
 128        }
 129        return 0;
 130}
 131
 132static int crypto_scomp_init_tfm(struct crypto_tfm *tfm)
 133{
 134        int ret;
 135
 136        mutex_lock(&scomp_lock);
 137        ret = crypto_scomp_alloc_all_scratches();
 138        mutex_unlock(&scomp_lock);
 139
 140        return ret;
 141}
 142
 143static int scomp_acomp_comp_decomp(struct acomp_req *req, int dir)
 144{
 145        struct crypto_acomp *tfm = crypto_acomp_reqtfm(req);
 146        void **tfm_ctx = acomp_tfm_ctx(tfm);
 147        struct crypto_scomp *scomp = *tfm_ctx;
 148        void **ctx = acomp_request_ctx(req);
 149        const int cpu = get_cpu();
 150        u8 *scratch_src = *per_cpu_ptr(scomp_src_scratches, cpu);
 151        u8 *scratch_dst = *per_cpu_ptr(scomp_dst_scratches, cpu);
 152        int ret;
 153
 154        if (!req->src || !req->slen || req->slen > SCOMP_SCRATCH_SIZE) {
 155                ret = -EINVAL;
 156                goto out;
 157        }
 158
 159        if (req->dst && !req->dlen) {
 160                ret = -EINVAL;
 161                goto out;
 162        }
 163
 164        if (!req->dlen || req->dlen > SCOMP_SCRATCH_SIZE)
 165                req->dlen = SCOMP_SCRATCH_SIZE;
 166
 167        scatterwalk_map_and_copy(scratch_src, req->src, 0, req->slen, 0);
 168        if (dir)
 169                ret = crypto_scomp_compress(scomp, scratch_src, req->slen,
 170                                            scratch_dst, &req->dlen, *ctx);
 171        else
 172                ret = crypto_scomp_decompress(scomp, scratch_src, req->slen,
 173                                              scratch_dst, &req->dlen, *ctx);
 174        if (!ret) {
 175                if (!req->dst) {
 176                        req->dst = sgl_alloc(req->dlen, GFP_ATOMIC, NULL);
 177                        if (!req->dst)
 178                                goto out;
 179                }
 180                scatterwalk_map_and_copy(scratch_dst, req->dst, 0, req->dlen,
 181                                         1);
 182        }
 183out:
 184        put_cpu();
 185        return ret;
 186}
 187
 188static int scomp_acomp_compress(struct acomp_req *req)
 189{
 190        return scomp_acomp_comp_decomp(req, 1);
 191}
 192
 193static int scomp_acomp_decompress(struct acomp_req *req)
 194{
 195        return scomp_acomp_comp_decomp(req, 0);
 196}
 197
 198static void crypto_exit_scomp_ops_async(struct crypto_tfm *tfm)
 199{
 200        struct crypto_scomp **ctx = crypto_tfm_ctx(tfm);
 201
 202        crypto_free_scomp(*ctx);
 203
 204        mutex_lock(&scomp_lock);
 205        crypto_scomp_free_all_scratches();
 206        mutex_unlock(&scomp_lock);
 207}
 208
 209int crypto_init_scomp_ops_async(struct crypto_tfm *tfm)
 210{
 211        struct crypto_alg *calg = tfm->__crt_alg;
 212        struct crypto_acomp *crt = __crypto_acomp_tfm(tfm);
 213        struct crypto_scomp **ctx = crypto_tfm_ctx(tfm);
 214        struct crypto_scomp *scomp;
 215
 216        if (!crypto_mod_get(calg))
 217                return -EAGAIN;
 218
 219        scomp = crypto_create_tfm(calg, &crypto_scomp_type);
 220        if (IS_ERR(scomp)) {
 221                crypto_mod_put(calg);
 222                return PTR_ERR(scomp);
 223        }
 224
 225        *ctx = scomp;
 226        tfm->exit = crypto_exit_scomp_ops_async;
 227
 228        crt->compress = scomp_acomp_compress;
 229        crt->decompress = scomp_acomp_decompress;
 230        crt->dst_free = sgl_free;
 231        crt->reqsize = sizeof(void *);
 232
 233        return 0;
 234}
 235
 236struct acomp_req *crypto_acomp_scomp_alloc_ctx(struct acomp_req *req)
 237{
 238        struct crypto_acomp *acomp = crypto_acomp_reqtfm(req);
 239        struct crypto_tfm *tfm = crypto_acomp_tfm(acomp);
 240        struct crypto_scomp **tfm_ctx = crypto_tfm_ctx(tfm);
 241        struct crypto_scomp *scomp = *tfm_ctx;
 242        void *ctx;
 243
 244        ctx = crypto_scomp_alloc_ctx(scomp);
 245        if (IS_ERR(ctx)) {
 246                kfree(req);
 247                return NULL;
 248        }
 249
 250        *req->__ctx = ctx;
 251
 252        return req;
 253}
 254
 255void crypto_acomp_scomp_free_ctx(struct acomp_req *req)
 256{
 257        struct crypto_acomp *acomp = crypto_acomp_reqtfm(req);
 258        struct crypto_tfm *tfm = crypto_acomp_tfm(acomp);
 259        struct crypto_scomp **tfm_ctx = crypto_tfm_ctx(tfm);
 260        struct crypto_scomp *scomp = *tfm_ctx;
 261        void *ctx = *req->__ctx;
 262
 263        if (ctx)
 264                crypto_scomp_free_ctx(scomp, ctx);
 265}
 266
 267static const struct crypto_type crypto_scomp_type = {
 268        .extsize = crypto_alg_extsize,
 269        .init_tfm = crypto_scomp_init_tfm,
 270#ifdef CONFIG_PROC_FS
 271        .show = crypto_scomp_show,
 272#endif
 273        .report = crypto_scomp_report,
 274        .maskclear = ~CRYPTO_ALG_TYPE_MASK,
 275        .maskset = CRYPTO_ALG_TYPE_MASK,
 276        .type = CRYPTO_ALG_TYPE_SCOMPRESS,
 277        .tfmsize = offsetof(struct crypto_scomp, base),
 278};
 279
 280int crypto_register_scomp(struct scomp_alg *alg)
 281{
 282        struct crypto_alg *base = &alg->base;
 283
 284        base->cra_type = &crypto_scomp_type;
 285        base->cra_flags &= ~CRYPTO_ALG_TYPE_MASK;
 286        base->cra_flags |= CRYPTO_ALG_TYPE_SCOMPRESS;
 287
 288        return crypto_register_alg(base);
 289}
 290EXPORT_SYMBOL_GPL(crypto_register_scomp);
 291
 292int crypto_unregister_scomp(struct scomp_alg *alg)
 293{
 294        return crypto_unregister_alg(&alg->base);
 295}
 296EXPORT_SYMBOL_GPL(crypto_unregister_scomp);
 297
 298int crypto_register_scomps(struct scomp_alg *algs, int count)
 299{
 300        int i, ret;
 301
 302        for (i = 0; i < count; i++) {
 303                ret = crypto_register_scomp(&algs[i]);
 304                if (ret)
 305                        goto err;
 306        }
 307
 308        return 0;
 309
 310err:
 311        for (--i; i >= 0; --i)
 312                crypto_unregister_scomp(&algs[i]);
 313
 314        return ret;
 315}
 316EXPORT_SYMBOL_GPL(crypto_register_scomps);
 317
 318void crypto_unregister_scomps(struct scomp_alg *algs, int count)
 319{
 320        int i;
 321
 322        for (i = count - 1; i >= 0; --i)
 323                crypto_unregister_scomp(&algs[i]);
 324}
 325EXPORT_SYMBOL_GPL(crypto_unregister_scomps);
 326
 327MODULE_LICENSE("GPL");
 328MODULE_DESCRIPTION("Synchronous compression type");
 329