root/drivers/block/zram/backend_lz4.c
#include <linux/kernel.h>
#include <linux/lz4.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>

#include "backend_lz4.h"

struct lz4_ctx {
        void *mem;

        LZ4_streamDecode_t *dstrm;
        LZ4_stream_t *cstrm;
};

static void lz4_release_params(struct zcomp_params *params)
{
}

static int lz4_setup_params(struct zcomp_params *params)
{
        if (params->level == ZCOMP_PARAM_NOT_SET)
                params->level = LZ4_ACCELERATION_DEFAULT;

        return 0;
}

static void lz4_destroy(struct zcomp_ctx *ctx)
{
        struct lz4_ctx *zctx = ctx->context;

        if (!zctx)
                return;

        vfree(zctx->mem);
        kfree(zctx->dstrm);
        kfree(zctx->cstrm);
        kfree(zctx);
}

static int lz4_create(struct zcomp_params *params, struct zcomp_ctx *ctx)
{
        struct lz4_ctx *zctx;

        zctx = kzalloc_obj(*zctx);
        if (!zctx)
                return -ENOMEM;

        ctx->context = zctx;
        if (params->dict_sz == 0) {
                zctx->mem = vmalloc(LZ4_MEM_COMPRESS);
                if (!zctx->mem)
                        goto error;
        } else {
                zctx->dstrm = kzalloc_obj(*zctx->dstrm);
                if (!zctx->dstrm)
                        goto error;

                zctx->cstrm = kzalloc_obj(*zctx->cstrm);
                if (!zctx->cstrm)
                        goto error;
        }

        return 0;

error:
        lz4_destroy(ctx);
        return -ENOMEM;
}

static int lz4_compress(struct zcomp_params *params, struct zcomp_ctx *ctx,
                        struct zcomp_req *req)
{
        struct lz4_ctx *zctx = ctx->context;
        int ret;

        if (!zctx->cstrm) {
                ret = LZ4_compress_fast(req->src, req->dst, req->src_len,
                                        req->dst_len, params->level,
                                        zctx->mem);
        } else {
                /* Cstrm needs to be reset */
                ret = LZ4_loadDict(zctx->cstrm, params->dict, params->dict_sz);
                if (ret != params->dict_sz)
                        return -EINVAL;
                ret = LZ4_compress_fast_continue(zctx->cstrm, req->src,
                                                 req->dst, req->src_len,
                                                 req->dst_len, params->level);
        }
        if (!ret)
                return -EINVAL;
        req->dst_len = ret;
        return 0;
}

static int lz4_decompress(struct zcomp_params *params, struct zcomp_ctx *ctx,
                          struct zcomp_req *req)
{
        struct lz4_ctx *zctx = ctx->context;
        int ret;

        if (!zctx->dstrm) {
                ret = LZ4_decompress_safe(req->src, req->dst, req->src_len,
                                          req->dst_len);
        } else {
                /* Dstrm needs to be reset */
                ret = LZ4_setStreamDecode(zctx->dstrm, params->dict,
                                          params->dict_sz);
                if (!ret)
                        return -EINVAL;
                ret = LZ4_decompress_safe_continue(zctx->dstrm, req->src,
                                                   req->dst, req->src_len,
                                                   req->dst_len);
        }
        if (ret < 0)
                return -EINVAL;
        return 0;
}

const struct zcomp_ops backend_lz4 = {
        .compress       = lz4_compress,
        .decompress     = lz4_decompress,
        .create_ctx     = lz4_create,
        .destroy_ctx    = lz4_destroy,
        .setup_params   = lz4_setup_params,
        .release_params = lz4_release_params,
        .name           = "lz4",
};