/*************************************************************************** * * This file is provided under a dual BSD/GPLv2 license. When using or * redistributing this file, you may do so under either license. * * GPL LICENSE SUMMARY * * Copyright(c) 2007,2008 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * The full GNU General Public License is included in this distribution * in the file called LICENSE.GPL. * * Contact Information: * Intel Corporation * * BSD LICENSE * * Copyright(c) 2007,2008 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name of Intel Corporation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * * version: Security.L.1.0.130 * ***************************************************************************/ /* * An OCF module that uses IntelĀ® QuickAssist Integrated Accelerator to do the * crypto. * * This driver requires the ICP Access Library that is available from Intel in * order to operate. */ #include "icp_ocf.h" #define ICP_OCF_COMP_NAME "ICP_OCF" #define ICP_OCF_VER_MAIN (2) #define ICP_OCF_VER_MJR (0) #define ICP_OCF_VER_MNR (0) #define MAX_DEREG_RETRIES (100) #define DEFAULT_DEREG_RETRIES (10) #define DEFAULT_DEREG_DELAY_IN_JIFFIES (10) /* This defines the maximum number of sessions possible between OCF and the OCF Tolapai Driver. If set to zero, there is no limit. */ #define DEFAULT_OCF_TO_DRV_MAX_SESSION_COUNT (0) #define NUM_SUPPORTED_CAPABILITIES (21) /*Slabs zones*/ struct kmem_cache *drvSessionData_zone = NULL; struct kmem_cache *drvOpData_zone = NULL; struct kmem_cache *drvDH_zone = NULL; struct kmem_cache *drvLnModExp_zone = NULL; struct kmem_cache *drvRSADecrypt_zone = NULL; struct kmem_cache *drvRSAPrivateKey_zone = NULL; struct kmem_cache *drvDSARSSign_zone = NULL; struct kmem_cache *drvDSARSSignKValue_zone = NULL; struct kmem_cache *drvDSAVerify_zone = NULL; /*Slab zones for flatbuffers and bufferlist*/ struct kmem_cache *drvFlatBuffer_zone = NULL; static int icp_ocfDrvInit(void); static void icp_ocfDrvExit(void); static void icp_ocfDrvFreeCaches(void); static void icp_ocfDrvDeferedFreeLacSessionProcess(void *arg); int32_t icp_ocfDrvDriverId = INVALID_DRIVER_ID; /* Module parameter - gives the number of times LAC deregistration shall be re-tried */ int num_dereg_retries = DEFAULT_DEREG_RETRIES; /* Module parameter - gives the delay time in jiffies before a LAC session shall be attempted to be deregistered again */ int dereg_retry_delay_in_jiffies = DEFAULT_DEREG_DELAY_IN_JIFFIES; /* Module parameter - gives the maximum number of sessions possible between OCF and the OCF Tolapai Driver. If set to zero, there is no limit.*/ int max_sessions = DEFAULT_OCF_TO_DRV_MAX_SESSION_COUNT; /* This is set when the module is removed from the system, no further processing can take place if this is set */ atomic_t icp_ocfDrvIsExiting = ATOMIC_INIT(0); /* This is used to show how many lac sessions were not deregistered*/ atomic_t lac_session_failed_dereg_count = ATOMIC_INIT(0); /* This is used to track the number of registered sessions between OCF and * and the OCF Tolapai driver, when max_session is set to value other than * zero. This ensures that the max_session set for the OCF and the driver * is equal to the LAC registered sessions */ atomic_t num_ocf_to_drv_registered_sessions = ATOMIC_INIT(0); /* Head of linked list used to store session data */ struct list_head icp_ocfDrvGlobalSymListHead; struct list_head icp_ocfDrvGlobalSymListHead_FreeMemList; spinlock_t icp_ocfDrvSymSessInfoListSpinlock = SPIN_LOCK_UNLOCKED; rwlock_t icp_kmem_cache_destroy_alloc_lock = RW_LOCK_UNLOCKED; struct workqueue_struct *icp_ocfDrvFreeLacSessionWorkQ; struct icp_drvBuffListInfo defBuffListInfo; static struct { softc_device_decl sc_dev; } icpDev; static device_method_t icp_methods = { /* crypto device methods */ DEVMETHOD(cryptodev_newsession, icp_ocfDrvNewSession), DEVMETHOD(cryptodev_freesession, icp_ocfDrvFreeLACSession), DEVMETHOD(cryptodev_process, icp_ocfDrvSymProcess), DEVMETHOD(cryptodev_kprocess, icp_ocfDrvPkeProcess), }; module_param(num_dereg_retries, int, S_IRUGO); module_param(dereg_retry_delay_in_jiffies, int, S_IRUGO); module_param(max_sessions, int, S_IRUGO); MODULE_PARM_DESC(num_dereg_retries, "Number of times to retry LAC Sym Session Deregistration. " "Default 10, Max 100"); MODULE_PARM_DESC(dereg_retry_delay_in_jiffies, "Delay in jiffies " "(added to a schedule() function call) before a LAC Sym " "Session Dereg is retried. Default 10"); MODULE_PARM_DESC(max_sessions, "This sets the maximum number of sessions " "between OCF and this driver. If this value is set to zero, " "max session count checking is disabled. Default is zero(0)"); /* Name : icp_ocfDrvInit * * Description : This function will register all the symmetric and asymmetric * functionality that will be accelerated by the hardware. It will also * get a unique driver ID from the OCF and initialise all slab caches */ static int __init icp_ocfDrvInit(void) { int ocfStatus = 0; IPRINTK("=== %s ver %d.%d.%d ===\n", ICP_OCF_COMP_NAME, ICP_OCF_VER_MAIN, ICP_OCF_VER_MJR, ICP_OCF_VER_MNR); if (MAX_DEREG_RETRIES < num_dereg_retries) { EPRINTK("Session deregistration retry count set to greater " "than %d", MAX_DEREG_RETRIES); return -1; } /* Initialize and Start the Cryptographic component */ if (CPA_STATUS_SUCCESS != cpaCyStartInstance(CPA_INSTANCE_HANDLE_SINGLE)) { EPRINTK("Failed to initialize and start the instance " "of the Cryptographic component.\n"); return -1; } /* Set the default size of BufferList to allocate */ memset(&defBuffListInfo, 0, sizeof(struct icp_drvBuffListInfo)); if (ICP_OCF_DRV_STATUS_SUCCESS != icp_ocfDrvBufferListMemInfo(ICP_OCF_DRV_DEFAULT_BUFFLIST_ARRAYS, &defBuffListInfo)) { EPRINTK("Failed to get bufferlist memory info.\n"); return -1; } /*Register OCF Tolapai Driver with OCF */ memset(&icpDev, 0, sizeof(icpDev)); softc_device_init(&icpDev, "icp", 0, icp_methods); icp_ocfDrvDriverId = crypto_get_driverid(softc_get_device(&icpDev), CRYPTOCAP_F_HARDWARE); if (icp_ocfDrvDriverId < 0) { EPRINTK("%s : ICP driver failed to register with OCF!\n", __FUNCTION__); return -ENODEV; } /*Create all the slab caches used by the OCF Tolapai Driver */ drvSessionData_zone = ICP_CACHE_CREATE("ICP Session Data", struct icp_drvSessionData); ICP_CACHE_NULL_CHECK(drvSessionData_zone); /* * Allocation of the OpData includes the allocation space for meta data. * The memory after the opData structure is reserved for this meta data. */ drvOpData_zone = kmem_cache_create("ICP Op Data", sizeof(struct icp_drvOpData) + defBuffListInfo.metaSize ,0, SLAB_HWCACHE_ALIGN, NULL, NULL); ICP_CACHE_NULL_CHECK(drvOpData_zone); drvDH_zone = ICP_CACHE_CREATE("ICP DH data", CpaCyDhPhase1KeyGenOpData); ICP_CACHE_NULL_CHECK(drvDH_zone); drvLnModExp_zone = ICP_CACHE_CREATE("ICP ModExp data", CpaCyLnModExpOpData); ICP_CACHE_NULL_CHECK(drvLnModExp_zone); drvRSADecrypt_zone = ICP_CACHE_CREATE("ICP RSA decrypt data", CpaCyRsaDecryptOpData); ICP_CACHE_NULL_CHECK(drvRSADecrypt_zone); drvRSAPrivateKey_zone = ICP_CACHE_CREATE("ICP RSA private key data", CpaCyRsaPrivateKey); ICP_CACHE_NULL_CHECK(drvRSAPrivateKey_zone); drvDSARSSign_zone = ICP_CACHE_CREATE("ICP DSA Sign", CpaCyDsaRSSignOpData); ICP_CACHE_NULL_CHECK(drvDSARSSign_zone); /*too awkward to use a macro here */ drvDSARSSignKValue_zone = kmem_cache_create("ICP DSA Sign Rand Val", DSA_RS_SIGN_PRIMEQ_SIZE_IN_BYTES, 0, SLAB_HWCACHE_ALIGN, NULL, NULL); ICP_CACHE_NULL_CHECK(drvDSARSSignKValue_zone); drvDSAVerify_zone = ICP_CACHE_CREATE("ICP DSA Verify", CpaCyDsaVerifyOpData); ICP_CACHE_NULL_CHECK(drvDSAVerify_zone); drvFlatBuffer_zone = ICP_CACHE_CREATE("ICP Flat Buffers", CpaFlatBuffer); ICP_CACHE_NULL_CHECK(drvFlatBuffer_zone); /* Register the ICP symmetric crypto support. */ ICP_REGISTER_SYM_FUNCTIONALITY_WITH_OCF(CRYPTO_NULL_CBC); ICP_REGISTER_SYM_FUNCTIONALITY_WITH_OCF(CRYPTO_DES_CBC); ICP_REGISTER_SYM_FUNCTIONALITY_WITH_OCF(CRYPTO_3DES_CBC); ICP_REGISTER_SYM_FUNCTIONALITY_WITH_OCF(CRYPTO_AES_CBC); ICP_REGISTER_SYM_FUNCTIONALITY_WITH_OCF(CRYPTO_ARC4); ICP_REGISTER_SYM_FUNCTIONALITY_WITH_OCF(CRYPTO_MD5); ICP_REGISTER_SYM_FUNCTIONALITY_WITH_OCF(CRYPTO_MD5_HMAC); ICP_REGISTER_SYM_FUNCTIONALITY_WITH_OCF(CRYPTO_SHA1); ICP_REGISTER_SYM_FUNCTIONALITY_WITH_OCF(CRYPTO_SHA1_HMAC); ICP_REGISTER_SYM_FUNCTIONALITY_WITH_OCF(CRYPTO_SHA2_256); ICP_REGISTER_SYM_FUNCTIONALITY_WITH_OCF(CRYPTO_SHA2_256_HMAC); ICP_REGISTER_SYM_FUNCTIONALITY_WITH_OCF(CRYPTO_SHA2_384); ICP_REGISTER_SYM_FUNCTIONALITY_WITH_OCF(CRYPTO_SHA2_384_HMAC); ICP_REGISTER_SYM_FUNCTIONALITY_WITH_OCF(CRYPTO_SHA2_512); ICP_REGISTER_SYM_FUNCTIONALITY_WITH_OCF(CRYPTO_SHA2_512_HMAC); /* Register the ICP asymmetric algorithm support */ ICP_REGISTER_ASYM_FUNCTIONALITY_WITH_OCF(CRK_DH_COMPUTE_KEY); ICP_REGISTER_ASYM_FUNCTIONALITY_WITH_OCF(CRK_MOD_EXP); ICP_REGISTER_ASYM_FUNCTIONALITY_WITH_OCF(CRK_MOD_EXP_CRT); ICP_REGISTER_ASYM_FUNCTIONALITY_WITH_OCF(CRK_DSA_SIGN); ICP_REGISTER_ASYM_FUNCTIONALITY_WITH_OCF(CRK_DSA_VERIFY); /* Register the ICP random number generator support */ if (OCF_REGISTRATION_STATUS_SUCCESS == crypto_rregister(icp_ocfDrvDriverId, icp_ocfDrvReadRandom, NULL)) { ocfStatus++; } if (OCF_ZERO_FUNCTIONALITY_REGISTERED == ocfStatus) { DPRINTK("%s: Failed to register any device capabilities\n", __FUNCTION__); icp_ocfDrvFreeCaches(); icp_ocfDrvDriverId = INVALID_DRIVER_ID; return -ECANCELED; } DPRINTK("%s: Registered %d of %d device capabilities\n", __FUNCTION__, ocfStatus, NUM_SUPPORTED_CAPABILITIES); /*Session data linked list used during module exit*/ INIT_LIST_HEAD(&icp_ocfDrvGlobalSymListHead); INIT_LIST_HEAD(&icp_ocfDrvGlobalSymListHead_FreeMemList); icp_ocfDrvFreeLacSessionWorkQ = create_singlethread_workqueue("ocfLacDeregWorkQueue"); return 0; } /* Name : icp_ocfDrvExit * * Description : This function will deregister all the symmetric sessions * registered with the LAC component. It will also deregister all symmetric * and asymmetric functionality that can be accelerated by the hardware via OCF * and random number generation if it is enabled. */ static void icp_ocfDrvExit(void) { CpaStatus lacStatus = CPA_STATUS_SUCCESS; struct icp_drvSessionData *sessionData = NULL; struct icp_drvSessionData *tempSessionData = NULL; int i, remaining_delay_time_in_jiffies = 0; /* There is a possibility of a process or new session command being */ /* sent before this variable is incremented. The aim of this variable */ /* is to stop a loop of calls creating a deadlock situation which */ /* would prevent the driver from exiting. */ atomic_inc(&icp_ocfDrvIsExiting); /*Existing sessions will be routed to another driver after these calls */ crypto_unregister_all(icp_ocfDrvDriverId); crypto_runregister_all(icp_ocfDrvDriverId); /*If any sessions are waiting to be deregistered, do that. This also flushes the work queue */ destroy_workqueue(icp_ocfDrvFreeLacSessionWorkQ); /*ENTER CRITICAL SECTION */ spin_lock_bh(&icp_ocfDrvSymSessInfoListSpinlock); list_for_each_entry_safe(tempSessionData, sessionData, &icp_ocfDrvGlobalSymListHead, listNode) { for (i = 0; i < num_dereg_retries; i++) { /*No harm if bad input - LAC will handle error cases */ if (ICP_SESSION_RUNNING == tempSessionData->inUse) { lacStatus = cpaCySymRemoveSession (CPA_INSTANCE_HANDLE_SINGLE, tempSessionData->sessHandle); if (CPA_STATUS_SUCCESS == lacStatus) { /* Succesfully deregistered */ break; } else if (CPA_STATUS_RETRY != lacStatus) { atomic_inc (&lac_session_failed_dereg_count); break; } /*schedule_timout returns the time left for completion if * this task is set to TASK_INTERRUPTIBLE */ remaining_delay_time_in_jiffies = dereg_retry_delay_in_jiffies; while (0 > remaining_delay_time_in_jiffies) { remaining_delay_time_in_jiffies = schedule_timeout (remaining_delay_time_in_jiffies); } DPRINTK ("%s(): Retry %d to deregistrate the session\n", __FUNCTION__, i); } } /*remove from current list */ list_del(&(tempSessionData->listNode)); /*add to free mem linked list */ list_add(&(tempSessionData->listNode), &icp_ocfDrvGlobalSymListHead_FreeMemList); } /*EXIT CRITICAL SECTION */ spin_unlock_bh(&icp_ocfDrvSymSessInfoListSpinlock); /*set back to initial values */ sessionData = NULL; /*still have a reference in our list! */ tempSessionData = NULL; /*free memory */ list_for_each_entry_safe(tempSessionData, sessionData, &icp_ocfDrvGlobalSymListHead_FreeMemList, listNode) { list_del(&(tempSessionData->listNode)); /* Free allocated CpaCySymSessionCtx */ if (NULL != tempSessionData->sessHandle) { kfree(tempSessionData->sessHandle); } memset(tempSessionData, 0, sizeof(struct icp_drvSessionData)); kmem_cache_free(drvSessionData_zone, tempSessionData); } if (0 != atomic_read(&lac_session_failed_dereg_count)) { DPRINTK("%s(): %d LAC sessions were not deregistered " "correctly. This is not a clean exit! \n", __FUNCTION__, atomic_read(&lac_session_failed_dereg_count)); } icp_ocfDrvFreeCaches(); icp_ocfDrvDriverId = INVALID_DRIVER_ID; /* Shutdown the Cryptographic component */ lacStatus = cpaCyStopInstance(CPA_INSTANCE_HANDLE_SINGLE); if (CPA_STATUS_SUCCESS != lacStatus) { DPRINTK("%s(): Failed to stop instance of the " "Cryptographic component.(status == %d)\n", __FUNCTION__, lacStatus); } } /* Name : icp_ocfDrvFreeCaches * * Description : This function deregisters all slab caches */ static void icp_ocfDrvFreeCaches(void) { if (atomic_read(&icp_ocfDrvIsExiting) != CPA_TRUE) { atomic_set(&icp_ocfDrvIsExiting, 1); } /*Sym Zones */ ICP_CACHE_DESTROY(drvSessionData_zone); ICP_CACHE_DESTROY(drvOpData_zone); /*Asym zones */ ICP_CACHE_DESTROY(drvDH_zone); ICP_CACHE_DESTROY(drvLnModExp_zone); ICP_CACHE_DESTROY(drvRSADecrypt_zone); ICP_CACHE_DESTROY(drvRSAPrivateKey_zone); ICP_CACHE_DESTROY(drvDSARSSignKValue_zone); ICP_CACHE_DESTROY(drvDSARSSign_zone); ICP_CACHE_DESTROY(drvDSAVerify_zone); /*FlatBuffer and BufferList Zones */ ICP_CACHE_DESTROY(drvFlatBuffer_zone); } /* Name : icp_ocfDrvDeregRetry * * Description : This function will try to farm the session deregistration * off to a work queue. If it fails, nothing more can be done and it * returns an error */ int icp_ocfDrvDeregRetry(CpaCySymSessionCtx sessionToDeregister) { struct icp_ocfDrvFreeLacSession *workstore = NULL; DPRINTK("%s(): Retry - Deregistering session (%p)\n", __FUNCTION__, sessionToDeregister); /*make sure the session is not available to be allocated during this process */ atomic_inc(&lac_session_failed_dereg_count); /*Farm off to work queue */ workstore = kmalloc(sizeof(struct icp_ocfDrvFreeLacSession), GFP_ATOMIC); if (NULL == workstore) { DPRINTK("%s(): unable to free session - no memory available " "for work queue\n", __FUNCTION__); return ENOMEM; } workstore->sessionToDeregister = sessionToDeregister; INIT_WORK(&(workstore->work), icp_ocfDrvDeferedFreeLacSessionProcess, workstore); queue_work(icp_ocfDrvFreeLacSessionWorkQ, &(workstore->work)); return ICP_OCF_DRV_STATUS_SUCCESS; } /* Name : icp_ocfDrvDeferedFreeLacSessionProcess * * Description : This function will retry (module input parameter) * 'num_dereg_retries' times to deregister any symmetric session that recieves a * CPA_STATUS_RETRY message from the LAC component. This function is run in * Thread context because it is called from a worker thread */ static void icp_ocfDrvDeferedFreeLacSessionProcess(void *arg) { struct icp_ocfDrvFreeLacSession *workstore = NULL; CpaCySymSessionCtx sessionToDeregister = NULL; int i = 0; int remaining_delay_time_in_jiffies = 0; CpaStatus lacStatus = CPA_STATUS_SUCCESS; workstore = (struct icp_ocfDrvFreeLacSession *)arg; if (NULL == workstore) { DPRINTK("%s() function called with null parameter \n", __FUNCTION__); return; } sessionToDeregister = workstore->sessionToDeregister; kfree(workstore); /*if exiting, give deregistration one more blast only */ if (atomic_read(&icp_ocfDrvIsExiting) == CPA_TRUE) { lacStatus = cpaCySymRemoveSession(CPA_INSTANCE_HANDLE_SINGLE, sessionToDeregister); if (lacStatus != CPA_STATUS_SUCCESS) { DPRINTK("%s() Failed to Dereg LAC session %p " "during module exit\n", __FUNCTION__, sessionToDeregister); return; } atomic_dec(&lac_session_failed_dereg_count); return; } for (i = 0; i <= num_dereg_retries; i++) { lacStatus = cpaCySymRemoveSession(CPA_INSTANCE_HANDLE_SINGLE, sessionToDeregister); if (lacStatus == CPA_STATUS_SUCCESS) { atomic_dec(&lac_session_failed_dereg_count); return; } if (lacStatus != CPA_STATUS_RETRY) { DPRINTK("%s() Failed to deregister session - lacStatus " " = %d", __FUNCTION__, lacStatus); break; } /*schedule_timout returns the time left for completion if this task is set to TASK_INTERRUPTIBLE */ remaining_delay_time_in_jiffies = dereg_retry_delay_in_jiffies; while (0 > remaining_delay_time_in_jiffies) { remaining_delay_time_in_jiffies = schedule_timeout(remaining_delay_time_in_jiffies); } } DPRINTK("%s(): Unable to deregister session\n", __FUNCTION__); DPRINTK("%s(): Number of unavailable LAC sessions = %d\n", __FUNCTION__, atomic_read(&lac_session_failed_dereg_count)); } /* Name : icp_ocfDrvPtrAndLenToFlatBuffer * * Description : This function converts a "pointer and length" buffer * structure to Fredericksburg Flat Buffer (CpaFlatBuffer) format. * * This function assumes that the data passed in are valid. */ inline void icp_ocfDrvPtrAndLenToFlatBuffer(void *pData, uint32_t len, CpaFlatBuffer * pFlatBuffer) { pFlatBuffer->pData = pData; pFlatBuffer->dataLenInBytes = len; } /* Name : icp_ocfDrvSingleSkBuffToFlatBuffer * * Description : This function converts a single socket buffer (sk_buff) * structure to a Fredericksburg Flat Buffer (CpaFlatBuffer) format. * * This function assumes that the data passed in are valid. */ static inline void icp_ocfDrvSingleSkBuffToFlatBuffer(struct sk_buff *pSkb, CpaFlatBuffer * pFlatBuffer) { pFlatBuffer->pData = pSkb->data; pFlatBuffer->dataLenInBytes = skb_headlen(pSkb); } /* Name : icp_ocfDrvSkBuffToBufferList * * Description : This function converts a socket buffer (sk_buff) structure to * Fredericksburg Scatter/Gather (CpaBufferList) buffer format. * * This function assumes that the bufferlist has been allocated with the correct * number of buffer arrays. * */ inline int icp_ocfDrvSkBuffToBufferList(struct sk_buff *pSkb, CpaBufferList * bufferList) { CpaFlatBuffer *curFlatBuffer = NULL; char *skbuffPageAddr = NULL; struct sk_buff *pCurFrag = NULL; struct skb_shared_info *pShInfo = NULL; uint32_t page_offset = 0, i = 0; DPRINTK("%s(): Entry Point\n", __FUNCTION__); /* * In all cases, the first skb needs to be translated to FlatBuffer. * Perform a buffer translation for the first skbuff */ curFlatBuffer = bufferList->pBuffers; icp_ocfDrvSingleSkBuffToFlatBuffer(pSkb, curFlatBuffer); /* Set the userData to point to the original sk_buff */ bufferList->pUserData = (void *)pSkb; /* We now know we'll have at least one element in the SGL */ bufferList->numBuffers = 1; if (0 == skb_is_nonlinear(pSkb)) { /* Is a linear buffer - therefore it's a single skbuff */ DPRINTK("%s(): Exit Point\n", __FUNCTION__); return ICP_OCF_DRV_STATUS_SUCCESS; } curFlatBuffer++; pShInfo = skb_shinfo(pSkb); if (pShInfo->frag_list != NULL && pShInfo->nr_frags != 0) { EPRINTK("%s():" "Translation for a combination of frag_list " "and frags[] array not supported!\n", __FUNCTION__); return ICP_OCF_DRV_STATUS_FAIL; } else if (pShInfo->frag_list != NULL) { /* * Non linear skbuff supported through frag_list * Perform translation for each fragment (sk_buff) * in the frag_list of the first sk_buff. */ for (pCurFrag = pShInfo->frag_list; pCurFrag != NULL; pCurFrag = pCurFrag->next) { icp_ocfDrvSingleSkBuffToFlatBuffer(pCurFrag, curFlatBuffer); curFlatBuffer++; bufferList->numBuffers++; } } else if (pShInfo->nr_frags != 0) { /* * Perform translation for each fragment in frags array * and add to the BufferList */ for (i = 0; i < pShInfo->nr_frags; i++) { /* Get the page address and offset of this frag */ skbuffPageAddr = (char *)pShInfo->frags[i].page; page_offset = pShInfo->frags[i].page_offset; /* Convert a pointer and length to a flat buffer */ icp_ocfDrvPtrAndLenToFlatBuffer(skbuffPageAddr + page_offset, pShInfo->frags[i].size, curFlatBuffer); curFlatBuffer++; bufferList->numBuffers++; } } else { EPRINTK("%s():" "Could not recognize skbuff fragments!\n", __FUNCTION__); return ICP_OCF_DRV_STATUS_FAIL; } DPRINTK("%s(): Exit Point\n", __FUNCTION__); return ICP_OCF_DRV_STATUS_SUCCESS; } /* Name : icp_ocfDrvBufferListToSkBuff * * Description : This function converts a Fredericksburg Scatter/Gather * (CpaBufferList) buffer format to socket buffer structure. */ inline int icp_ocfDrvBufferListToSkBuff(CpaBufferList * bufferList, struct sk_buff **skb) { DPRINTK("%s(): Entry Point\n", __FUNCTION__); /* Retrieve the orignal skbuff */ *skb = (struct sk_buff *)bufferList->pUserData; if (NULL == *skb) { EPRINTK("%s():" "Error on converting from a BufferList. " "The BufferList does not contain an sk_buff.\n", __FUNCTION__); return ICP_OCF_DRV_STATUS_FAIL; } DPRINTK("%s(): Exit Point\n", __FUNCTION__); return ICP_OCF_DRV_STATUS_SUCCESS; } /* Name : icp_ocfDrvPtrAndLenToBufferList * * Description : This function converts a "pointer and length" buffer * structure to Fredericksburg Scatter/Gather Buffer (CpaBufferList) format. * * This function assumes that the data passed in are valid. */ inline void icp_ocfDrvPtrAndLenToBufferList(void *pDataIn, uint32_t length, CpaBufferList * pBufferList) { pBufferList->numBuffers = 1; pBufferList->pBuffers->pData = pDataIn; pBufferList->pBuffers->dataLenInBytes = length; } /* Name : icp_ocfDrvBufferListToPtrAndLen * * Description : This function converts Fredericksburg Scatter/Gather Buffer * (CpaBufferList) format to a "pointer and length" buffer structure. * * This function assumes that the data passed in are valid. */ inline void icp_ocfDrvBufferListToPtrAndLen(CpaBufferList * pBufferList, void **ppDataOut, uint32_t * pLength) { *ppDataOut = pBufferList->pBuffers->pData; *pLength = pBufferList->pBuffers->dataLenInBytes; } /* Name : icp_ocfDrvBufferListMemInfo * * Description : This function will set the number of flat buffers in * bufferlist, the size of memory to allocate for the pPrivateMetaData * member of the CpaBufferList. */ int icp_ocfDrvBufferListMemInfo(uint16_t numBuffers, struct icp_drvBuffListInfo *buffListInfo) { buffListInfo->numBuffers = numBuffers; if (CPA_STATUS_SUCCESS != cpaCyBufferListGetMetaSize(CPA_INSTANCE_HANDLE_SINGLE, buffListInfo->numBuffers, &(buffListInfo->metaSize))) { EPRINTK("%s() Failed to get buffer list meta size.\n", __FUNCTION__); return ICP_OCF_DRV_STATUS_FAIL; } return ICP_OCF_DRV_STATUS_SUCCESS; } /* Name : icp_ocfDrvGetSkBuffFrags * * Description : This function will determine the number of * fragments in a socket buffer(sk_buff). */ inline uint16_t icp_ocfDrvGetSkBuffFrags(struct sk_buff * pSkb) { uint16_t numFrags = 0; struct sk_buff *pCurFrag = NULL; struct skb_shared_info *pShInfo = NULL; if (NULL == pSkb) return 0; numFrags = 1; if (0 == skb_is_nonlinear(pSkb)) { /* Linear buffer - it's a single skbuff */ return numFrags; } pShInfo = skb_shinfo(pSkb); if (NULL != pShInfo->frag_list && 0 != pShInfo->nr_frags) { EPRINTK("%s(): Combination of frag_list " "and frags[] array not supported!\n", __FUNCTION__); return 0; } else if (0 != pShInfo->nr_frags) { numFrags += pShInfo->nr_frags; return numFrags; } else if (NULL != pShInfo->frag_list) { for (pCurFrag = pShInfo->frag_list; pCurFrag != NULL; pCurFrag = pCurFrag->next) { numFrags++; } return numFrags; } else { return 0; } } /* Name : icp_ocfDrvFreeFlatBuffer * * Description : This function will deallocate flat buffer. */ inline void icp_ocfDrvFreeFlatBuffer(CpaFlatBuffer * pFlatBuffer) { if (pFlatBuffer != NULL) { memset(pFlatBuffer, 0, sizeof(CpaFlatBuffer)); kmem_cache_free(drvFlatBuffer_zone, pFlatBuffer); } } /* Name : icp_ocfDrvAllocMetaData * * Description : This function will allocate memory for the * pPrivateMetaData member of CpaBufferList. */ inline int icp_ocfDrvAllocMetaData(CpaBufferList * pBufferList, const struct icp_drvOpData *pOpData) { Cpa32U metaSize = 0; if (pBufferList->numBuffers <= ICP_OCF_DRV_DEFAULT_BUFFLIST_ARRAYS){ void *pOpDataStartAddr = (void *)pOpData; if (0 == defBuffListInfo.metaSize) { pBufferList->pPrivateMetaData = NULL; return ICP_OCF_DRV_STATUS_SUCCESS; } /* * The meta data allocation has been included as part of the * op data. It has been pre-allocated in memory just after the * icp_drvOpData structure. */ pBufferList->pPrivateMetaData = pOpDataStartAddr + sizeof(struct icp_drvOpData); } else { if (CPA_STATUS_SUCCESS != cpaCyBufferListGetMetaSize(CPA_INSTANCE_HANDLE_SINGLE, pBufferList->numBuffers, &metaSize)) { EPRINTK("%s() Failed to get buffer list meta size.\n", __FUNCTION__); return ICP_OCF_DRV_STATUS_FAIL; } if (0 == metaSize) { pBufferList->pPrivateMetaData = NULL; return ICP_OCF_DRV_STATUS_SUCCESS; } pBufferList->pPrivateMetaData = kmalloc(metaSize, GFP_ATOMIC); } if (NULL == pBufferList->pPrivateMetaData) { EPRINTK("%s() Failed to allocate pPrivateMetaData.\n", __FUNCTION__); return ICP_OCF_DRV_STATUS_FAIL; } return ICP_OCF_DRV_STATUS_SUCCESS; } /* Name : icp_ocfDrvFreeMetaData * * Description : This function will deallocate pPrivateMetaData memory. */ inline void icp_ocfDrvFreeMetaData(CpaBufferList * pBufferList) { if (NULL == pBufferList->pPrivateMetaData) { return; } /* * Only free the meta data if the BufferList has more than * ICP_OCF_DRV_DEFAULT_BUFFLIST_ARRAYS number of buffers. * Otherwise, the meta data shall be freed when the icp_drvOpData is * freed. */ if (ICP_OCF_DRV_DEFAULT_BUFFLIST_ARRAYS < pBufferList->numBuffers){ kfree(pBufferList->pPrivateMetaData); } } module_init(icp_ocfDrvInit); module_exit(icp_ocfDrvExit); MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("Intel"); MODULE_DESCRIPTION("OCF Driver for Intel Quick Assist crypto acceleration");