600字范文,内容丰富有趣,生活中的好帮手!
600字范文 > ssl加速卡_OpenSSL异步模式与Intel QAT加速卡(一)

ssl加速卡_OpenSSL异步模式与Intel QAT加速卡(一)

时间:2022-11-11 13:19:58

相关推荐

ssl加速卡_OpenSSL异步模式与Intel QAT加速卡(一)

这周工作遇到一个超越原先认知范围的问题,今天下午开始写这篇博文之前依然束手无策,但就刚刚被别部门给力的队友解决了!今天所写的技术都是出初步接触,所以如果有大神直接使用过OpenSSL的异步模式去对接QAT engine进行RSA解密或者gzip等密集计算的(尤其是多线程使用),跪求出来指点与指正~

一、问题综述

我们Workflow作为一个性能优异的异步调度框架,用作server可以对标的系统之一就是nginx这样的接入,而且与nginx的事件编程与形如http模块拆分11个阶段的这种反人类设计比起来,我们的任务概念是非常容易上手开发的。那么,nginx可以做的事情,我们都可以做吗?

那是必吁~的!

比如,这周遇到的一个用户场景:

OpenSSL的解密一旦遇到高并发接入,这种密集计算串行执行起来是非常慢的。但是业内一般都有更好的解决方案,以下是其中一种:首先,Intel有个QAT加速卡,可以作为外接设备进行密集计算的提速,因为其用于RSA解密比较多,所以Intel给它做了个对接于OpenSSL异步调度engine;

于是也需要异步操作OpenSSL。它提供了ASYNC_start_job的机制去获取一个job及注册回调函数(在开始后和结束时都会被调起,由使用者根据状态决定做什么行为),然后如果正在IO,状态就会变成PAUSE;并且你可以拿到fd,用你喜欢的方式去监听它。等这个PAUSE状态变回SUCCESS了,会通知你计算完成;

由于存在状态,因此我们需要在应用层保存上下文和监听fd和做具体读写,这就是nginx做的事情:nginx作为一个支持tls的server,Intel为其提供了框架的patch和模块去支持ssl的异步上下文和增删事件到epoll的步骤,具体的nginx模块也实现了读写事件回来之后的回调函数。

于是,nginx可以通过OpenSSL的异步模式调用QAT的加速卡从而实现异步高效高并发的解密计算。其实不止nginx,所有做接入的系统都应该做到这件事,比如阿里的Tengine等,当然也包括我们的Workflow。

这里涉及了三个层次,自底向上介绍一下各个层次吧,然后我们才能知道上层可以依赖下层做什么事情。

二、Intel QAT加速卡与QAT Engine

QAT加速卡全名是QuickAssist Technology,它在硬件层面实现了密集计算的并行优化,按层次看包括具体的设备(device)、内核态的驱动(driver)、用户态调用的库(library)、以及访问这个库的api、最后就是按照OpenSSL的接口封装了一个engine。

这个engine的作用非常重要:动态决定了我们走OpenSSL的某些操作(比如解密)时调用哪个接口,即通过engine我们就可以调用QAT的library提供的api。

简单介绍一下作为一个优秀的engine要实现什么才能加入OpenSSL豪华午餐。可以想象,在事件驱动的世界中,动态engine的原理必然是钩子,我接触过的有以下几类可以作钩子的形态:函数指针(今天讨论的qat_engine openssl nginx都是纯C,所以都是函数指针)

虚函数(要求子类派生并override)

std::function(任何可调用的对象都可以)

要说效率,应该是函数指针占用内存最小、调用效率最高?

说回框架,框架按照场景的全集,提供足够多的钩子,具体engine按自己可以支持的去实现具体钩子函数就好了。

另外值得一提,除了实现钩子函数以外,engine这样的东西还需要管理好自己的生命周期与关注添加钩子函数的方式,列几个常见的接口:

// 仅创建结构体ENGINE *ENGINE_new(void);

// 初始化,或者如果已经被初始化了的话,增加其引用计数int ENGINE_init(ENGINE *e);

/* Add another "ENGINE" type into the array. */ // 相当于注册int ENGINE_add(ENGINE *e);

/* Retrieve an engine from the list by its unique "id" value. */ //按设备寻找engine结构体ENGINE *ENGINE_by_id(const char *id);

然后框架给出全局机制去决定调起哪个engine的哪个钩子,对于OpenSSL来说是配置项来决定的,然后遍历那些注册上了的engine的接口。不得不感慨跟先前做Sogou RPC框架是同理的,万物相通。

# ssl支持配置项的入口函数OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG, NULL);

# 官网给的配置项例子[ engine_section ]

qat = qat_section

[ qat_section ]

engine_id = qat # 这个就是以上ENGINE_by_id()的设备id

dynamic_path = /usr/local/ssl/lib/engines-1.1/qat.so

# Add engine specific messages heredefault_algorithms = ALL

The cryptographic functionality that can be provided by an ENGINE implementation includes the following abstractions;

RSA_METHOD - for providing alternative RSA implementations

DSA_METHOD, DH_METHOD, RAND_METHOD, ECDH_METHOD, ECDSA_METHOD,

- similarly for other OpenSSL APIs

EVP_CIPHER - potentially multiple cipher algorithms (indexed by 'nid')

EVP_DIGEST - potentially multiple hash algorithms (indexed by 'nid')

key-loading - loading public and/or private EVP_PKEY keys

这里我只看了第一项:RSA_METHOD,在qat_engine中大概是这样绑定的:

RSA_METHOD *qat_get_RSA_methods(void)

{

res &= RSA_meth_set_pub_enc(qat_rsa_method, qat_rsa_pub_enc);

res &= RSA_meth_set_pub_dec(qat_rsa_method, qat_rsa_pub_dec);

res &= RSA_meth_set_priv_enc(qat_rsa_method, qat_rsa_priv_enc);

res &= RSA_meth_set_priv_dec(qat_rsa_method, qat_rsa_priv_dec); //以下展开这个为例 res &= RSA_meth_set_mod_exp(qat_rsa_method, qat_rsa_mod_exp);

res &= RSA_meth_set_bn_mod_exp(qat_rsa_method, BN_mod_exp_mont);

res &= RSA_meth_set_init(qat_rsa_method, qat_rsa_init);

res &= RSA_meth_set_finish(qat_rsa_method, qat_rsa_finish);

return qat_rsa_method;

}

看一下具体做解密的函数,截取了主要流程,可以看到为了不同状态的换入换出,程序很碎很复杂。代码在[qat_rsa.c]

// 刚才注册到私钥解密的函数int qat_rsa_priv_dec(int flen, const unsigned char *from,

unsigned char *to, RSA *rsa, int padding)

{

// 调用到具体的解密函数 if (1 != qat_rsa_decrypt(dec_op_data, rsa_len, output_buffer, &fallback)) {

}

}

// 具体实现static int qat_rsa_decrypt(CpaCyRsaDecryptOpData * dec_op_data, int rsa_len,

CpaFlatBuffer * output_buf, int * fallback)

{

do {

// 通过这里去获取下一个handler的id inst_num = get_next_inst_num();

DUMP_RSA_DECRYPT(qat_instance_handles[inst_num], &op_done, dec_op_data, output_buf);

// 这里可以跟据id拿到handler,传入回调函数,并且调用真正的cpa解压函数 sts = cpaCyRsaDecrypt(qat_instance_handles[inst_num], qat_rsaCallbackFn, &op_done,

dec_op_data, output_buf);

if (sts == CPA_STATUS_RETRY) {

if ((qat_wake_job(op_done.job, ASYNC_STATUS_EAGAIN) == 0) ||

(qat_pause_job(op_done.job, ASYNC_STATUS_EAGAIN) == 0)) {

WARN("qat_wake_job or qat_pause_job failed\n");

break;

}

}

}

while (sts == CPA_STATUS_RETRY);

DUMP_RSA_DECRYPT_OUTPUT(output_buf);

QAT_DEC_IN_FLIGHT_REQS(num_requests_in_flight, tlv);

}

然后这里看看[qat_init.c],关于如何找到id和handler的分配:

int get_next_inst_num(void)

{

ENGINE* e = ENGINE_by_id(engine_qat_id);

qat_engine_init(e);

}

int qat_init(ENGINE *e)

{

// handler分配内存 qat_instance_handles =

(CpaInstanceHandle *) OPENSSL_zalloc(((int)qat_num_instances) *

sizeof(CpaInstanceHandle));

// 注册回调函数 if (enable_sw_fallback) {

status = cpaCyInstanceSetNotificationCb(qat_instance_handles[instNum],

qat_instance_notification_callbackFn,

(void *)(intptr_t)instNum);

}

}

最后是这个回调函数格式:

void qat_instance_notification_callbackFn(const CpaInstanceHandle ih, void *callbackTag,

const CpaInstanceEvent inst_ev) { }

以上看了这么多主要是在找Intel关于OpenSSL 1.11.0+和QAT Engine的白皮书上写的CpaRsaDecrypt接口,但是qat_engine源码中并没有找到:

根据刚才看的源码,目前这个接口应该是叫做cpaCyRsaDecrypt(),于是终于打通了关于engine的全部步骤:配置文件里有个engine_id = qat;

ENGINE_by_id()可以通过以上的“qat”拿到具体的engine *;

engine里注册了RSA_METHOD;

具体的qat_rsa_priv_dec函数通过get_next_inst_num()拿到对应的id

根据qat_instance_handler[id]拿到CpaInstanceHandle;

和Cpa相关的具体接口是cpaCyRsaDecrypt(),以传入handler和具体计算结束的回调函数qat_rsaCallbackFn;

整个机制一开始说了用户可以拿到一个fd,而qat就是拥有这个fd的另一端。回调里应该就是给这个eventfd发个信息,以通知计算完毕。

(以上内容越写越细,以至于以下这些点今天都来不及写了,虽然我是个懒癌晚期的爱鸽人士,但是一定会趁热打铁这周内写完~.07.19)

三、OpenSSL 异步模式

四、nginx如何支持OpenSSL async

五、Workflow如何使用QAT加速卡与吊打nginx的性能报告

六、nginx模块开发与workflow上手难易对比(以添加连接上下文为例)

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。