[openssl] intel qat场景下的openssl框架
源代码
[classic_tong:https://www.cnblogs.com/hugetong/p/14363775.html]
我们使用openssl版本1.1.1的源代码进行安装与实验. 目前已经有了3.0.0的alpha版本.
源码下载在这里: https://www.openssl.org/source/
git在这里:git@github.com:openssl/openssl.git
编译与安装
使用如下命令即可方便的完成编译与安装
./Configure --prefix=/root/debug/ LDFLAGS="-Wl,-rpath,/root/debug/lib" linux-x86_64 make make install
如下是安装后的交付物.
[root@T9 OUTPUT_openssl_bare]# tree -L 3 . ├── bin │ ├── c_rehash │ └── openssl ├── certs ├── ct_log_list.cnf ├── ct_log_list.cnf.dist ├── include │ └── openssl │ ├── ... │ └── x509_vfy.h ├── lib │ ├── engines-1.1 │ │ ├── capi.so │ │ └── padlock.so │ ├── libcrypto.a │ ├── libcrypto.so -> libcrypto.so.1.1 │ ├── libcrypto.so.1.1 │ ├── libssl.a │ ├── libssl.so -> libssl.so.1.1 │ ├── libssl.so.1.1 │ └── pkgconfig │ ├── libcrypto.pc │ ├── libssl.pc │ └── openssl.pc ├── misc │ ├── CA.pl │ ├── tsget -> tsget.pl │ └── tsget.pl ├── openssl.cnf ├── openssl.cnf.dist ├── private └── share ├── doc │ └── openssl └── man ├── ... └── man7 17 directories, 124 files
openssl框架
逻辑结构
在这里,我们偷两张官网的图, 然后通过这两张图进行更直观的讲解.
两个图通过不同的维度表达了同样的事情, 我们这里只关注左图, 右图用来自学补充.
通过左图,我们能看见openssl由四组件构成, 分别是APP, TLS, CRYPTO, ENGINE
ENGINE是提供加密能力的一组引擎, 插件式整合在框架内. 在使用中可以对不同的算法指定不同的加密引擎.
CRYPTO是加密组件的的集合, 对加密逻辑的抽象, 使用ENGINE中的能力完成密码功能, 其中两个重要的核心组件分别是EVP(envelope)和BIO(BASIC INPUT OUTPUT)
TLS表现为一组API用于操作SSL socket. TLS库是对CRYPTO的封装,使用EVP接口, EVP模块使用BIO接口.
APP就是一组命令行工具, 他们被集成在二进制文件/bin/openssl中, 可以通过不同的参数进行调用. APP主要是对TLS与CRYPTO的使用.
物理结构
物理结构并不准确, 实际上就是想说一下交付物的结构, 见前面的第一小节, 剔除不重要的(我们不关心的)之后, 是如下这样:
├── bin │ ├── c_rehash │ └── openssl ├── lib │ ├── engines-1.1 │ │ ├── capi.so │ │ └── padlock.so │ ├── libcrypto.a │ ├── libcrypto.so -> libcrypto.so.1.1 │ ├── libcrypto.so.1.1 │ ├── libssl.a │ ├── libssl.so -> libssl.so.1.1 │ └── libssl.so.1.1 ├── openssl.cnf ├── private └── certs
现在对应上前文讲到的逻辑结构, bin目录下的openssl就是component APP, lib目录下面的libcrypto是component CRYPTO, libssl是component SSL, lib/engines-1.1目录下面的所有so文件都是一个engine,
engines是在运行是dlopen动态加载上去的, 下文提到的qatengine在安装之后, 也会放置在这个目录下面以供openssl使用.
private和certs是两个给app使用的两个空目录, 分别用于存放用户的私钥和证书文件. 我们在讨论结构话题的当前, 可以不去关心它.
最后一个, 十分重要. 是配置文件. openssl.cnf, 这个文件是openssl的默认配置文件, 前文提到的所有使用openssl的APP都被这个配置文件控制,
另外一个需要注意的是以库的形式使用openssl的应用程序, 也可以通过api的方式, 让openssl被该配置文件的内容约束行为, 比如nginx程序便采用了这样的设置, 这个API是这样的:
OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, NULL) // 使用ssl的应用程序 OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG, &settings) // 使用crypto的应用程序
第二个参数如果指定了NULL将使用默认配置文件, 否则使用被设置的内容.
qat场景下openssl的框架
Intel的QAT卡, 推荐了一个典型的应用场景, 并且开源了代码, 这组代码包含四部分,
1. nginx的patch,https://github.com/intel/asynch_mode_nginx
2. openssl新增的async mode,已经合并到了openssl的主分支。
3 一个openssl engine叫qatengine 详见: https://github.com/intel/QAT_Engine
4 一个跟随硬件发布的驱动程序,可以在intel网站下载
qatengine安装成功后, 在前文提到的目录下将能够看见它的so: openssl/lib/engines-1.1/qat.so
如上图所示, QAT卡就是一个加解密卡, 加解密卡的一般操作模型是: 1, 把待处理数据通过接口api写入卡, 2, 卡处理. 3, 将处理好的数据在加解密卡中通过接口api读出.
这个接口api使用qatdriver提供的, 如上图, 符合一般的设备驱动模型分为内核态和用户态两部分. (可以从intel的网站进行下载, 这里是所以资料的入口:https://01.org/intel-quickassist-technology )
从框架体系上, nginx或其他任何使用openssl进行加解密操作的应用程序只需要保持与原来同样的方式对openssl进行调用. 如果它需要使用QAT卡
进行加解密加速, 可以通过openssl的api将需要的算法指定到engine QAT上去. 在这之后 openssl 将会dlopen将qat.so动态加载进应用程序.
至此应用程序便可以通过intel的qat卡进行加解密加速操作了. 不需要任何额外修改(但是并不能获得最佳性能,最大效率)。
qatdriver
(由于时间关系, 该小节内容的理解可能不准确并且包含错误)
1. 从功能上,
QAT提供加解密与压缩解压功能, 其中加解密包含对称与非对称两类, QAT卡的优势主要体现在非对称加密上.
2. 从结构上,
每张QAT卡包含32个独立的BANK, 每个BANK最多支持配置2个加解密或压缩服务. 在BANK的上一层是instance, 大概bank数乘2就是instance总数,
根据配置不同,如果poll, epoll以及功能选择是加解密还是压缩, instance的总数上限也有所不同,. 详见文档<<Programmer's Guide>> (另外,我也没太搞懂.
3. 从代码上,
可以分为三块,两个内核模块, 一个用户态模块. 因为QAT卡要支持用户态使用同样也要支持kernel里的加解密框架使用, 所以各有一个模块, 剩下的一个内核模块
是用来分配连续物理内存的. 用户态的模块使用UIO与连续内存直接与硬件通信, 绕过了内核访问的开销.
qatengine
如前所述, qatengine的主要作用是将“加解密操作的输入输出数据”在用户应用程序与硬件卡之间进行传递, 主要操作就是IO的读写.
然后我们回顾这个模型, 在IO的写入与读取之间有一点时间的等待(我们也叫它IO阻塞,或者同步IO), 等待硬件卡完成操作, 这个等待与通知的处理过程, qatengine将其封装为如下几种方式.
(那是因为,qat卡是没有通知机制的(对比其他硬件就有通知机制,比如网卡硬中断。另外我也不太肯定qat卡是不是真的不能中断。但是我们假设它没有中断并无大碍,因为我要引出的内容
是这个假装之后的一个推论),但是被Engine封装之后就有了通知。是因为它用轮训的方式伪装成了通知。轮休发现硬件卡计算完成时,写一个eventfd形成通知机制。下面提到的这四个方式,
指的就是这个轮休方式。)
ENABLE_INLINE_POLLING
SET_INTERNAL_POLL_INTERVAL
ENABLE_EXTERNAL_POLLING
ENABLE_HEURISTIC_POLLING
参考文档:
https://github.com/intel/asynch_mode_nginx#support-for-nginx-side-polling
代码:
https://github.com/intel/asynch_mode_nginx/blob/master/modules/nginx_qat_module/ngx_ssl_engine_qat_module.c::ngx_ssl_engine_qat_send_ctrl()
https://github.com/intel/QAT_Engine/blob/master/e_qat.c::qat_cmd_defns[]
代码中有四种poll方式, 文档中只推荐了externel和heuristic两种, 这两张都是基于timer的poll。下面我要提到的是另一种,比较特殊的inline poll模式,ENABLE_INLINE_POLLING。
这种模式,是单独启动一个polling线程进行轮训的,他的特殊在于这里边可能会有一些多线程同步读写的问题,不再展开。
引出了另一个我怀疑它可能存在的bug。提交在了issue里,地址如下:https://github.com/intel/QAT_Engine/issues/178
性能
使用qat卡的本质需求是性能, 是解放CPU, 让一部分原本由CPU进行的计算转移到qat卡上进行, 在这段时间内CPU可以用来进行其他的计算工作.
所以硬件只是基础, 提高QAT的利用率, 降低CPU的切换开销和等待时间是性能最大化的核心工作.
这个问题, 有两个层面.
1 多个SSL连接的并行.
对qat卡的写->读操作被封装在了SSL的read或write的API内, 于是SSL的qat IO就变成了阻塞IO, 可以使用epoll将该IO变成异步IO, 主要这里epoll的是qat的fd, 与网络socket的fd是不同的.
需要区分理解, 要想将SSL的接口变成彻底的IO异步的需要epoll socket fd的同时也epoll qat的fd. 如下图所示:
2 单SSL连接内多个加解密操作之间的并行.
这部分的细节讨论详见: [openssl] openssl async模块框架分析
------
第一部分完