// SPDX-License-Identifier: GPL-2.0-only /* Copyright(c) 2022 Intel Corporation */ #include #include #include #include #include #include #include "adf_accel_devices.h" #include "adf_common_drv.h" #include "qat_bl.h" #include "qat_comp_req.h" #include "qat_compression.h" #include "qat_algs_send.h" static DEFINE_MUTEX(algs_lock); static unsigned int active_devs; enum direction { DECOMPRESSION = 0, COMPRESSION = 1, }; struct qat_compression_req; struct qat_compression_ctx { u8 comp_ctx[QAT_COMP_CTX_SIZE]; struct qat_compression_instance *inst; int (*qat_comp_callback)(struct qat_compression_req *qat_req, void *resp); }; struct qat_dst { bool is_null; int resubmitted; }; struct qat_compression_req { u8 req[QAT_COMP_REQ_SIZE]; struct qat_compression_ctx *qat_compression_ctx; struct acomp_req *acompress_req; struct qat_request_buffs buf; enum direction dir; int actual_dlen; struct qat_alg_req alg_req; struct work_struct resubmit; struct qat_dst dst; }; static int qat_alg_send_dc_message(struct qat_compression_req *qat_req, struct qat_compression_instance *inst, struct crypto_async_request *base) { struct qat_alg_req *alg_req = &qat_req->alg_req; alg_req->fw_req = (u32 *)&qat_req->req; alg_req->tx_ring = inst->dc_tx; alg_req->base = base; alg_req->backlog = &inst->backlog; return qat_alg_send_message(alg_req); } static void qat_comp_resubmit(struct work_struct *work) { struct qat_compression_req *qat_req = container_of(work, struct qat_compression_req, resubmit); struct qat_compression_ctx *ctx = qat_req->qat_compression_ctx; struct adf_accel_dev *accel_dev = ctx->inst->accel_dev; struct qat_request_buffs *qat_bufs = &qat_req->buf; struct qat_compression_instance *inst = ctx->inst; struct acomp_req *areq = qat_req->acompress_req; struct crypto_acomp *tfm = crypto_acomp_reqtfm(areq); unsigned int dlen = CRYPTO_ACOMP_DST_MAX; u8 *req = qat_req->req; dma_addr_t dfbuf; int ret; areq->dlen = dlen; dev_dbg(&GET_DEV(accel_dev), "[%s][%s] retry NULL dst request - dlen = %d\n", crypto_tfm_alg_driver_name(crypto_acomp_tfm(tfm)), qat_req->dir == COMPRESSION ? "comp" : "decomp", dlen); ret = qat_bl_realloc_map_new_dst(accel_dev, &areq->dst, dlen, qat_bufs, qat_algs_alloc_flags(&areq->base)); if (ret) goto err; qat_req->dst.resubmitted = true; dfbuf = qat_req->buf.bloutp; qat_comp_override_dst(req, dfbuf, dlen); ret = qat_alg_send_dc_message(qat_req, inst, &areq->base); if (ret != -ENOSPC) return; err: qat_bl_free_bufl(accel_dev, qat_bufs); acomp_request_complete(areq, ret); } static void qat_comp_generic_callback(struct qat_compression_req *qat_req, void *resp) { struct acomp_req *areq = qat_req->acompress_req; struct qat_compression_ctx *ctx = qat_req->qat_compression_ctx; struct adf_accel_dev *accel_dev = ctx->inst->accel_dev; struct crypto_acomp *tfm = crypto_acomp_reqtfm(areq); struct qat_compression_instance *inst = ctx->inst; int consumed, produced; s8 cmp_err, xlt_err; int res = -EBADMSG; int status; u8 cnv; status = qat_comp_get_cmp_status(resp); status |= qat_comp_get_xlt_status(resp); cmp_err = qat_comp_get_cmp_err(resp); xlt_err = qat_comp_get_xlt_err(resp); consumed = qat_comp_get_consumed_ctr(resp); produced = qat_comp_get_produced_ctr(resp); dev_dbg(&GET_DEV(accel_dev), "[%s][%s][%s] slen = %8d dlen = %8d consumed = %8d produced = %8d cmp_err = %3d xlt_err = %3d", crypto_tfm_alg_driver_name(crypto_acomp_tfm(tfm)), qat_req->dir == COMPRESSION ? "comp " : "decomp", status ? "ERR" : "OK ", areq->slen, areq->dlen, consumed, produced, cmp_err, xlt_err); areq->dlen = 0; if (qat_req->dir == DECOMPRESSION && qat_req->dst.is_null) { if (cmp_err == ERR_CODE_OVERFLOW_ERROR) { if (qat_req->dst.resubmitted) { dev_dbg(&GET_DEV(accel_dev), "Output does not fit destination buffer\n"); res = -EOVERFLOW; goto end; } INIT_WORK(&qat_req->resubmit, qat_comp_resubmit); adf_misc_wq_queue_work(&qat_req->resubmit); return; } } if (unlikely(status != ICP_QAT_FW_COMN_STATUS_FLAG_OK)) goto end; if (qat_req->dir == COMPRESSION) { cnv = qat_comp_get_cmp_cnv_flag(resp); if (unlikely(!cnv)) { dev_err(&GET_DEV(accel_dev), "Verified compression not supported\n"); goto end; } if (unlikely(produced > qat_req->actual_dlen)) { memset(inst->dc_data->ovf_buff, 0, inst->dc_data->ovf_buff_sz); dev_dbg(&GET_DEV(accel_dev), "Actual buffer overflow: produced=%d, dlen=%d\n", produced, qat_req->actual_dlen); goto end; } } res = 0; areq->dlen = produced; if (ctx->qat_comp_callback) res = ctx->qat_comp_callback(qat_req, resp); end: qat_bl_free_bufl(accel_dev, &qat_req->buf); acomp_request_complete(areq, res); } void qat_comp_alg_callback(void *resp) { struct qat_compression_req *qat_req = (void *)(__force long)qat_comp_get_opaque(resp); struct qat_instance_backlog *backlog = qat_req->alg_req.backlog; qat_comp_generic_callback(qat_req, resp); qat_alg_send_backlog(backlog); } static int qat_comp_alg_init_tfm(struct crypto_acomp *acomp_tfm) { struct crypto_tfm *tfm = crypto_acomp_tfm(acomp_tfm); struct qat_compression_ctx *ctx = crypto_tfm_ctx(tfm); struct qat_compression_instance *inst; int node; if (tfm->node == NUMA_NO_NODE) node = numa_node_id(); else node = tfm->node; memset(ctx, 0, sizeof(*ctx)); inst = qat_compression_get_instance_node(node); if (!inst) return -EINVAL; ctx->inst = inst; ctx->inst->build_deflate_ctx(ctx->comp_ctx); return 0; } static void qat_comp_alg_exit_tfm(struct crypto_acomp *acomp_tfm) { struct crypto_tfm *tfm = crypto_acomp_tfm(acomp_tfm); struct qat_compression_ctx *ctx = crypto_tfm_ctx(tfm); qat_compression_put_instance(ctx->inst); memset(ctx, 0, sizeof(*ctx)); } static int qat_comp_alg_compress_decompress(struct acomp_req *areq, enum direction dir, unsigned int shdr, unsigned int sftr, unsigned int dhdr, unsigned int dftr) { struct qat_compression_req *qat_req = acomp_request_ctx(areq); struct crypto_acomp *acomp_tfm = crypto_acomp_reqtfm(areq); struct crypto_tfm *tfm = crypto_acomp_tfm(acomp_tfm); struct qat_compression_ctx *ctx = crypto_tfm_ctx(tfm); struct qat_compression_instance *inst = ctx->inst; gfp_t f = qat_algs_alloc_flags(&areq->base); struct qat_sgl_to_bufl_params params = {0}; int slen = areq->slen - shdr - sftr; int dlen = areq->dlen - dhdr - dftr; dma_addr_t sfbuf, dfbuf; u8 *req = qat_req->req; size_t ovf_buff_sz; int ret; params.sskip = shdr; params.dskip = dhdr; if (!areq->src || !slen) return -EINVAL; if (areq->dst && !dlen) return -EINVAL; qat_req->dst.is_null = false; /* Handle acomp requests that require the allocation of a destination * buffer. The size of the destination buffer is double the source * buffer (rounded up to the size of a page) to fit the decompressed * output or an expansion on the data for compression. */ if (!areq->dst) { qat_req->dst.is_null = true; dlen = round_up(2 * slen, PAGE_SIZE); areq->dst = sgl_alloc(dlen, f, NULL); if (!areq->dst) return -ENOMEM; dlen -= dhdr + dftr; areq->dlen = dlen; qat_req->dst.resubmitted = false; } if (dir == COMPRESSION) { params.extra_dst_buff = inst->dc_data->ovf_buff_p; ovf_buff_sz = inst->dc_data->ovf_buff_sz; params.sz_extra_dst_buff = ovf_buff_sz; } ret = qat_bl_sgl_to_bufl(ctx->inst->accel_dev, areq->src, areq->dst, &qat_req->buf, ¶ms, f); if (unlikely(ret)) return ret; sfbuf = qat_req->buf.blp; dfbuf = qat_req->buf.bloutp; qat_req->qat_compression_ctx = ctx; qat_req->acompress_req = areq; qat_req->dir = dir; if (dir == COMPRESSION) { qat_req->actual_dlen = dlen; dlen += ovf_buff_sz; qat_comp_create_compression_req(ctx->comp_ctx, req, (u64)(__force long)sfbuf, slen, (u64)(__force long)dfbuf, dlen, (u64)(__force long)qat_req); } else { qat_comp_create_decompression_req(ctx->comp_ctx, req, (u64)(__force long)sfbuf, slen, (u64)(__force long)dfbuf, dlen, (u64)(__force long)qat_req); } ret = qat_alg_send_dc_message(qat_req, inst, &areq->base); if (ret == -ENOSPC) qat_bl_free_bufl(inst->accel_dev, &qat_req->buf); return ret; } static int qat_comp_alg_compress(struct acomp_req *req) { return qat_comp_alg_compress_decompress(req, COMPRESSION, 0, 0, 0, 0); } static int qat_comp_alg_decompress(struct acomp_req *req) { return qat_comp_alg_compress_decompress(req, DECOMPRESSION, 0, 0, 0, 0); } static struct acomp_alg qat_acomp[] = { { .base = { .cra_name = "deflate", .cra_driver_name = "qat_deflate", .cra_priority = 4001, .cra_flags = CRYPTO_ALG_ASYNC | CRYPTO_ALG_ALLOCATES_MEMORY, .cra_ctxsize = sizeof(struct qat_compression_ctx), .cra_module = THIS_MODULE, }, .init = qat_comp_alg_init_tfm, .exit = qat_comp_alg_exit_tfm, .compress = qat_comp_alg_compress, .decompress = qat_comp_alg_decompress, .dst_free = sgl_free, .reqsize = sizeof(struct qat_compression_req), }}; int qat_comp_algs_register(void) { int ret = 0; mutex_lock(&algs_lock); if (++active_devs == 1) ret = crypto_register_acomps(qat_acomp, ARRAY_SIZE(qat_acomp)); mutex_unlock(&algs_lock); return ret; } void qat_comp_algs_unregister(void) { mutex_lock(&algs_lock); if (--active_devs == 0) crypto_unregister_acomps(qat_acomp, ARRAY_SIZE(qat_acomp)); mutex_unlock(&algs_lock); }