一、以下是请求过程:
static inline int crypto_wait_req(int err, struct crypto_wait *wait)
{
switch (err) {
case -EINPROGRESS:
case -EBUSY:
wait_for_completion(&wait->completion);
reinit_completion(&wait->completion);
err = wait->err;
break;
};
return err;
}
static inline void crypto_init_wait(struct crypto_wait *wait)
{
init_completion(&wait->completion);
}
二、 completion 用法
通常wait_for_completion需要completion唤醒配合,如下:
#include <linux/completion.h>
static struct completion my_completion; // 1. 定义完成事件
static int my_thread_function(void *data) {
pr_info("Thread started...\n");
msleep(5000); // 模拟一个长时间的操作
pr_info("Operation completed!\n");
complete(&my_completion); // 3. 标记完成事件
return 0;
}
static int __init my_init(void) {
struct task_struct *thread;
pr_info("Module init...\n");
// 初始化完成事件
init_completion(&my_completion);
// 创建一个内核线程
thread = kthread_run(my_thread_function, NULL, "my_thread");
if (IS_ERR(thread)) {
pr_err("Failed to create thread.\n");
return PTR_ERR(thread);
}
// 等待完成事件
wait_for_completion(&my_completion); //2. 阻塞当前线程,直到完成事件被标记为已完成。
pr_info("Main thread: Operation completed!\n");
return 0;
}
三、 set_callback
static inline void ahash_request_set_callback(struct ahash_request *req,
u32 flags,
crypto_completion_t compl,
void *data)
{
req->base.complete = compl;
req->base.data = data;
req->base.flags = flags;
}
四、crypto_ahash_update
硬件加速请求。
+static int ccp_do_sm3_update(struct ahash_request *req, unsigned int nbytes,
+ unsigned int final)
+{
+ struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+ struct ccp_ctx *ctx = crypto_ahash_ctx(tfm);
+ struct ccp_sm3_req_ctx *rctx = ahash_request_ctx(req);
+ ret = ccp_crypto_enqueue_request(&req->base, &rctx->cmd);
+
+ return ret;
+
+e_free:
+ sg_free_table(&rctx->data_sg);
+
+ return ret;
+}
int ccp_crypto_enqueue_request(struct crypto_async_request *req,
struct ccp_cmd *cmd)
{
struct ccp_crypto_cmd *crypto_cmd;
gfp_t gfp;
gfp = req->flags & CRYPTO_TFM_REQ_MAY_SLEEP ? GFP_KERNEL : GFP_ATOMIC;
crypto_cmd = kzalloc(sizeof(*crypto_cmd), gfp);
if (!crypto_cmd)
return -ENOMEM;
/* The tfm pointer must be saved and not referenced from the
* crypto_async_request (req) pointer because it is used after
* completion callback for the request and the req pointer
* might not be valid anymore.
*/
crypto_cmd->cmd = cmd;
crypto_cmd->req = req;
crypto_cmd->tfm = req->tfm;
cmd->callback = ccp_crypto_complete;
cmd->data = crypto_cmd;
if (req->flags & CRYPTO_TFM_REQ_MAY_BACKLOG)
cmd->flags |= CCP_CMD_MAY_BACKLOG;
else
cmd->flags &= ~CCP_CMD_MAY_BACKLOG;
return ccp_crypto_enqueue_cmd(crypto_cmd);
}
ccp_crypto_enqueue_request使用例子
static struct completion my_completion;
// CCP 操作完成的回调函数
static void ccp_crypto_complete(struct crypto_async_request *req, int error) {
printk(KERN_INFO "CCP operation completed.\n");
if (error) {
printk(KERN_ERR "CCP operation failed with error code %d\n", error);
} else {
printk(KERN_INFO "CCP operation succeeded.\n");
// 在这里处理操作的结果
}
// 唤醒等待完成的线程
complete(&my_completion);
}
static int __init my_init(void) {
struct ccp_cmd cmd;
struct crypto_async_request *req;
int ret;
printk(KERN_INFO "Module init...\n");
// 初始化完成事件
init_completion(&my_completion);
// 分配 CCP 操作结构体
req = crypto_req_alloc(ccp_queue, GFP_KERNEL);
if (!req) {
printk(KERN_ERR "Failed to allocate CCP request structure.\n");
return -ENOMEM;
}
// 初始化 CCP 操作结构体
memset(&cmd, 0, sizeof(cmd));
cmd.req = req;
cmd.req->complete = ccp_crypto_complete; // 操作完成的回调函数
// 设置其他操作参数
// ...
// 提交请求给 CCP 硬件加速器
ret = ccp_crypto_enqueue_request(cmd.req, &cmd);
if (ret) {
printk(KERN_ERR "Failed to enqueue request: %d\n", ret);
crypto_req_put(req);
return ret;
}
// 等待完成事件
wait_for_completion(&my_completion);
// 释放 CCP 操作结构体
crypto_req_put(req);
return 0;
}
struct ccp_cmd {
/* The list_head, work_struct, ccp and ret variables are for use
* by the CCP driver only.
*/
struct list_head entry;
struct work_struct work;
struct ccp_device *ccp;
int ret;
u32 flags;
enum ccp_engine engine;
u32 engine_error;
union {
struct ccp_aes_engine aes;
struct ccp_xts_aes_engine xts;
struct ccp_des3_engine des3;
struct ccp_sha_engine sha;
struct ccp_rsa_engine rsa;
struct ccp_passthru_engine passthru;
struct ccp_passthru_nomap_engine passthru_nomap;
struct ccp_ecc_engine ecc;
} u;
/* Completion callback support */
void (*callback)(void *data, int err);
void *data;
};
struct crypto_async_request {
struct list_head list;
crypto_completion_t complete;
void *data;
struct crypto_tfm *tfm;
u32 flags;
};
大致的逻辑应该是通过update 把硬件加速命令通过ccp_crypto_enqueue_request传递到硬件加速其中。如果处理完成直接返回正确,处理异常如果是设备忙碌,外部会通过crypto_wait_req等待,直到硬件加速器执行完毕唤醒wait。
crypto引擎的使用流程
crypto_init_wait(&wait);
ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
crypto_req_done, &wait);
crypto_ahash_clear_flags(ahash_tfm, ~0);
buf = kzalloc(keylen + QCE_MAX_ALIGN_SIZE, GFP_KERNEL);
if (!buf) {
ret = -ENOMEM;
goto err_free_req;
}
memcpy(buf, key, keylen);
sg_init_one(&sg, buf, keylen);
ahash_request_set_crypt(req, &sg, ctx->authkey, keylen);
ret = crypto_wait_req(crypto_ahash_digest(req), &wait);
如上,crypto_init_wait 初始化complete, set_callback设置回调任务。crypto_wait_req(crypto_ahash_digest(req), &wait);等待任务完成,
回调crypto_req_done唤醒任务。
ccp 队列任务处理逻辑
drivers\crypto\ccp\ccp-dev.c
static void ccp_do_cmd_complete(unsigned long data)
{
struct ccp_tasklet_data *tdata = (struct ccp_tasklet_data *)data;
struct ccp_cmd *cmd = tdata->cmd;
cmd->callback(cmd->data, cmd->ret);
complete(&tdata->completion);
}
/**
* ccp_cmd_queue_thread - create a kernel thread to manage a CCP queue
*
* @data: thread-specific data
*/
int ccp_cmd_queue_thread(void *data)
{
struct ccp_cmd_queue *cmd_q = (struct ccp_cmd_queue *)data;
struct ccp_cmd *cmd;
struct ccp_tasklet_data tdata;
struct tasklet_struct tasklet;
tasklet_init(&tasklet, ccp_do_cmd_complete, (unsigned long)&tdata);
set_current_state(TASK_INTERRUPTIBLE);
while (!kthread_should_stop()) {
schedule();
set_current_state(TASK_INTERRUPTIBLE);
cmd = ccp_dequeue_cmd(cmd_q);
if (!cmd)
continue;
__set_current_state(TASK_RUNNING);
/* Execute the command */
cmd->ret = ccp_run_cmd(cmd_q, cmd);
/* Schedule the completion callback */
tdata.cmd = cmd;
init_completion(&tdata.completion);
tasklet_schedule(&tasklet);
wait_for_completion(&tdata.completion);
}
__set_current_state(TASK_RUNNING);
return 0;
}
tasklet_init 是一个 Linux 内核中用于初始化 Tasklet 的函数。Tasklet 是 Linux 内核中一种轻量级的软中断机制,用于在中断上下文之外执行延迟的、短暂的任务。
#include <linux/interrupt.h>
// 定义 Tasklet 执行的函数
void tasklet_function(unsigned long data) {
printk(KERN_INFO "Tasklet executed with data: %lu\n", data);
}
// 中断处理程序
irqreturn_t my_interrupt_handler(int irq, void *dev_id) {
// 触发 Tasklet 执行
tasklet_schedule((struct tasklet_struct *)dev_id);
return IRQ_HANDLED;
}
int init_module(void) {
// 分配 Tasklet
struct tasklet_struct *my_tasklet;
my_tasklet = kmalloc(sizeof(struct tasklet_struct), GFP_KERNEL);
if (!my_tasklet) {
return -ENOMEM;
}
// 初始化 Tasklet
tasklet_init(my_tasklet, tasklet_function, 42);
// 注册中断处理程序并将 Tasklet 作为参数传递给它
// 这只是一个示例,实际中断处理程序的注册方式取决于硬件和具体情况
request_irq(IRQ_NUMBER, my_interrupt_handler, IRQF_SHARED, "my_interrupt", (void *)my_tasklet);
return 0;
}
void cleanup_module(void) {
// 删除中断处理程序
free_irq(IRQ_NUMBER, (void *)my_tasklet);
// 删除 Tasklet
tasklet_kill(my_tasklet);
kfree(my_tasklet);
}
如上tasklet_init注册的任务会在中断中利用tasklet_shedule触发从而执行任务。
也就是说ccp_cmd_queue_thread类似一个监听服务,在不断地处理任务。
static int crypto_ahash_init_tfm(struct crypto_tfm *tfm)
{
struct crypto_ahash *hash = __crypto_ahash_cast(tfm);
struct ahash_alg *alg = crypto_ahash_alg(hash);
hash->setkey = ahash_nosetkey;
if (tfm->__crt_alg->cra_type != &crypto_ahash_type)
return crypto_init_shash_ops_async(tfm);
hash->init = alg->init;
hash->update = alg->update;
hash->final = alg->final;
hash->finup = alg->finup ?: ahash_def_finup;
hash->digest = alg->digest;
hash->export = alg->export;
hash->import = alg->import;
if (alg->setkey) {
hash->setkey = alg->setkey;
ahash_set_needkey(hash);
}
return 0;
}
ccp 引擎触发流程:
算法调用流程: