Path: blob/main/sys/contrib/openzfs/module/os/linux/zfs/qat_crypt.c
48774 views
// SPDX-License-Identifier: CDDL-1.01/*2* CDDL HEADER START3*4* The contents of this file are subject to the terms of the5* Common Development and Distribution License (the "License").6* You may not use this file except in compliance with the License.7*8* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE9* or https://opensource.org/licenses/CDDL-1.0.10* See the License for the specific language governing permissions11* and limitations under the License.12*13* When distributing Covered Code, include this CDDL HEADER in each14* file and include the License file at usr/src/OPENSOLARIS.LICENSE.15* If applicable, add the following below this CDDL HEADER, with the16* fields enclosed by brackets "[]" replaced with your own identifying17* information: Portions Copyright [yyyy] [name of copyright owner]18*19* CDDL HEADER END20*/2122/*23* This file represents the QAT implementation of checksums and encryption.24* Internally, QAT shares the same cryptographic instances for both of these25* operations, so the code has been combined here. QAT data compression uses26* compression instances, so that code is separated into qat_compress.c27*/2829#if defined(_KERNEL) && defined(HAVE_QAT)30#include <linux/slab.h>31#include <linux/vmalloc.h>32#include <linux/pagemap.h>33#include <linux/completion.h>34#include <sys/zfs_context.h>35#include <sys/zio_crypt.h>36#include "lac/cpa_cy_im.h"37#include "lac/cpa_cy_common.h"38#include <sys/qat.h>3940/*41* Max instances in a QAT device, each instance is a channel to submit42* jobs to QAT hardware, this is only for pre-allocating instances43* and session arrays; the actual number of instances are defined in44* the QAT driver's configure file.45*/46#define QAT_CRYPT_MAX_INSTANCES 484748#define MAX_PAGE_NUM 10244950static Cpa32U inst_num = 0;51static Cpa16U num_inst = 0;52static CpaInstanceHandle cy_inst_handles[QAT_CRYPT_MAX_INSTANCES];53static boolean_t qat_cy_init_done = B_FALSE;54int zfs_qat_encrypt_disable = 0;55int zfs_qat_checksum_disable = 0;5657typedef struct cy_callback {58CpaBoolean verify_result;59struct completion complete;60} cy_callback_t;6162static void63symcallback(void *p_callback, CpaStatus status, const CpaCySymOp operation,64void *op_data, CpaBufferList *buf_list_dst, CpaBoolean verify)65{66cy_callback_t *cb = p_callback;6768if (cb != NULL) {69/* indicate that the function has been called */70cb->verify_result = verify;71complete(&cb->complete);72}73}7475boolean_t76qat_crypt_use_accel(size_t s_len)77{78return (!zfs_qat_encrypt_disable &&79qat_cy_init_done &&80s_len >= QAT_MIN_BUF_SIZE &&81s_len <= QAT_MAX_BUF_SIZE);82}8384boolean_t85qat_checksum_use_accel(size_t s_len)86{87return (!zfs_qat_checksum_disable &&88qat_cy_init_done &&89s_len >= QAT_MIN_BUF_SIZE &&90s_len <= QAT_MAX_BUF_SIZE);91}9293void94qat_cy_clean(void)95{96for (Cpa16U i = 0; i < num_inst; i++)97cpaCyStopInstance(cy_inst_handles[i]);9899num_inst = 0;100qat_cy_init_done = B_FALSE;101}102103int104qat_cy_init(void)105{106CpaStatus status = CPA_STATUS_FAIL;107108if (qat_cy_init_done)109return (0);110111status = cpaCyGetNumInstances(&num_inst);112if (status != CPA_STATUS_SUCCESS)113return (-1);114115/* if the user has configured no QAT encryption units just return */116if (num_inst == 0)117return (0);118119if (num_inst > QAT_CRYPT_MAX_INSTANCES)120num_inst = QAT_CRYPT_MAX_INSTANCES;121122status = cpaCyGetInstances(num_inst, &cy_inst_handles[0]);123if (status != CPA_STATUS_SUCCESS)124return (-1);125126for (Cpa16U i = 0; i < num_inst; i++) {127status = cpaCySetAddressTranslation(cy_inst_handles[i],128(void *)virt_to_phys);129if (status != CPA_STATUS_SUCCESS)130goto error;131132status = cpaCyStartInstance(cy_inst_handles[i]);133if (status != CPA_STATUS_SUCCESS)134goto error;135}136137qat_cy_init_done = B_TRUE;138return (0);139140error:141qat_cy_clean();142return (-1);143}144145void146qat_cy_fini(void)147{148if (!qat_cy_init_done)149return;150151qat_cy_clean();152}153154static CpaStatus155qat_init_crypt_session_ctx(qat_encrypt_dir_t dir, CpaInstanceHandle inst_handle,156CpaCySymSessionCtx **cy_session_ctx, crypto_key_t *key,157Cpa64U crypt, Cpa32U aad_len)158{159CpaStatus status = CPA_STATUS_SUCCESS;160Cpa32U ctx_size;161Cpa32U ciper_algorithm;162Cpa32U hash_algorithm;163CpaCySymSessionSetupData sd = { 0 };164165if (zio_crypt_table[crypt].ci_crypt_type == ZC_TYPE_CCM) {166return (CPA_STATUS_FAIL);167} else {168ciper_algorithm = CPA_CY_SYM_CIPHER_AES_GCM;169hash_algorithm = CPA_CY_SYM_HASH_AES_GCM;170}171172sd.cipherSetupData.cipherAlgorithm = ciper_algorithm;173sd.cipherSetupData.pCipherKey = key->ck_data;174sd.cipherSetupData.cipherKeyLenInBytes = key->ck_length / 8;175sd.hashSetupData.hashAlgorithm = hash_algorithm;176sd.hashSetupData.hashMode = CPA_CY_SYM_HASH_MODE_AUTH;177sd.hashSetupData.digestResultLenInBytes = ZIO_DATA_MAC_LEN;178sd.hashSetupData.authModeSetupData.aadLenInBytes = aad_len;179sd.sessionPriority = CPA_CY_PRIORITY_NORMAL;180sd.symOperation = CPA_CY_SYM_OP_ALGORITHM_CHAINING;181sd.digestIsAppended = CPA_FALSE;182sd.verifyDigest = CPA_FALSE;183184if (dir == QAT_ENCRYPT) {185sd.cipherSetupData.cipherDirection =186CPA_CY_SYM_CIPHER_DIRECTION_ENCRYPT;187sd.algChainOrder =188CPA_CY_SYM_ALG_CHAIN_ORDER_HASH_THEN_CIPHER;189} else {190ASSERT3U(dir, ==, QAT_DECRYPT);191sd.cipherSetupData.cipherDirection =192CPA_CY_SYM_CIPHER_DIRECTION_DECRYPT;193sd.algChainOrder =194CPA_CY_SYM_ALG_CHAIN_ORDER_CIPHER_THEN_HASH;195}196197status = cpaCySymSessionCtxGetSize(inst_handle, &sd, &ctx_size);198if (status != CPA_STATUS_SUCCESS)199return (status);200201status = QAT_PHYS_CONTIG_ALLOC(cy_session_ctx, ctx_size);202if (status != CPA_STATUS_SUCCESS)203return (status);204205status = cpaCySymInitSession(inst_handle, symcallback, &sd,206*cy_session_ctx);207if (status != CPA_STATUS_SUCCESS) {208QAT_PHYS_CONTIG_FREE(*cy_session_ctx);209return (status);210}211212return (CPA_STATUS_SUCCESS);213}214215static CpaStatus216qat_init_checksum_session_ctx(CpaInstanceHandle inst_handle,217CpaCySymSessionCtx **cy_session_ctx, Cpa64U cksum)218{219CpaStatus status = CPA_STATUS_SUCCESS;220Cpa32U ctx_size;221Cpa32U hash_algorithm;222CpaCySymSessionSetupData sd = { 0 };223224/*225* ZFS's SHA512 checksum is actually SHA512/256, which uses226* a different IV from standard SHA512. QAT does not support227* SHA512/256, so we can only support SHA256.228*/229if (cksum == ZIO_CHECKSUM_SHA256)230hash_algorithm = CPA_CY_SYM_HASH_SHA256;231else232return (CPA_STATUS_FAIL);233234sd.sessionPriority = CPA_CY_PRIORITY_NORMAL;235sd.symOperation = CPA_CY_SYM_OP_HASH;236sd.hashSetupData.hashAlgorithm = hash_algorithm;237sd.hashSetupData.hashMode = CPA_CY_SYM_HASH_MODE_PLAIN;238sd.hashSetupData.digestResultLenInBytes = sizeof (zio_cksum_t);239sd.digestIsAppended = CPA_FALSE;240sd.verifyDigest = CPA_FALSE;241242status = cpaCySymSessionCtxGetSize(inst_handle, &sd, &ctx_size);243if (status != CPA_STATUS_SUCCESS)244return (status);245246status = QAT_PHYS_CONTIG_ALLOC(cy_session_ctx, ctx_size);247if (status != CPA_STATUS_SUCCESS)248return (status);249250status = cpaCySymInitSession(inst_handle, symcallback, &sd,251*cy_session_ctx);252if (status != CPA_STATUS_SUCCESS) {253QAT_PHYS_CONTIG_FREE(*cy_session_ctx);254return (status);255}256257return (CPA_STATUS_SUCCESS);258}259260static CpaStatus261qat_init_cy_buffer_lists(CpaInstanceHandle inst_handle, uint32_t nr_bufs,262CpaBufferList *src, CpaBufferList *dst)263{264CpaStatus status = CPA_STATUS_SUCCESS;265Cpa32U meta_size = 0;266267status = cpaCyBufferListGetMetaSize(inst_handle, nr_bufs, &meta_size);268if (status != CPA_STATUS_SUCCESS)269return (status);270271status = QAT_PHYS_CONTIG_ALLOC(&src->pPrivateMetaData, meta_size);272if (status != CPA_STATUS_SUCCESS)273goto error;274275if (src != dst) {276status = QAT_PHYS_CONTIG_ALLOC(&dst->pPrivateMetaData,277meta_size);278if (status != CPA_STATUS_SUCCESS)279goto error;280}281282return (CPA_STATUS_SUCCESS);283284error:285QAT_PHYS_CONTIG_FREE(src->pPrivateMetaData);286if (src != dst)287QAT_PHYS_CONTIG_FREE(dst->pPrivateMetaData);288289return (status);290}291292int293qat_crypt(qat_encrypt_dir_t dir, uint8_t *src_buf, uint8_t *dst_buf,294uint8_t *aad_buf, uint32_t aad_len, uint8_t *iv_buf, uint8_t *digest_buf,295crypto_key_t *key, uint64_t crypt, uint32_t enc_len)296{297CpaStatus status = CPA_STATUS_SUCCESS;298Cpa16U i;299CpaInstanceHandle cy_inst_handle;300Cpa16U nr_bufs = (enc_len >> PAGE_SHIFT) + 2;301Cpa32U bytes_left = 0;302Cpa8S *data = NULL;303CpaCySymSessionCtx *cy_session_ctx = NULL;304cy_callback_t cb;305CpaCySymOpData op_data = { 0 };306CpaBufferList src_buffer_list = { 0 };307CpaBufferList dst_buffer_list = { 0 };308CpaFlatBuffer *flat_src_buf_array = NULL;309CpaFlatBuffer *flat_src_buf = NULL;310CpaFlatBuffer *flat_dst_buf_array = NULL;311CpaFlatBuffer *flat_dst_buf = NULL;312struct page *in_pages[MAX_PAGE_NUM];313struct page *out_pages[MAX_PAGE_NUM];314Cpa32U in_page_num = 0;315Cpa32U out_page_num = 0;316Cpa32U in_page_off = 0;317Cpa32U out_page_off = 0;318319if (dir == QAT_ENCRYPT) {320QAT_STAT_BUMP(encrypt_requests);321QAT_STAT_INCR(encrypt_total_in_bytes, enc_len);322} else {323QAT_STAT_BUMP(decrypt_requests);324QAT_STAT_INCR(decrypt_total_in_bytes, enc_len);325}326327i = (Cpa32U)atomic_inc_32_nv(&inst_num) % num_inst;328cy_inst_handle = cy_inst_handles[i];329330status = qat_init_crypt_session_ctx(dir, cy_inst_handle,331&cy_session_ctx, key, crypt, aad_len);332if (status != CPA_STATUS_SUCCESS) {333/* don't count CCM as a failure since it's not supported */334if (zio_crypt_table[crypt].ci_crypt_type == ZC_TYPE_GCM)335QAT_STAT_BUMP(crypt_fails);336return (status);337}338339/*340* We increment nr_bufs by 2 to allow us to handle non341* page-aligned buffer addresses and buffers whose sizes342* are not divisible by PAGE_SIZE.343*/344status = qat_init_cy_buffer_lists(cy_inst_handle, nr_bufs,345&src_buffer_list, &dst_buffer_list);346if (status != CPA_STATUS_SUCCESS)347goto fail;348349status = QAT_PHYS_CONTIG_ALLOC(&flat_src_buf_array,350nr_bufs * sizeof (CpaFlatBuffer));351if (status != CPA_STATUS_SUCCESS)352goto fail;353status = QAT_PHYS_CONTIG_ALLOC(&flat_dst_buf_array,354nr_bufs * sizeof (CpaFlatBuffer));355if (status != CPA_STATUS_SUCCESS)356goto fail;357status = QAT_PHYS_CONTIG_ALLOC(&op_data.pDigestResult,358ZIO_DATA_MAC_LEN);359if (status != CPA_STATUS_SUCCESS)360goto fail;361status = QAT_PHYS_CONTIG_ALLOC(&op_data.pIv,362ZIO_DATA_IV_LEN);363if (status != CPA_STATUS_SUCCESS)364goto fail;365if (aad_len > 0) {366status = QAT_PHYS_CONTIG_ALLOC(&op_data.pAdditionalAuthData,367aad_len);368if (status != CPA_STATUS_SUCCESS)369goto fail;370memcpy(op_data.pAdditionalAuthData, aad_buf, aad_len);371}372373bytes_left = enc_len;374data = src_buf;375flat_src_buf = flat_src_buf_array;376while (bytes_left > 0) {377in_page_off = ((long)data & ~PAGE_MASK);378in_pages[in_page_num] = qat_mem_to_page(data);379flat_src_buf->pData = kmap(in_pages[in_page_num]) + in_page_off;380flat_src_buf->dataLenInBytes =381min((long)PAGE_SIZE - in_page_off, (long)bytes_left);382data += flat_src_buf->dataLenInBytes;383bytes_left -= flat_src_buf->dataLenInBytes;384flat_src_buf++;385in_page_num++;386}387src_buffer_list.pBuffers = flat_src_buf_array;388src_buffer_list.numBuffers = in_page_num;389390bytes_left = enc_len;391data = dst_buf;392flat_dst_buf = flat_dst_buf_array;393while (bytes_left > 0) {394out_page_off = ((long)data & ~PAGE_MASK);395out_pages[out_page_num] = qat_mem_to_page(data);396flat_dst_buf->pData = kmap(out_pages[out_page_num]) +397out_page_off;398flat_dst_buf->dataLenInBytes =399min((long)PAGE_SIZE - out_page_off, (long)bytes_left);400data += flat_dst_buf->dataLenInBytes;401bytes_left -= flat_dst_buf->dataLenInBytes;402flat_dst_buf++;403out_page_num++;404}405dst_buffer_list.pBuffers = flat_dst_buf_array;406dst_buffer_list.numBuffers = out_page_num;407408op_data.sessionCtx = cy_session_ctx;409op_data.packetType = CPA_CY_SYM_PACKET_TYPE_FULL;410op_data.cryptoStartSrcOffsetInBytes = 0;411op_data.messageLenToCipherInBytes = 0;412op_data.hashStartSrcOffsetInBytes = 0;413op_data.messageLenToHashInBytes = 0;414op_data.messageLenToCipherInBytes = enc_len;415op_data.ivLenInBytes = ZIO_DATA_IV_LEN;416memcpy(op_data.pIv, iv_buf, ZIO_DATA_IV_LEN);417/* if dir is QAT_DECRYPT, copy digest_buf to pDigestResult */418if (dir == QAT_DECRYPT)419memcpy(op_data.pDigestResult, digest_buf, ZIO_DATA_MAC_LEN);420421cb.verify_result = CPA_FALSE;422init_completion(&cb.complete);423status = cpaCySymPerformOp(cy_inst_handle, &cb, &op_data,424&src_buffer_list, &dst_buffer_list, NULL);425if (status != CPA_STATUS_SUCCESS)426goto fail;427428/* we now wait until the completion of the operation. */429wait_for_completion(&cb.complete);430431if (cb.verify_result == CPA_FALSE) {432status = CPA_STATUS_FAIL;433goto fail;434}435436if (dir == QAT_ENCRYPT) {437/* if dir is QAT_ENCRYPT, save pDigestResult to digest_buf */438memcpy(digest_buf, op_data.pDigestResult, ZIO_DATA_MAC_LEN);439QAT_STAT_INCR(encrypt_total_out_bytes, enc_len);440} else {441QAT_STAT_INCR(decrypt_total_out_bytes, enc_len);442}443444fail:445if (status != CPA_STATUS_SUCCESS)446QAT_STAT_BUMP(crypt_fails);447448for (i = 0; i < in_page_num; i++)449kunmap(in_pages[i]);450for (i = 0; i < out_page_num; i++)451kunmap(out_pages[i]);452453cpaCySymRemoveSession(cy_inst_handle, cy_session_ctx);454if (aad_len > 0)455QAT_PHYS_CONTIG_FREE(op_data.pAdditionalAuthData);456QAT_PHYS_CONTIG_FREE(op_data.pIv);457QAT_PHYS_CONTIG_FREE(op_data.pDigestResult);458QAT_PHYS_CONTIG_FREE(src_buffer_list.pPrivateMetaData);459QAT_PHYS_CONTIG_FREE(dst_buffer_list.pPrivateMetaData);460QAT_PHYS_CONTIG_FREE(cy_session_ctx);461QAT_PHYS_CONTIG_FREE(flat_src_buf_array);462QAT_PHYS_CONTIG_FREE(flat_dst_buf_array);463464return (status);465}466467int468qat_checksum(uint64_t cksum, uint8_t *buf, uint64_t size, zio_cksum_t *zcp)469{470CpaStatus status;471Cpa16U i;472CpaInstanceHandle cy_inst_handle;473Cpa16U nr_bufs = (size >> PAGE_SHIFT) + 2;474Cpa32U bytes_left = 0;475Cpa8S *data = NULL;476CpaCySymSessionCtx *cy_session_ctx = NULL;477cy_callback_t cb;478Cpa8U *digest_buffer = NULL;479CpaCySymOpData op_data = { 0 };480CpaBufferList src_buffer_list = { 0 };481CpaFlatBuffer *flat_src_buf_array = NULL;482CpaFlatBuffer *flat_src_buf = NULL;483struct page *in_pages[MAX_PAGE_NUM];484Cpa32U page_num = 0;485Cpa32U page_off = 0;486487QAT_STAT_BUMP(cksum_requests);488QAT_STAT_INCR(cksum_total_in_bytes, size);489490i = (Cpa32U)atomic_inc_32_nv(&inst_num) % num_inst;491cy_inst_handle = cy_inst_handles[i];492493status = qat_init_checksum_session_ctx(cy_inst_handle,494&cy_session_ctx, cksum);495if (status != CPA_STATUS_SUCCESS) {496/* don't count unsupported checksums as a failure */497if (cksum == ZIO_CHECKSUM_SHA256 ||498cksum == ZIO_CHECKSUM_SHA512)499QAT_STAT_BUMP(cksum_fails);500return (status);501}502503/*504* We increment nr_bufs by 2 to allow us to handle non505* page-aligned buffer addresses and buffers whose sizes506* are not divisible by PAGE_SIZE.507*/508status = qat_init_cy_buffer_lists(cy_inst_handle, nr_bufs,509&src_buffer_list, &src_buffer_list);510if (status != CPA_STATUS_SUCCESS)511goto fail;512513status = QAT_PHYS_CONTIG_ALLOC(&flat_src_buf_array,514nr_bufs * sizeof (CpaFlatBuffer));515if (status != CPA_STATUS_SUCCESS)516goto fail;517status = QAT_PHYS_CONTIG_ALLOC(&digest_buffer,518sizeof (zio_cksum_t));519if (status != CPA_STATUS_SUCCESS)520goto fail;521522bytes_left = size;523data = buf;524flat_src_buf = flat_src_buf_array;525while (bytes_left > 0) {526page_off = ((long)data & ~PAGE_MASK);527in_pages[page_num] = qat_mem_to_page(data);528flat_src_buf->pData = kmap(in_pages[page_num]) + page_off;529flat_src_buf->dataLenInBytes =530min((long)PAGE_SIZE - page_off, (long)bytes_left);531data += flat_src_buf->dataLenInBytes;532bytes_left -= flat_src_buf->dataLenInBytes;533flat_src_buf++;534page_num++;535}536src_buffer_list.pBuffers = flat_src_buf_array;537src_buffer_list.numBuffers = page_num;538539op_data.sessionCtx = cy_session_ctx;540op_data.packetType = CPA_CY_SYM_PACKET_TYPE_FULL;541op_data.hashStartSrcOffsetInBytes = 0;542op_data.messageLenToHashInBytes = size;543op_data.pDigestResult = digest_buffer;544545cb.verify_result = CPA_FALSE;546init_completion(&cb.complete);547status = cpaCySymPerformOp(cy_inst_handle, &cb, &op_data,548&src_buffer_list, &src_buffer_list, NULL);549if (status != CPA_STATUS_SUCCESS)550goto fail;551552/* we now wait until the completion of the operation. */553wait_for_completion(&cb.complete);554555if (cb.verify_result == CPA_FALSE) {556status = CPA_STATUS_FAIL;557goto fail;558}559560memcpy(zcp, digest_buffer, sizeof (zio_cksum_t));561562fail:563if (status != CPA_STATUS_SUCCESS)564QAT_STAT_BUMP(cksum_fails);565566for (i = 0; i < page_num; i++)567kunmap(in_pages[i]);568569cpaCySymRemoveSession(cy_inst_handle, cy_session_ctx);570QAT_PHYS_CONTIG_FREE(digest_buffer);571QAT_PHYS_CONTIG_FREE(src_buffer_list.pPrivateMetaData);572QAT_PHYS_CONTIG_FREE(cy_session_ctx);573QAT_PHYS_CONTIG_FREE(flat_src_buf_array);574575return (status);576}577578static int579param_set_qat_encrypt(const char *val, zfs_kernel_param_t *kp)580{581int ret;582int *pvalue = kp->arg;583ret = param_set_int(val, kp);584if (ret)585return (ret);586/*587* zfs_qat_encrypt_disable = 0: enable qat encrypt588* try to initialize qat instance if it has not been done589*/590if (*pvalue == 0 && !qat_cy_init_done) {591ret = qat_cy_init();592if (ret != 0) {593zfs_qat_encrypt_disable = 1;594return (ret);595}596}597return (ret);598}599600static int601param_set_qat_checksum(const char *val, zfs_kernel_param_t *kp)602{603int ret;604int *pvalue = kp->arg;605ret = param_set_int(val, kp);606if (ret)607return (ret);608/*609* set_checksum_param_ops = 0: enable qat checksum610* try to initialize qat instance if it has not been done611*/612if (*pvalue == 0 && !qat_cy_init_done) {613ret = qat_cy_init();614if (ret != 0) {615zfs_qat_checksum_disable = 1;616return (ret);617}618}619return (ret);620}621622module_param_call(zfs_qat_encrypt_disable, param_set_qat_encrypt,623param_get_int, &zfs_qat_encrypt_disable, 0644);624MODULE_PARM_DESC(zfs_qat_encrypt_disable, "Enable/Disable QAT encryption");625626module_param_call(zfs_qat_checksum_disable, param_set_qat_checksum,627param_get_int, &zfs_qat_checksum_disable, 0644);628MODULE_PARM_DESC(zfs_qat_checksum_disable, "Enable/Disable QAT checksumming");629630#endif631632633