1. 前言 最近看了流密码算法的Crypto PMD,稍微总结一下。
2. 介绍 Snow3g/Zuc PMD都是基于intel-ipsec-mb库实现的库。许多数据和操作都是公共的,代码都位于dpdk/drivers/crypto/ipsec_mb目录下。
intel-ipsec-mb库是为包处理提供的一个软件加密库,可以用在IPsec, TLS, Wireless等应用中。
ipsec_mb下还有多个pmd,对应不同的算法。不同pmd类型相关的数据会保存到ipsec_mb_pmds变量中,公共函数根据当前的pmd类型获取对应pmd的数据和函数处理数据。实现不同pmd的分离。PMD类型在PMD初始化时保存到dev的dev_private区域。
3. ZUC PMD ZUC PMD的主要函数整理如下:
下面描述一下主要的功能实现:
3.1 PMD操作回调及数据设置 ipsec_mb_register_zuc函数在程序启动时设置ZUC PMD相关的配置,保存到全局数组变量ipsec_mb_pmds中。
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 RTE_INIT(ipsec_mb_register_zuc) { struct ipsec_mb_internals *zuc_data = &ipsec_mb_pmds[IPSEC_MB_PMD_TYPE_ZUC]; zuc_data->caps = zuc_capabilities; zuc_data->dequeue_burst = zuc_pmd_dequeue_burst; zuc_data->feature_flags = RTE_CRYPTODEV_FF_SYMMETRIC_CRYPTO | RTE_CRYPTODEV_FF_SYM_OPERATION_CHAINING | RTE_CRYPTODEV_FF_NON_BYTE_ALIGNED_DATA | RTE_CRYPTODEV_FF_OOP_SGL_IN_LB_OUT | RTE_CRYPTODEV_FF_SYM_SESSIONLESS | RTE_CRYPTODEV_FF_OOP_LB_IN_LB_OUT; zuc_data->internals_priv_size = 0 ; zuc_data->ops = &zuc_pmd_ops; zuc_data->qp_priv_size = sizeof (struct zuc_qp_data); zuc_data->session_configure = zuc_session_configure; zuc_data->session_priv_size = sizeof (struct zuc_session); } #define RTE_PRIO(prio) \ RTE_PRIORITY_ ## prio #ifndef RTE_INIT_PRIO #define RTE_INIT_PRIO(func, prio) \ static void __attribute__((constructor(RTE_PRIO(prio)), used)) func(void) #endif #define RTE_INIT(func) \ RTE_INIT_PRIO(func, LAST)
RTE_INIT使用的是编译器的constructor属性,使得该函数在main函数运行之前就调用,类似于静态执行的一种机制。
3.2 设备驱动发现及卸载 上一篇文章中讲过,PMD会调用RTE_PMD_REGISTER_VDEV注册虚拟设备到DPDK框架中,在应用启动之后主动把驱动的发现和卸载的接口注册上去,也是调用的RTE_INIT实现的。
ZUC PMD设备驱动发现及卸载函数分别调用ipsec_mb_create、ipsec_mb_remove。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 static int zuc_probe (struct rte_vdev_device *vdev) { return ipsec_mb_create(vdev, IPSEC_MB_PMD_TYPE_ZUC); } static struct rte_vdev_driver cryptodev_zuc_pmd_drv = { .probe = zuc_probe, .remove = ipsec_mb_remove }; static struct cryptodev_driver zuc_crypto_drv ;RTE_PMD_REGISTER_VDEV(CRYPTODEV_NAME_ZUC_PMD, cryptodev_zuc_pmd_drv);
设备发现实现函数ipsec_mb_create,主要内容:
1)获取pmd操作函数及数据 2)解析参数,创建pmd 3)在dev的私有数据部分,保存pmd类型及队列数目配置 4)获取驱动id, 驱动id在注册时确定,跟设备id不一样,驱动存放在cryptodev_driver_list 5)设置出入队函数、特性等数据到dev
设备卸载实现函数ipsec_mb_remove
释放队列对资源
释放pmd
3.3 能力定义 ZUC PMD顾名思义就是只支持ZUC算法了。
3.4 队列对配置 实现函数为ipsec_mb_qp_setup: 1)为dev->data->queue_pairs[qp_id]申请空间,并初始化相关内容。 2)保存mp_pool, mp_pool_private。 3)创建队列对的软件队列rte_ring 4)队列对相关的统计数据结构初始化
3.5 Session处理 rte_cryptodev_sym_session_create从队列对对应的mp_pool申请一个rte_cryptodev_sym_session。调用rte_cryptodev_sym_session_init初始化,从mp_pool_private申请PMD的私有session结构,调用sys_session_configure把xform数据转换为私有的session数据。
看了许多DPDK的示例代码,默认一个vdev申请一个session, 每个session对应一种算法,也就对应到一个chain类型(CIPHER, AUTH, CIPHER_AUTH, AUTH_CIPHER和AEAD)。在出队入队时,同一批的ops,默认会是同一个session的,当然,具体看那个应用的实现了,也是可以支持不同算法类型的ops的,框架并没有对此限定。
ZUC pmd实现函数zuc_session_configure: 1)解析xform链,获取operation对应的类型及相关的cipher_xform, auth_xform, aead_xform 2)从xform中获取数据信息填充到私有session结构
3.6 Operation管理 用户根据情况自行创建op_mpool,调用rte_crypto_op_pool_create创建,op_mpool池分为对称和非对称两种类型。后面可以追加用户自定义的数据。 struct rte_mempool * rte_crypto_op_pool_create(const char *name, enum rte_crypto_op_type type, unsigned nb_elts, unsigned cache_size, uint16_t priv_size, int socket_id)
大致结构如下:
然后rte_crypto_op_alloc申请对应的operation,把session跟operation关联起来,把数据相关的信息填充到operation中。 一般把iv放到priv data区域。
3.7 入队出队 入队统一使用公共的ipsec_mb_enqueue_burst,实现比较简单,直接把相关的ops添加到ingress_queue中,增加入队计数。 出队列实现函数zuc_pmd_dequeue_burst:
从ingress_queue获取nu_ops个ops
获取ops管理的session数据, 得到ops对应的算法类型。
获取相同算法类型的ops集中处理,最大16个。前N个类型相同,则最多处理16个,如果N小于16,则先处理N个,再处理下一种类型数据。
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 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 static uint16_t zuc_pmd_dequeue_burst (void *queue_pair, struct rte_crypto_op **c_ops, uint16_t nb_ops) { struct rte_crypto_op *curr_c_op ; struct zuc_session *curr_sess ; struct zuc_session *sessions [ZUC_MAX_BURST ]; struct rte_crypto_op *int_c_ops [ZUC_MAX_BURST ]; enum ipsec_mb_operation prev_zuc_op = IPSEC_MB_OP_NOT_SUPPORTED; enum ipsec_mb_operation curr_zuc_op ; struct ipsec_mb_qp *qp = queue_pair; unsigned int nb_dequeued; unsigned int i; uint8_t burst_size = 0 ; uint8_t processed_ops; nb_dequeued = rte_ring_dequeue_burst(qp->ingress_queue, (void **)c_ops, nb_ops, NULL ); for (i = 0 ; i < nb_dequeued; i++) { curr_c_op = c_ops[i]; curr_sess = (struct zuc_session *) ipsec_mb_get_session_private(qp, curr_c_op); if (unlikely(curr_sess == NULL )) { curr_c_op->status = RTE_CRYPTO_OP_STATUS_INVALID_SESSION; break ; } curr_zuc_op = curr_sess->op; if (burst_size == 0 ) { prev_zuc_op = curr_zuc_op; int_c_ops[0 ] = curr_c_op; sessions[0 ] = curr_sess; burst_size++; } else if (curr_zuc_op == prev_zuc_op) { int_c_ops[burst_size] = curr_c_op; sessions[burst_size] = curr_sess; burst_size++; if (burst_size == ZUC_MAX_BURST) { processed_ops = process_ops(int_c_ops, curr_zuc_op, sessions, qp, burst_size); if (processed_ops < burst_size) { burst_size = 0 ; break ; } burst_size = 0 ; } } else { processed_ops = process_ops(int_c_ops, prev_zuc_op, sessions, qp, burst_size); if (processed_ops < burst_size) { burst_size = 0 ; break ; } burst_size = 0 ; prev_zuc_op = curr_zuc_op; int_c_ops[0 ] = curr_c_op; sessions[0 ] = curr_sess; burst_size++; } } if (burst_size != 0 ) { processed_ops = process_ops(int_c_ops, prev_zuc_op, sessions, qp, burst_size); } qp->stats.dequeued_count += i; return i; }
3.8 Session池创建 每个numa节点分别分配一个mp_pool和一个mp_pool_private。 mp_pool的成员为rte_cryptodev_sym_session, 元素个数为设备数目乘以队列数,也就是让每个队列都能分配到一个session。 mp_pool使用接口rte_cryptodev_sym_session_pool_create创建
struct rte_mempool * rte_cryptodev_sym_session_pool_create(const char *name, uint32_t nb_elts, uint32_t elt_size, uint32_t cache_size, uint16_t user_data_size, int socket_id)
mp_pool的元素为rte_cryptodev_sym_session
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 struct rte_cryptodev_sym_session { uint64_t opaque_data; uint16_t nb_drivers; uint16_t user_data_sz; __extension__ struct { void *data; uint16_t refcnt; } sess_data[]; };
根据代码注释,我们可以确定每个session对应固定的算法, key,操作类型等数据。 mp_pool_priveate的则是PMD定义的私有数据,大小通过PMD提供的sym_session_get_size接口获取。mp_pool_private使用rte_mempool_create创建。 这两个pool池会被设置到队列对的配置中去。调用rte_cryptodev_queue_pair_setup进行配置。
PMD初始化的时候会保存socket_id到dev结构中。
更多 大概梳理了一下ZUC PMD的接口,及其app调用的流程中的部分功能实现。
行动,才不会被动!
欢迎关注个人公众号 微信 -> 搜索 -> fishmwei,沟通交流。
博客地址: https://fishmwei.github.io
掘金主页: https://juejin.cn/user/2084329776486919