
/***************************************************************************
 *
 *   SPDX-License-Identifier: BSD-3-Clause
 *   Copyright(c) 2007-2026 Intel Corporation
 * 
 *   These contents may have been developed with support from one or more
 *   Intel-operated generative artificial intelligence solutions.
 *
 ***************************************************************************/

#include "cpa_dc.h"
#include "cpa_sample_code_crypto_utils.h"
#include "qat_compression_main.h"
#include "icp_sal_poll.h"
#include "qat_perf_latency.h"
#include "qat_sym_utils.h"

extern volatile CpaBoolean reliability_g;
extern Cpa32U packageIdCount_g;
extern volatile CpaBoolean digestAppended_g;
extern CpaStatus createStartandWaitForCompletion(Cpa32U instType);

extern void symPerformCallback(void *pCallbackTag,
                               CpaStatus status,
                               const CpaCySymOp operationType,
                               void *pOpData,
                               CpaBufferList *pDstBuffer,
                               CpaBoolean verifyResult);
CpaStatus cipherNewPerformanceTest(CpaBoolean mode,
                                   Cpa32U mask,
                                   Cpa32U bufferSize,
                                   Cpa32U numBuffers,
                                   Cpa32U numLoops);
void qatSymmetricPerformance(single_thread_test_data_t *testSetup);

#if defined(USER_SPACE) && defined(SUPPORTED_FEAT_EPOLL) &&                    \
    defined(STV_TEST_CODE)
static CpaStatus qatsymDataWithIteration(symmetric_test_params_t *setup,
                                         CpaCySymOpData *encryptOpData,
                                         CpaCySymOpData *decryptOpData,
                                         CpaBufferList *srcBufferListArray)
{
    CpaStatus status = CPA_STATUS_SUCCESS;
    Cpa32U iterationCount;
    Cpa32U i;
    Cpa32U numLoops;

    CpaInstanceResponseMode originalMode;
    CpaInstanceResponseMode currentMode;
    CpaInstanceResponseMode alternateMode;

    /* Get iteration count */
    status = getCyResponseModeIterationCount(&iterationCount);
    if (CPA_STATUS_SUCCESS != status)
    {
        PRINT_ERR("Failed to get Cy response mode iteration count\n");
        return status;
    }

    /* Store original response mode */
    originalMode = setup->currentResponseMode;
    /* Determine alternate mode */
    if (originalMode == CPA_INST_RX_NOTIFY_NONE)
    {
        alternateMode = CPA_INST_RX_NOTIFY_BY_EVENT;
    }
    else
    {
        alternateMode = CPA_INST_RX_NOTIFY_NONE;
    }
    PRINT("Starting response mode iteration: count=%d, initial=%d, "
          "alternate=%d\n",
          iterationCount,
          originalMode,
          alternateMode);
    /* Perform iterations with alternating response modes */
    for (i = 0; i < iterationCount && status == CPA_STATUS_SUCCESS; i++)
    {
        if (i % 2 == 0)
        {
            /* Even iterations: use original mode */
            currentMode = originalMode;
        }
        else
        {
            /* Odd iterations: use alternate mode */
            currentMode = alternateMode;
        }
        /* Update the test parameters with current mode */
        setup->currentResponseMode = currentMode;
        /* Get instance type to determine correct service type */
        CpaInstanceInfo2 instInfo = { 0 };
        CpaStatus infoStatus =
            cpaCyInstanceGetInfo2(setup->cyInstanceHandle, &instInfo);
        if (CPA_STATUS_SUCCESS != infoStatus)
        {
            PRINT_ERR("Failed to get instance info on iteration %d\n", i);
            status = infoStatus;
            break;
        }
        CpaStatus modeStatus =
            cpaInstanceSetResponseMode(setup->cyInstanceHandle,
                                       instInfo.accelerationServiceType,
                                       currentMode);
        if (CPA_STATUS_SUCCESS != modeStatus)
        {
            PRINT_ERR(
                "Failed to set response mode %d on iteration %d, status: %d\n",
                currentMode,
                i,
                modeStatus);
            /* Continue with existing mode rather than failing completely */
        }
        if (setup->setupData.cipherSetupData.cipherDirection ==
                CPA_CY_SYM_CIPHER_DIRECTION_ENCRYPT &&
            reliability_g == CPA_FALSE)
        {
            status = qatSymPerform(setup, encryptOpData, srcBufferListArray);
            if (CPA_STATUS_SUCCESS != status)
            {
                PRINT_ERR(
                    "qatSymPerform for encryptOpData returned status %d\n",
                    status);
            }
        }
        else if (setup->setupData.cipherSetupData.cipherDirection ==
                     CPA_CY_SYM_CIPHER_DIRECTION_DECRYPT &&
                 reliability_g == CPA_FALSE)
        {
            /*copy numLoops, set setup->numLoops to 1 to compress data, then
             * restore setup->numLoops to measure decompress performance*/
            numLoops = setup->numLoops;
            setup->numLoops = 1;

            status = qatSymPerform(setup, encryptOpData, srcBufferListArray);
            if (CPA_STATUS_SUCCESS == status)
            {
                /*restore setup->NumLoops*/
                setup->numLoops = numLoops;
                status =
                    qatSymPerform(setup, decryptOpData, srcBufferListArray);
                if (CPA_STATUS_SUCCESS != status)
                {
                    PRINT_ERR(
                        "qatSymPerform for decryptOpData returned status %d\n",
                        status);
                }
            }
        }
        else if (setup->setupData.cipherSetupData.cipherDirection ==
                     CPA_CY_SYM_CIPHER_DIRECTION_ENCRYPT &&
                 reliability_g == CPA_TRUE)
        {
            /*copy numLoops, set setup->numLoops to 1 to do repeated
             * compress-swdecompress for numLoops times*/
            numLoops = setup->numLoops;
            setup->numLoops = 1;
            for (i = 0; i < numLoops; i++)
            {
                status =
                    qatSymPerform(setup, encryptOpData, srcBufferListArray);

                // now i need to decrypt
                if (CPA_STATUS_SUCCESS == status)
                {
                    status =
                        qatSymPerform(setup, decryptOpData, srcBufferListArray);

                    if (CPA_STATUS_SUCCESS == status)
                    {
                        /*3rd param is compression setup struct, need to
                        resolve status = qatCmpBuffers(srcBufferListArray,
                            copyBufferListArray,
                            setup);*/
                    }
                }
                if (CPA_STATUS_SUCCESS != status)
                {
                    break;
                }
            }
        }
    }
    /* Restore original mode on both instance and test parameters */
    setup->currentResponseMode = originalMode;

    CpaStatus restoreStatus = cpaInstanceSetResponseMode(
        setup->cyInstanceHandle, CPA_ACC_SVC_TYPE_CRYPTO, originalMode);
    if (CPA_STATUS_SUCCESS != restoreStatus)
    {
        PRINT_ERR("Failed to restore original response mode %d after "
                  "iterations, status: %d\n",
                  originalMode,
                  restoreStatus);
        /* Log error but don't fail the overall operation */
    }
    return status;
}

#endif /* USER_SPACE && SUPPORTED_FEAT_EPOLL && STV_TEST_CODE */

/*allocates buffers store a file for compression. The buffers are sent to
 * hardware, performance is recorded and stored in the setup parameter
 * the sample code framework prints out results after the thread completes*/
static CpaStatus scSymPoc(symmetric_test_params_t *setup_sym)
{
    CpaStatus status = CPA_STATUS_SUCCESS;
    Cpa32U *testBufferSize = setup_sym->packetSizeInBytesArray;
    Cpa32U digestSizeInBytes =
        setup_sym->setupData.hashSetupData.digestResultLenInBytes;
    Cpa32U numberOfBuffersPerList = 1;
    CpaBufferList *srcBufferListArray = NULL;
    CpaBufferList *copyBufferListArray = NULL;
    CpaCySymOpData *encryptOpData = NULL;
    CpaCySymOpData *decryptOpData = NULL;
    CpaCySymSessionCtx encryptSessionCtx = NULL;
    CpaCySymSessionCtx decryptSessionCtx = NULL;
    CpaCySymCbFunc cySymCbFn = symPerformCallback;
    Cpa32U numLoops = 0;
    Cpa32U i = 0;

    /* Allocate memory for source & destination bufferLists and results */
    status = qatAllocateSymLists(setup_sym,
                                 &srcBufferListArray,
                                 &copyBufferListArray,
                                 &encryptOpData,
                                 &decryptOpData);

    /* Allocate the CpaFlatBuffers in each list */
    if (CPA_STATUS_SUCCESS == status)
    {
        status = qatAllocateSymFlatBuffers(setup_sym,
                                           srcBufferListArray,
                                           numberOfBuffersPerList,
                                           testBufferSize,
                                           digestSizeInBytes,
                                           copyBufferListArray);

        if (CPA_STATUS_SUCCESS != status)
        {
            PRINT_ERR("could not allocate all flat buffers for symmetric\n");
        }
    }

    /* Copy corpus data into allocated buffers */
    if (CPA_STATUS_SUCCESS == status)
    {
        status = PopulateBuffers(srcBufferListArray,
                                 setup_sym->numBuffLists,
                                 NULL,
                                 0,
                                 testBufferSize);
    }

    if (CPA_STATUS_SUCCESS == status)
    {
        status = copyBuffers(
            srcBufferListArray, copyBufferListArray, setup_sym->numBuffLists);
    }

    /* Initialize the compression session to use */
    if (CPA_STATUS_SUCCESS == status)
    {
        status = qatSymSessionInit(
            setup_sym, &encryptSessionCtx, &decryptSessionCtx, cySymCbFn);

        if (CPA_STATUS_SUCCESS != status)
        {
            PRINT_ERR("qatSymSessionInit returned status %d\n", status);
        }
    }

    /* Initialize encrypt opData */
    if (CPA_STATUS_SUCCESS == status)
    {
        status = qatSymOpDataSetup(setup_sym,
                                   encryptSessionCtx,
                                   /* testBufferSize, */
                                   setup_sym->packetSizeInBytesArray,
                                   encryptOpData,
                                   srcBufferListArray);

        if (CPA_STATUS_SUCCESS != status)
        {
            PRINT_ERR(
                "qatSymPerformOpDataSetup for encrypt returned status %d\n",
                status);
        }
    }

    /* Initialize decrypt opData */
    if (CPA_STATUS_SUCCESS == status)
    {
        status = qatSymOpDataSetup(setup_sym,
                                   decryptSessionCtx,
                                   /* testBufferSize, */
                                   setup_sym->packetSizeInBytesArray,
                                   encryptOpData,
                                   srcBufferListArray);

        if (CPA_STATUS_SUCCESS != status)
        {
            PRINT_ERR(
                "qatSymPerformOpDataSetup for decrypt returned status %d\n",
                status);
        }
    }

    // encrypt or decrypt the data
    if (CPA_STATUS_SUCCESS == status)
    {
#if defined(USER_SPACE) && defined(SUPPORTED_FEAT_EPOLL) &&                    \
    defined(STV_TEST_CODE)
        /* Check if response mode iteration is enabled */
        Cpa32U iterCount = 0;
        status = getCyResponseModeIterationCount(&iterCount);
        PRINT("iterCount = %d\n", iterCount);
        if (CPA_STATUS_SUCCESS != status)
        {
            PRINT_ERR("Failed to get Cy response mode iteration count in "
                      "qatSymPerform\n");
            return status;
        }
        if (iterCount > 1)
        {
            qatsymDataWithIteration(
                setup_sym, encryptOpData, decryptOpData, srcBufferListArray);
        }
        else
        {
            if (setup_sym->setupData.cipherSetupData.cipherDirection ==
                    CPA_CY_SYM_CIPHER_DIRECTION_ENCRYPT &&
                reliability_g == CPA_FALSE)
            {
                status =
                    qatSymPerform(setup_sym, encryptOpData, srcBufferListArray);
                if (CPA_STATUS_SUCCESS != status)
                {
                    PRINT_ERR(
                        "qatSymPerform for encryptOpData returned status %d\n",
                        status);
                }
            }

            else if (setup_sym->setupData.cipherSetupData.cipherDirection ==
                         CPA_CY_SYM_CIPHER_DIRECTION_DECRYPT &&
                     reliability_g == CPA_FALSE)
            {
                /*copy numLoops, set setup->numLoops to 1 to compress data, then
                 * restore setup->numLoops to measure decompress performance*/
                numLoops = setup_sym->numLoops;
                setup_sym->numLoops = 1;

                status =
                    qatSymPerform(setup_sym, encryptOpData, srcBufferListArray);
                if (CPA_STATUS_SUCCESS == status)
                {
                    /*restore setup->NumLoops*/
                    setup_sym->numLoops = numLoops;
                    status = qatSymPerform(
                        setup_sym, decryptOpData, srcBufferListArray);
                    if (CPA_STATUS_SUCCESS != status)
                    {
                        PRINT_ERR("qatSymPerform for decryptOpData returned "
                                  "status %d\n",
                                  status);
                    }
                }
            }

            else if (setup_sym->setupData.cipherSetupData.cipherDirection ==
                         CPA_CY_SYM_CIPHER_DIRECTION_ENCRYPT &&
                     reliability_g == CPA_TRUE)
            {

                /*copy numLoops, set setup->numLoops to 1 to do repeated
                 * compress-swdecompress for numLoops times*/
                numLoops = setup_sym->numLoops;
                setup_sym->numLoops = 1;
                for (i = 0; i < numLoops; i++)
                {

                    status = qatSymPerform(
                        setup_sym, encryptOpData, srcBufferListArray);

                    // now i need to decrypt

                    if (CPA_STATUS_SUCCESS == status)
                    {
                        status = qatSymPerform(
                            setup_sym, decryptOpData, srcBufferListArray);

                        if (CPA_STATUS_SUCCESS == status)
                        {
                            /*3rd param is compression setup struct, need to
                            resolve status = qatCmpBuffers(srcBufferListArray,
                                copyBufferListArray,
                                setup_sym);*/
                        }
                    }
                    if (CPA_STATUS_SUCCESS != status)
                    {
                        break;
                    }
                }
            }
        }
#else // USER_SPACE && SUPPORTED_FEAT_EPOLL not active
        if (setup_sym->setupData.cipherSetupData.cipherDirection ==
                CPA_CY_SYM_CIPHER_DIRECTION_ENCRYPT &&
            reliability_g == CPA_FALSE)
        {
            status =
                qatSymPerform(setup_sym, encryptOpData, srcBufferListArray);
            if (CPA_STATUS_SUCCESS != status)
            {
                PRINT_ERR(
                    "qatSymPerform for encryptOpData returned status %d\n",
                    status);
            }
        }

        else if (setup_sym->setupData.cipherSetupData.cipherDirection ==
                     CPA_CY_SYM_CIPHER_DIRECTION_DECRYPT &&
                 reliability_g == CPA_FALSE)
        {
            /*copy numLoops, set setup->numLoops to 1 to compress data, then
             * restore setup->numLoops to measure decompress performance*/
            numLoops = setup_sym->numLoops;
            setup_sym->numLoops = 1;

            status =
                qatSymPerform(setup_sym, encryptOpData, srcBufferListArray);
            if (CPA_STATUS_SUCCESS == status)
            {
                /*restore setup->NumLoops*/
                setup_sym->numLoops = numLoops;
                status =
                    qatSymPerform(setup_sym, decryptOpData, srcBufferListArray);
                if (CPA_STATUS_SUCCESS != status)
                {
                    PRINT_ERR(
                        "qatSymPerform for decryptOpData returned status %d\n",
                        status);
                }
            }
        }

        else if (setup_sym->setupData.cipherSetupData.cipherDirection ==
                     CPA_CY_SYM_CIPHER_DIRECTION_ENCRYPT &&
                 reliability_g == CPA_TRUE)
        {

            /*copy numLoops, set setup->numLoops to 1 to do repeated
             * compress-swdecompress for numLoops times*/
            PRINT("Starting repeated compress-decompress for %d times\n",
                  setup_sym->numLoops);
            numLoops = setup_sym->numLoops;
            setup_sym->numLoops = 1;
            for (i = 0; i < numLoops; i++)
            {

                status =
                    qatSymPerform(setup_sym, encryptOpData, srcBufferListArray);

                // now i need to decrypt

                if (CPA_STATUS_SUCCESS == status)
                {
                    status = qatSymPerform(
                        setup_sym, decryptOpData, srcBufferListArray);

                    if (CPA_STATUS_SUCCESS == status)
                    {
                        /*3rd param is compression setup struct, need to
                        resolve status = qatCmpBuffers(srcBufferListArray,
                            copyBufferListArray,
                            setup_sym);*/
                    }
                }
                if (CPA_STATUS_SUCCESS != status)
                {
                    break;
                }
            }
        }
#endif /* USER_SPACE && SUPPORTED_FEAT_EPOLL && STV_TEST_CODE */
    }

    // SYMMETRIC
    if (CPA_STATUS_SUCCESS != qatSymFreeOpData(setup_sym, encryptOpData))
    {
        PRINT_ERR("could not free pIv and auth memory in encryptOpData ");
        status = CPA_STATUS_FAIL;
    }

    if (CPA_STATUS_SUCCESS != qatSymFreeOpData(setup_sym, decryptOpData))
    {
        PRINT_ERR("could not free pIv and auth memory in decryptOpData ");
        status = CPA_STATUS_FAIL;
    }

    /* Remove the session free the handle */
    if (CPA_STATUS_SUCCESS != qatSymSessionTeardown(setup_sym,
                                                    &encryptSessionCtx,
                                                    &decryptSessionCtx))
    {
        PRINT_ERR("qatSymSessionTeardown error\n");
        status = CPA_STATUS_FAIL;
    }

    /* Free CpaFlatBuffers and privateMetaData in CpaBufferLists */
    if (CPA_STATUS_SUCCESS != qatFreeSymFlatBuffers(setup_sym,
                                                    srcBufferListArray,
                                                    copyBufferListArray))
    {
        PRINT_ERR("qatFreeSymFlatBuffers error\n");
        status = CPA_STATUS_FAIL;
    }

    /* Free CpaBufferLists and encrypt/decrypt OpData */
    if (CPA_STATUS_SUCCESS != qatFreeSymLists(&srcBufferListArray,
                                              &copyBufferListArray,
                                              &encryptOpData,
                                              &decryptOpData))
    {
        PRINT_ERR("qatFreeSymLists error\n");
        status = CPA_STATUS_FAIL;
    }
    return status;
}

void qatSymmetricPerformance(single_thread_test_data_t *testSetup)
{
    symmetric_test_params_t symTestSetup;
    symmetric_test_params_t *pSetup =
        ((symmetric_test_params_t *)testSetup->setupPtr);
    Cpa32U loopIteration = 0;
    CpaStatus status = CPA_STATUS_SUCCESS;
    /*define the distribution of the packet mix
     * here we defined 2 lots of 10 sizes
     * later it is replicated into 100 buffers*/
    Cpa32U packetMix[NUM_PACKETS_IMIX] = {
        BUFFER_SIZE_64,   BUFFER_SIZE_752,  BUFFER_SIZE_1504, BUFFER_SIZE_64,
        BUFFER_SIZE_752,  BUFFER_SIZE_1504, BUFFER_SIZE_64,   BUFFER_SIZE_64,
        BUFFER_SIZE_1504, BUFFER_SIZE_1504, BUFFER_SIZE_752,  BUFFER_SIZE_64,
        BUFFER_SIZE_752,  BUFFER_SIZE_64,   BUFFER_SIZE_1504, BUFFER_SIZE_1504,
        BUFFER_SIZE_64,   BUFFER_SIZE_8992, BUFFER_SIZE_64,   BUFFER_SIZE_1504};
    Cpa32U *pPacketSize;
    Cpa16U numInstances = 0;
    CpaInstanceHandle *cyInstances = NULL;
    CpaInstanceInfo2 *instanceInfo = NULL;

    memset(&symTestSetup, 0, sizeof(symmetric_test_params_t));

    /*cast the setup to a known structure so that we can populate our local
     * test setup*/
    symTestSetup.setupData = pSetup->setupData;

    if (reliability_g)
    {
        PRINT("\nThread %u, LI %u,  ",
              testSetup->threadID,
              testSetup->logicalQaInstance);
        if (symTestSetup.setupData.symOperation == CPA_CY_SYM_OP_CIPHER)
        {
            PRINT("Cipher ");
            printCipherAlg(symTestSetup.setupData.cipherSetupData);
        }
        else if (symTestSetup.setupData.symOperation == CPA_CY_SYM_OP_HASH)
        {
            PRINT("Hash ");
            printHashAlg(symTestSetup.setupData.hashSetupData);
        }
        else if (symTestSetup.setupData.symOperation ==
                 CPA_CY_SYM_OP_ALGORITHM_CHAINING)
        {
            PRINT("AlgChain ");
            printCipherAlg(symTestSetup.setupData.cipherSetupData);
            PRINT(" ");
            printHashAlg(symTestSetup.setupData.hashSetupData);
        }
        PRINT(" in Reliability mode\n");
    }

    /*this barrier is to halt this thread when run in user space context, the
     * startThreads function releases this barrier, in kernel space it does
     * nothing, but kernel space threads do not start until we call startThreads
     * anyway*/
    startBarrier();
    /*give our thread a unique memory location to store performance stats*/
    symTestSetup.performanceStats = testSetup->performanceStats;
    /*get the instance handles so that we can start our thread on the selected
     * instance*/
    status = cpaCyGetNumInstances(&numInstances);
    if (CPA_STATUS_SUCCESS != status || numInstances == 0)
    {
        PRINT_ERR("cpaCyGetNumInstances error, status:%d, numInstanaces:%d\n",
                  status,
                  numInstances);
        symTestSetup.performanceStats->threadReturnStatus = CPA_STATUS_FAIL;
        sampleCodeThreadExit();
    }
    cyInstances = qaeMemAlloc(sizeof(CpaInstanceHandle) * numInstances);
    if (NULL == cyInstances)
    {
        PRINT_ERR("Error allocating memory for instance handles\n");
        symTestSetup.performanceStats->threadReturnStatus = CPA_STATUS_FAIL;
        sampleCodeThreadExit();
    }
    if (cpaCyGetInstances(numInstances, cyInstances) != CPA_STATUS_SUCCESS)
    {
        PRINT_ERR("Failed to get instances\n");
        symTestSetup.performanceStats->threadReturnStatus = CPA_STATUS_FAIL;
        qaeMemFree((void **)&cyInstances);
        sampleCodeThreadExit();
    }
    if (testSetup->logicalQaInstance > numInstances)
    {
        PRINT_ERR("%u is Invalid Logical QA Instance, max is: %u\n",
                  testSetup->logicalQaInstance,
                  numInstances);
        symTestSetup.performanceStats->threadReturnStatus = CPA_STATUS_FAIL;
        qaeMemFree((void **)&cyInstances);
        sampleCodeThreadExit();
    }

    instanceInfo = qaeMemAlloc(sizeof(CpaInstanceInfo2));
    if (instanceInfo == NULL)
    {
        PRINT_ERR("Failed to allocate memory for instanceInfo");
        symTestSetup.performanceStats->threadReturnStatus = CPA_STATUS_FAIL;
        sampleCodeThreadExit();
        return;
    }
    memset(instanceInfo, 0, sizeof(CpaInstanceInfo2));

    /* give our thread a logical crypto instance to use*/
    symTestSetup.cyInstanceHandle = cyInstances[testSetup->logicalQaInstance];
    status = cpaCyInstanceGetInfo2(symTestSetup.cyInstanceHandle, instanceInfo);
    if (CPA_STATUS_SUCCESS != status)
    {
        PRINT_ERR("%s::%d cpaCyInstanceGetInfo2 failed", __func__, __LINE__);
        qaeMemFree((void **)&cyInstances);
        qaeMemFree((void **)&instanceInfo);
        symTestSetup.performanceStats->threadReturnStatus = CPA_STATUS_FAIL;
        sampleCodeThreadExit();
    }
    if (instanceInfo->physInstId.packageId > packageIdCount_g)
    {
        packageIdCount_g = instanceInfo->physInstId.packageId;
    }

    pPacketSize = qaeMemAlloc(sizeof(Cpa32U) * pSetup->numBuffLists);

    if (NULL == pPacketSize)
    {
        PRINT_ERR("Could not allocate memory for pPacketSize\n");
        symTestSetup.performanceStats->threadReturnStatus = CPA_STATUS_FAIL;
        qaeMemFree((void **)&cyInstances);
        qaeMemFree((void **)&instanceInfo);
        sampleCodeThreadExit();
    }

    if (testSetup->packetSize == PACKET_IMIX)
    {
        /*we are testing IMIX so we copy buffer sizes from preallocated
         * array into symTestSetup.numBuffLists*/
        Cpa32U indexer = sizeof(packetMix) / sizeof(Cpa32U);
        for (loopIteration = 0; loopIteration < pSetup->numBuffLists;
             loopIteration++)
        {
            pPacketSize[loopIteration] = packetMix[loopIteration % indexer];
        }
    }
    else
    {
        /*we are testing a uniform bufferSize, so we set the bufferSize array
         * accordingly*/
        for (loopIteration = 0; loopIteration < pSetup->numBuffLists;
             loopIteration++)
        {
            pPacketSize[loopIteration] = testSetup->packetSize;
        }
    }
    /*initialize digestIsAppended with input parameter */
    symTestSetup.setupData.digestIsAppended = pSetup->digestAppend;

    symTestSetup.numBuffLists = pSetup->numBuffLists;
    symTestSetup.flatBufferSizeInBytes = pSetup->flatBufferSizeInBytes;
    symTestSetup.numLoops = pSetup->numLoops;
    /*reset the stats print function to NULL, we set it to the proper function
     * if the test passes at the end of this function*/
    testSetup->statsPrintFunc = NULL;
    /*assign the array of buffer sizes we are testing to the symmetric test
     * setup*/
    symTestSetup.packetSizeInBytesArray = pPacketSize;
    /*assign our thread a unique memory location to store performance stats*/
    symTestSetup.performanceStats = testSetup->performanceStats;
    symTestSetup.performanceStats->packageId =
        instanceInfo->physInstId.packageId;
    symTestSetup.performanceStats->averagePacketSizeInBytes =
        testSetup->packetSize;
    /* give our thread a logical crypto instance to use*/
    symTestSetup.cyInstanceHandle = cyInstances[testSetup->logicalQaInstance];
    symTestSetup.syncMode = pSetup->syncMode;
    /*store core affinity, this assumes logical cpu core number is the same
     * logicalQaInstace */
    symTestSetup.performanceStats->logicalCoreAffinity =
        testSetup->logicalQaInstance;
    symTestSetup.threadID = testSetup->threadID;
    symTestSetup.isDpApi = pSetup->isDpApi;
    symTestSetup.cryptoSrcOffset = pSetup->cryptoSrcOffset;
    symTestSetup.digestAppend = pSetup->digestAppend;
#if defined(USER_SPACE) && defined(SUPPORTED_FEAT_EPOLL) &&                    \
    defined(STV_TEST_CODE)
    /* Determine the response mode for this instance */
    Cpa16U instanceIndex = (testSetup->logicalQaInstance) % numInstances;
    {
        CpaStatus responseStatus;

        /* First get the actual default response mode from the instance */
        responseStatus =
            cpaInstanceGetResponseMode(symTestSetup.cyInstanceHandle,
                                       CPA_ACC_SVC_TYPE_CRYPTO_SYM,
                                       &symTestSetup.currentResponseMode);
        if (CPA_STATUS_SUCCESS != responseStatus)
        {
            /* Fallback to assumed default if query fails */
            symTestSetup.currentResponseMode = CPA_INST_RX_NOTIFY_NONE;
            PRINT(
                "Failed to query instance response mode, using fallback: %d\n",
                responseStatus);
        }

        /* Check if this instance has been explicitly configured with override
         */
        if (isCyInstanceResponseModeConfigured())
        {
            Cpa64U instanceMask = getCyInstanceResponseModeMask();

            /* Check if this instance is in the configured mask */
            if ((instanceIndex < 64) &&
                (instanceMask & (1ULL << instanceIndex)))
            {
                /* Override with explicitly configured mode */
                symTestSetup.currentResponseMode = getCyInstanceResponseMode();
                PRINT("Using explicit response mode %d for instance %d\n",
                      symTestSetup.currentResponseMode,
                      instanceIndex);
            }
            else
            {
                PRINT(
                    "Using library default response mode %d for instance %d\n",
                    symTestSetup.currentResponseMode,
                    instanceIndex);
            }
        }
    }

    /* Print the final response mode for this thread */
    PRINT("Thread %u using sym instance %u with response mode: %d (%s)\n",
          testSetup->threadID,
          instanceIndex,
          symTestSetup.currentResponseMode,
          (symTestSetup.currentResponseMode == CPA_INST_RX_NOTIFY_NONE)
              ? "polling"
          : (symTestSetup.currentResponseMode == CPA_INST_RX_NOTIFY_BY_EVENT)
              ? "event"
              : "unknown");
#endif /* USER_SPACE && SUPPORTED_FEAT_EPOLL && STV_TEST_CODE */
    /*launch function that does all the work*/

    if (CPA_TRUE != checkCapability(cyInstances[testSetup->logicalQaInstance],
                                    &symTestSetup))
    {
        PRINT("\nThread %u Invalid test\n", testSetup->threadID);
        testSetup->statsPrintFunc =
            (stats_print_func_t)printSymmetricPerfDataAndStopCyService;
        symTestSetup.performanceStats->threadReturnStatus = CPA_STATUS_SUCCESS;
        sampleCodeBarrier();
        qaeMemFree((void **)&pPacketSize);
        qaeMemFree((void **)&cyInstances);
        qaeMemFree((void **)&instanceInfo);
        sampleCodeThreadComplete(testSetup->threadID);
    }

    status = scSymPoc(&symTestSetup);
    if (CPA_STATUS_SUCCESS != status)
    {
        printSymTestType(&symTestSetup);
        PRINT("Test %u FAILED\n", testSetup->threadID);
        symTestSetup.performanceStats->threadReturnStatus = CPA_STATUS_FAIL;
    }
    else
    {
        if (reliability_g)
        {
            PRINT("Symmetric thread %u complete\n", testSetup->threadID);
        }
        /*set the print function that can be used to print stats at the end of
         * the test*/
        testSetup->statsPrintFunc =
            (stats_print_func_t)printSymmetricPerfDataAndStopCyService;
    }
    if ((CPA_STATUS_SUCCESS != status) ||
        (symTestSetup.performanceStats->threadReturnStatus == CPA_STATUS_FAIL))
    {
        /* Stop Cy Service function should be called after all threads
         * complete their execution. This function will be called from
         * WaitForThreadCompletion().*/
        testSetup->statsPrintFunc =
            (stats_print_func_t)stopCyServicesFromCallback;
    }

    /*free memory and exit*/
    qaeMemFree((void **)&pPacketSize);
    qaeMemFree((void **)&cyInstances);
    qaeMemFree((void **)&instanceInfo);
    sampleCodeThreadComplete(testSetup->threadID);
}

static CpaStatus setupNewSymmetricTest(
    CpaCySymOp opType,
    CpaCySymCipherAlgorithm cipherAlg,
    Cpa32U cipherKeyLengthInBytes,
    Cpa32U cipherOffset,
    CpaCyPriority priority,
    CpaCySymHashAlgorithm hashAlg,
    CpaCySymHashMode hashMode,
    Cpa32U authKeyLengthInBytes,
    CpaCySymAlgChainOrder chainOrder,
    sync_mode_t syncMode,
    CpaCySymHashNestedModeSetupData *nestedModeSetupDataPtr,
    Cpa32U packetSize,
    Cpa32U bufferSizeInBytes,
    Cpa32U numBuffLists,
    Cpa32U numLoops,
    Cpa32U digestAppend)
{
    /*thread_setup_g is a multidimensional global array that stores the setup
     * for all thread variations in an array of characters. We store our test
     * setup at the start of the second array ie index 0. There maybe multiple
     * thread types(setups) running as counted by testTypeCount_g*/
    symmetric_test_params_t *symmetricSetup = NULL;
    Cpa8S name[] = {'S', 'Y', 'M', '\0'};

    if (testTypeCount_g >= MAX_THREAD_VARIATION)
    {
        PRINT_ERR("Maximum Supported Thread Variation has been exceeded\n");
        PRINT_ERR("Number of Thread Variations created: %d", testTypeCount_g);
        PRINT_ERR(" Max is %d\n", MAX_THREAD_VARIATION);
        return CPA_STATUS_FAIL;
    }

    /* Return an error if the number of packets is not modulus zero of the
     * number of packets to cover IMIX packet mix.
     */
    if (packetSize == PACKET_IMIX && (numBuffLists % NUM_PACKETS_IMIX) != 0)
    {
        PRINT_ERR("To ensure that the weighting of IMIX packets is correct "
                  ", the number of buffers (%d) should be a multiple of %d\n",
                  numBuffLists,
                  NUM_PACKETS_IMIX);
        return CPA_STATUS_FAIL;
    }

    /*start crypto service if not already started*/
    if (CPA_STATUS_SUCCESS != startCyServices())
    {
        PRINT_ERR("Failed to start Crypto services\n");
        return CPA_STATUS_FAIL;
    }
    if (!poll_inline_g)
    {
        /* start polling threads if polling is enabled in the configuration file
         */
        if (CPA_STATUS_SUCCESS != cyCreatePollingThreadsIfPollingIsEnabled())
        {
            PRINT_ERR("Error creating polling threads\n");
            return CPA_STATUS_FAIL;
        }
    }
    /*as setup is a multidimensional char array we need to cast it to the
     * symmetric structure*/
    memcpy(&thread_name_g[testTypeCount_g][0], name, THREAD_NAME_LEN);
    symmetricSetup =
        (symmetric_test_params_t *)&thread_setup_g[testTypeCount_g][0];
    memset(symmetricSetup, 0, sizeof(symmetric_test_params_t));
    testSetupData_g[testTypeCount_g].performance_function =
        (performance_func_t)sampleSymmetricPerformance;
    testSetupData_g[testTypeCount_g].packetSize = packetSize;
    /*then we store the test setup in the above location*/
    // symmetricSetup->setupData.sessionPriority=CPA_CY_PRIORITY_HIGH;
    symmetricSetup->setupData.sessionPriority = priority;
    symmetricSetup->setupData.symOperation = opType;
    symmetricSetup->setupData.cipherSetupData.cipherAlgorithm = cipherAlg;
    symmetricSetup->setupData.cipherSetupData.cipherDirection =
        CPA_CY_SYM_CIPHER_DIRECTION_ENCRYPT;
    symmetricSetup->setupData.cipherSetupData.cipherKeyLenInBytes =
        cipherKeyLengthInBytes;
    symmetricSetup->setupData.hashSetupData.hashAlgorithm = hashAlg;
    symmetricSetup->setupData.hashSetupData.hashMode = hashMode;
    symmetricSetup->isDpApi = CPA_FALSE;
    symmetricSetup->cryptoSrcOffset = cipherOffset;
    /* in this code we limit the digest result len to be the same as the the
     * authentication key len*/
    symmetricSetup->setupData.hashSetupData.digestResultLenInBytes =
        authKeyLengthInBytes;

    /* GCM hash works only on 8,12 and 16 bytes, default to 16 if others */
    if (CPA_CY_SYM_HASH_AES_GCM == hashAlg &&
        (authKeyLengthInBytes != 8 && authKeyLengthInBytes != 12 &&
         authKeyLengthInBytes != 16))
    {
        PRINT("CPA_CY_SYM_HASH_AES_GCM digest length %u unsupported , "
              "defaulting to 16 \n",
              authKeyLengthInBytes);
        symmetricSetup->setupData.hashSetupData.digestResultLenInBytes = 16;
    }

#if CPA_CY_API_VERSION_NUM_MAJOR >= 2
    // for SH3  keylength == blocklength. And keylength might be > = digestlen

    if (CPA_CY_SYM_HASH_SHA3_256 == hashAlg)
    {
        symmetricSetup->setupData.hashSetupData.digestResultLenInBytes =
            setHashDigestLen(hashAlg);
    }
#endif
    // check which kind of hash mode is selected
    if (CPA_CY_SYM_HASH_MODE_NESTED == hashMode)
    { // nested mode
        // set the struct for nested hash mode
        if (NULL == nestedModeSetupDataPtr)
        {
            // set a default nested mode setup data
            symmetricSetup->setupData.hashSetupData.nestedModeSetupData
                .outerHashAlgorithm = hashAlg;
            symmetricSetup->setupData.hashSetupData.nestedModeSetupData
                .pInnerPrefixData = NULL;
            symmetricSetup->setupData.hashSetupData.nestedModeSetupData
                .innerPrefixLenInBytes = 0;
            symmetricSetup->setupData.hashSetupData.nestedModeSetupData
                .pOuterPrefixData = NULL;
            symmetricSetup->setupData.hashSetupData.nestedModeSetupData
                .outerPrefixLenInBytes = 0;
        }
        else
        {
            symmetricSetup->setupData.hashSetupData.nestedModeSetupData =
                *nestedModeSetupDataPtr;
        }
    }

    if ((CPA_CY_SYM_HASH_AES_XCBC == hashAlg) &&
        (AES_XCBC_DIGEST_LENGTH_IN_BYTES != authKeyLengthInBytes))
    {
        symmetricSetup->setupData.hashSetupData.authModeSetupData
            .authKeyLenInBytes = AES_XCBC_DIGEST_LENGTH_IN_BYTES;
    }
    else if ((CPA_CY_SYM_HASH_KASUMI_F9 == hashAlg) ||
             (CPA_CY_SYM_HASH_SNOW3G_UIA2 == hashAlg))
    {
        /*
         * KASUMI_F9 and SNOW3G_UIA2 supports authKeyLen=128bits
         * and digestResultLen=32bits
         */
        symmetricSetup->setupData.hashSetupData.authModeSetupData
            .authKeyLenInBytes = KASUMI_F9_OR_SNOW3G_UIA2_KEY_SIZE_128_IN_BYTES;
        symmetricSetup->setupData.hashSetupData.digestResultLenInBytes =
            setHashDigestLen(hashAlg);
    }

#if CPA_CY_API_VERSION_NUM_MAJOR >= 2
    // ZUC-EIA3 supports authKeyLen=128bits and digestResultLen=32bits
    else if (CPA_CY_SYM_HASH_ZUC_EIA3 == hashAlg)
    {
        symmetricSetup->setupData.hashSetupData.authModeSetupData
            .authKeyLenInBytes = KEY_SIZE_128_IN_BYTES;
    }
#endif
    else
    {
        symmetricSetup->setupData.hashSetupData.authModeSetupData
            .authKeyLenInBytes = authKeyLengthInBytes;
    }

    symmetricSetup->setupData.algChainOrder = chainOrder;
    symmetricSetup->syncMode = syncMode;
    symmetricSetup->flatBufferSizeInBytes = bufferSizeInBytes;
    symmetricSetup->numLoops = numLoops;
    symmetricSetup->numBuffLists = numBuffLists;
    if (((bufferSizeInBytes != 0) && (packetSize == PACKET_IMIX)) ||
        (bufferSizeInBytes % IV_LEN_FOR_16_BYTE_BLOCK_CIPHER != 0))
    {
        PRINT_ERR("Doesn't support PACKET_IMIX  "
                  "when the flat buffer size is not 0 or "
                  " it's not align with block size (%d): ",
                  bufferSizeInBytes);
        return CPA_STATUS_FAIL;
    }
    symmetricSetup->digestAppend = digestAppend;

    return CPA_STATUS_SUCCESS;
}

CpaStatus cipherNewPerformanceTest(CpaBoolean mode,
                                   Cpa32U mask,
                                   Cpa32U bufferSize,
                                   Cpa32U numBuffers,
                                   Cpa32U numLoops)
{
    CpaStatus retStatus = CPA_STATUS_SUCCESS;

    if (CPA_STATUS_SUCCESS !=
        setupNewSymmetricTest(
            CPA_CY_SYM_OP_CIPHER,
            CPA_CY_SYM_CIPHER_AES_CBC,
            KEY_SIZE_128_IN_BYTES,
            NOT_USED,
            CPA_CY_PRIORITY_HIGH,
            NOT_USED /* hash alg not needed in cipher test*/,
            NOT_USED /* hash mode not needed in cipher test*/,
            NOT_USED /* auth key len not needed in cipher test*/,
            NOT_USED /* chain mode not needed in cipher test*/,
            1,
            NULL, /* nested hash data not needed in cipher test*/
            bufferSize,
            0,
            numBuffers,
            numLoops,
            digestAppended_g))
    {
        PRINT_ERR("Error setting up Cipher Test\n");
        retStatus = CPA_STATUS_FAIL;
    }
    else
    {
        if (CPA_STATUS_SUCCESS != createStartandWaitForCompletionCrypto(SYM))
        {
            retStatus = CPA_STATUS_FAIL;
            /*set status to fail,
             * but continue with rest of testing*/
        }
    }
    return retStatus;
}
