41

分享你的Crypto资源:基于DPDK的Virtio-Crypto运算资源虚拟化方案

 4 years ago
source link: https://www.sdnlab.com/23140.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

作者简介:Zhang, Roy Fan Intel软件工程师,主要从事DPDK开发与虚拟化方面的工作。

本文转载自DPDK与SPSK开源社区

为什么要Virtio-Crypto?

随着近年来互联网,特别是移动互联网的高速发展,用户对数据安全的要求也越来越高。越来越多的网络信息流都被做了加密处理,来防止诸如泄密,仿冒,和重播等类型的网络攻击。Google目前已实现全站HTTPS加密,并在其安全性报告中指出截止2018年7月,超过70%的Chrome数据都已实现加密。

000.jpg

而密保工作如IPSec则需要大量的计算机资源来进行。一个最基本的AES块加密工作也需要数十个回合的查表,移位,线性变换等操作。当今的网络流吞吐量的急剧增加导致加密的工作密度呈几何数量级的增长,这个增长刺激了整个加密加速器技术的发展。Intel最新的采用 C620 系列芯片组 (Purley) 的至强处理器可使用其集成的Intel QuickAssit Technology (QAT)芯片实现超过130Gbps的IPSec数据面处理能力。

与此同时,云计算也有着飞速的增长。云用户对网络安全的需求也随之增加。为了让云用户能享受到加密加速技术,大部分市面上的加密加速器,包括QAT,都使用了Single-root Input/Output (SRIOV) Virtual Function (VF)技术,使得多台VM/容器能共享一个QAT资源,并几乎没有任何性能损失,如图1所示。

001-.jpg

图1:QAT的SR-IOV在VM中的使用

但SR-IOV技术并非没有缺点

1.首先,VM需要安装对应的VF驱动,与硬件有强耦合性。
2.同时,一个PF的VF的数量并非无限,其最大数量限制了能共享该PF的VM/容器 的数量。
3.再次,一个PF的最大吞吐量即为一个VF的最大吞吐量,用户无法获得更高的性能。
4.最后,VF目前并不具备限流功能,更没有QoS加持,而PF的最大吞吐量有限,其中某个VF的吞吐量加大则会制约其他VF,这个限制使得加密加速器的VF技术无法普及在公有云中。

所以,云用户所需要的加密加速器的虚拟化技术需要有和实机硬件无关,可拓展等特点。同时后端(物理机端)能实现聚合多个加速器资源和完成限流,QoS等工作。而这一切的答案就是Virtio-Crypto。

什么是Virtio-Crypto?

Virtio-Crypto是Virtio标准所支持的虚拟设备之一,由前端驱动和后端设备组成。前端一般为虚拟机和容器可访问的Virtio-Crypto设备驱动,而后端则是由物理服务器上的程序如Qemu所模拟的Vhost-Crypto设备。若虚拟机或者容器应用通过访问虚拟Virtio-Crypto设备做加密或验证等操作时,前端的Virtio-Crypto驱动会将其传入到后端并交由物理服务器的Crypto资源来处理。前端的Virtio-Crypto驱动已被整合到Linux内核中,而在后端也已有Qemu的软件Virtio-Crypto设备实现。

002-.jpg

但是,这套方案也有有缺点
1.前端LKCF拖慢性能。因为前端的Virtio-Crypto驱动在内核的Linux Kernel Crypto Framework (LKCF)中,每次加密需要将数据进行2次拷贝,需要若干Callback调用,且不支持Burst操作。这些问题都严重拖慢了前端性能。
2.后端Qemu的Virtio-Crypto设备不支持硬件加速。

因此,目前的这套Virito-Crypto方案的效率太低,为了解决这个问题,我们提出了基于DPDK的Virtio-Crypto前后端的完整解决方案。

基于DPDK的Vhost-User-Crypto后端解决方案

2018年初Qemu被加入了Vhost-User的Crypto Proxy支持,使得除了Qemu之外其他的用户端Vhost-User应用来加速Crypto运算成为了可能。同年5月基于DPDK的Vhost User Crypto面世,成为了第一个支持Crypto设备的Vhost User加速方案。

Vhost Crypto作为DPDK Vhost 库的扩充,提供了几个新的API,并尽力隐藏了许多Vhost及Cryptodev实现的细节。我们将通过一个实例来看看他们怎么工作的。

首先,我们需要初始化Host的Cryptodev资源,包括设备,队列,和创建session mempool等。因为Virtio的特殊性,Host应该使用加密性能更强的Lookaside设备,如QAT。使用软件Cryptodev如AESNI-MB或者OPENSSL的话,因为VIRTIO数据传输的开销其总体性能将弱于VM上直接用软件加密引擎。

对于如何初始化Host的Cryptodev资源请参见本公众号以前的文章,或者是Cryptodev Programmer’s Guide。( https://doc.dpdk.org/guides/prog_guide/cryptodev_lib.html )。

然后,和配置其他Vhost Application一样,我们需要创建一个vhost_device_ops实例,用来注册Socket连接在创建和销毁时的回调函数,分别为new_device和destroy_device。

在new_device()函数中,我们需要做一下几件事。

Java
static int new_device(int vid) { ... /* 首先,我们需要调用rte_vhost_crypto_create()函数,该函数用来初始化Vhost Crypto实例。其中vid为Virtio Device设备号,cryptodev_id为初始化好的DPDKCryptodev设备号,crypto_sess_pool和crypto_sess_priv_pool为用来创建和初始化处理VM加解密任务的session。 在Virtio Crypto标准中,session的信息交换为session_id。而在DPDK Cryptodev中session则为一段具体的数据。因此Vhost Crypto的每一个实例都将维护一个用来映射virtio crypto session_id和DPDK Cryptodev session数据的关系的表,并自动处理VM发送的创建及删除session的请求。用户无需进行任何干预,只需在创建实例时提供Cryptodev ID和sessionmempool即可。 */ rte_vhost_crypto_create(vid, cryptodev_id, crypto_sess_pool, crypto_sess_priv_pool, rte_lcore_to_socket_id(lcore_id); ... /* 然后,我们需要告诉Vhost Crypto是否开启zero copy。开启zero copy可让函数避免在处理virtio cryptodata request时对数据进行拷贝和写回,而仅将request的物理地址进行VM->Host的转换。开启zero copy能大幅提升处理效率。 但该方法有一定的局限:要求HOST系统开启IOMMU,VIOMMU也要开启等,同时不支持Host端使用AESNI-MB PMD,以及不支持该数据内存在物理上不连续。在VM中LKCF对每个设备初始化后会进行可行性检测,其中就包括SCATTER-GATHER LIST的正确性检测。开启zero copy会使VM上的设备驱动无法通过这项检测而导致初始化失败,因此需要HOST端在等待LKCF检测完成之后再开启zero copy.*/ rte_vhost_crypto_set_zero_copy(vid,zero_copy); ... } Vhost Crypto用户应用的数据面处理也非常简单。以下代码为worker lcore执行的代码片段。 static int vhost_crypto_worker(void *arg) { uint16_tinflight = 0; intcallfds[MAX_NB_VIRTIO_CRYPTO_DEVICES]; structrte_crypto_op *ops[burst_size]; ... /* 首先分配一个burst的DPDK Crypto Operation */ rte_crypto_op_bulk_alloc(cop_pool,RTE_CRYPTO_OP_TYPE_SYMMETRIC, ops, burst_size); /* 实际处理VM Crypto Data Request的无限循环 */ for(;;) { uint16_tfetched; // 获得能完整enqueue到Cryptodev中operation数的大小,详见下文 nb_ops =RTE_MIN(burst_size, NB_DESCRIPTORS – inflight */ /* 从virtio queue中获取最大nb_ops个virtio crypto request并转换成DPDK Crypto Operation,并写入ops中,最后返回处理完成的ops数量。*/ fetched = rte_vhost_crypto_fetch_requests(vid,virtio_queue_id, ops,nb_ops); /* 将转换好的ops enqueue到Cryptodev的queue中。*/ inflight+= rte_cryptodev_enqueue_burst(cryptodev_id, queue_id, ops,fetched); /* 一个小贴士:DPDK Cryptodev的queue并非无限大,倘若该qeueue已经饱和,以上操作将不能成功enqueue到fetched个operation。而未能成功enqueue的operation必须妥善保存,以避免VM的crypto request丢失。因此,建议用户记录下初始Cryptodev Queue时指定NB_DESCRIPTORS(即为Queue的大小)。在调用rte_vhost_crypto_fetch_requests()时用其与inflight比较来获得合适的nb_ops大小,以避免fetched太多而不能enqueue的情况发生。记录inflight也可以用来进行更复杂的SLA相关操作,如QoS等 */ /* 将DPDK Cryptodev处理完的Crypto Operation Dequeue出来 */ fetched= rte_cryptodev_dequeue_burst(cryptodev_id, queue_id, ops_dequeue, RTE_MIN(burst_size, inflight)); /* 更新inflight */ inflight-= fetched; /* 因为Cryptodev操作的异步性,dequeue的operation未必为刚刚enqueue的,而且也不一定属于同一个Virtio Device。因此要调用以下函数将处理好的buffer写回(zero copy未开启),并在callfds中记录该burst中所有request的所有Virtio Device ID */ rte_vhost_crypto_finalize_requests(ops_dequeue, fetched,callfds, &nb_callfds); /* 最后,通知每一个Virtio Device,加解密工作完成 */ for(i = 0; i < nb_callfds; i++) eventfd_write(callfds[i],(eventfd_t)1); }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
staticint
new_device(intvid){
...
/*    首先,我们需要调用rte_vhost_crypto_create()函数,该函数用来初始化Vhost Crypto实例。其中vid为Virtio Device设备号,cryptodev_id为初始化好的DPDKCryptodev设备号,crypto_sess_pool和crypto_sess_priv_pool为用来创建和初始化处理VM加解密任务的session。
在Virtio Crypto标准中,session的信息交换为session_id。而在DPDK Cryptodev中session则为一段具体的数据。因此Vhost Crypto的每一个实例都将维护一个用来映射virtio crypto session_id和DPDK Cryptodev session数据的关系的表,并自动处理VM发送的创建及删除session的请求。用户无需进行任何干预,只需在创建实例时提供Cryptodev ID和sessionmempool即可。
*/
rte_vhost_crypto_create(vid,
cryptodev_id,
crypto_sess_pool,
crypto_sess_priv_pool,
rte_lcore_to_socket_id(lcore_id);
...
/*    然后,我们需要告诉Vhost Crypto是否开启zero copy。开启zero copy可让函数避免在处理virtio cryptodata request时对数据进行拷贝和写回,而仅将request的物理地址进行VM->Host的转换。开启zero copy能大幅提升处理效率。
但该方法有一定的局限:要求HOST系统开启IOMMU,VIOMMU也要开启等,同时不支持Host端使用AESNI-MB PMD,以及不支持该数据内存在物理上不连续。在VM中LKCF对每个设备初始化后会进行可行性检测,其中就包括SCATTER-GATHER LIST的正确性检测。开启zero copy会使VM上的设备驱动无法通过这项检测而导致初始化失败,因此需要HOST端在等待LKCF检测完成之后再开启zero copy.*/
rte_vhost_crypto_set_zero_copy(vid,zero_copy);
...
}
Vhost Crypto用户应用的数据面处理也非常简单。以下代码为worker lcore执行的代码片段。
staticint
vhost_crypto_worker(void*arg){
    uint16_tinflight=0;
    intcallfds[MAX_NB_VIRTIO_CRYPTO_DEVICES];
    structrte_crypto_op*ops[burst_size];
...
    /*  首先分配一个burst的DPDK Crypto Operation */
    rte_crypto_op_bulk_alloc(cop_pool,RTE_CRYPTO_OP_TYPE_SYMMETRIC,ops,
           burst_size);
  
    /*  实际处理VM Crypto Data Request的无限循环 */
    for(;;){
       uint16_tfetched;
//    获得能完整enqueue到Cryptodev中operation数的大小,详见下文 nb_ops =RTE_MIN(burst_size, NB_DESCRIPTORS – inflight */
/*    从virtio queue中获取最大nb_ops个virtio crypto request并转换成DPDK Crypto Operation,并写入ops中,最后返回处理完成的ops数量。*/
fetched=rte_vhost_crypto_fetch_requests(vid,virtio_queue_id,
ops,nb_ops);
      
/*    将转换好的ops enqueue到Cryptodev的queue中。*/
       inflight+=rte_cryptodev_enqueue_burst(cryptodev_id,queue_id,
           ops,fetched);
/*    一个小贴士:DPDK Cryptodev的queue并非无限大,倘若该qeueue已经饱和,以上操作将不能成功enqueue到fetched个operation。而未能成功enqueue的operation必须妥善保存,以避免VM的crypto request丢失。因此,建议用户记录下初始Cryptodev Queue时指定NB_DESCRIPTORS(即为Queue的大小)。在调用rte_vhost_crypto_fetch_requests()时用其与inflight比较来获得合适的nb_ops大小,以避免fetched太多而不能enqueue的情况发生。记录inflight也可以用来进行更复杂的SLA相关操作,如QoS等 */
/*    将DPDK Cryptodev处理完的Crypto Operation Dequeue出来 */
fetched=rte_cryptodev_dequeue_burst(cryptodev_id,queue_id,
      ops_dequeue,RTE_MIN(burst_size,inflight));
/*    更新inflight */
inflight-=fetched;
/*    因为Cryptodev操作的异步性,dequeue的operation未必为刚刚enqueue的,而且也不一定属于同一个Virtio Device。因此要调用以下函数将处理好的buffer写回(zero copy未开启),并在callfds中记录该burst中所有request的所有Virtio Device ID */
       rte_vhost_crypto_finalize_requests(ops_dequeue,
fetched,callfds,&nb_callfds);
      
       /*  最后,通知每一个Virtio Device,加解密工作完成 */
       for(i=0;i<nb_callfds;i++)
           eventfd_write(callfds[i],(eventfd_t)1);
}

Vhost Crypto的API具有易用,前后端解耦,扩展性高等优点。使用ZERO COPY也能大幅提升性能。可以很容易地整合到复杂的虚拟Crypto资源服务的应用中。然而,由于Virtio Crypto的LINUX驱动及QEMU PROXY的局限性,目前仅支持AES-CBC和SHA1-MAC算法。随着未来支持算法的增加,Vhost-Crypto也会不断发展壮大。

基于DPDK的Virtio-User-Crypto前端轮询驱动(PMD)

为解决LKCF的性能拖慢问题,我们在DPDK的Cryptodev Framework中加入了基于Virtio User的Virtio-Crypto PMD。关于DPDK Cryptodev Framework的介绍已在曾经的公众号文章中详细介绍过,这里就不在赘述了。该PMD和其他的DPDK Crypto PMD共享相同的控制面和数据面API,区别是其工作在虚拟机或者容器中。

要使用它,我们首先需要将Vhost Crypto应用启动并使其创建UNIX socket file,并将其有Qemu传递给VM。QEMU命令范例如下。

Java
qemu-system-x86_64 \ [...] \ -chardevsocket,id=charcrypto0,path=/path/to/your/socket \ -objectcryptodev-vhost-user,id=cryptodev0,chardev=charcrypto0 \ -devicevirtio-crypto-pci,id=crypto0,cryptodev=cryptodev0 [...]
1
2
3
4
5
6
qemu-system-x86_64\
[...]\
    -chardevsocket,id=charcrypto0,path=/path/to/your/socket\
    -objectcryptodev-vhost-user,id=cryptodev0,chardev=charcrypto0\
    -devicevirtio-crypto-pci,id=crypto0,cryptodev=cryptodev0
[...]

在VM中,该设备将被Linux Kernel默认绑定为一个Virtio Crypto设备。我们需要将QEMU传递上来的virtio设备绑定为UIO-PCI-GENERIC驱动。假设该设备拥有0000:00:04.0的PCI地址,我们可通过如下命令来将其改绑定。

Java
modprobe uio_pci_genericecho -n 0000:00:04.0 > /sys/bus/pci/drivers/virtio-pci/unbindecho "1af4 1054" > /sys/bus/pci/drivers/uio_pci_generic/new_id
1
modprobe uio_pci_genericecho-n0000:00:04.0>/sys/bus/pci/drivers/virtio-pci/unbindecho"1af4 1054">/sys/bus/pci/drivers/uio_pci_generic/new_id

然后,我们就能将其像其他Cryptodev PMD一样在DPDK中使用,并能享受HOST端的加速了。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK