[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模块框架分析
——
第一部分完