mirror of https://github.com/hak5/openwrt.git
892 lines
29 KiB
C
892 lines
29 KiB
C
|
/***************************************************************************
|
||
|
*
|
||
|
* 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");
|