RE: [EXT] Re: [PATCH v4 21/22] pdcp: add thread safe processing
Hi Stephen, I've dropped this patch for now. Based on what we conclude on this thread, will post this as a separate patch as required. Thanks, Anoob > -Original Message- > From: Anoob Joseph > Sent: Saturday, May 27, 2023 10:55 AM > To: Stephen Hemminger > Cc: Thomas Monjalon ; Akhil Goyal > ; Jerin Jacob Kollanukkaran ; > Konstantin Ananyev ; Volodymyr Fialko > ; Hemant Agrawal ; > Mattias Rönnblom ; Kiran Kumar > Kokkilagadda ; dev@dpdk.org; Olivier Matz > > Subject: RE: [EXT] Re: [PATCH v4 21/22] pdcp: add thread safe processing > > Hi Stephen, > > Please see inline. > > Thanks, > Anoob > > > -Original Message- > > From: Stephen Hemminger > > Sent: Saturday, May 27, 2023 3:42 AM > > To: Anoob Joseph > > Cc: Thomas Monjalon ; Akhil Goyal > > ; Jerin Jacob Kollanukkaran ; > > Konstantin Ananyev ; Volodymyr > Fialko > > ; Hemant Agrawal ; > > Mattias Rönnblom ; Kiran Kumar > > Kokkilagadda ; dev@dpdk.org; Olivier Matz > > > > Subject: [EXT] Re: [PATCH v4 21/22] pdcp: add thread safe processing > > > > External Email > > > > -- > > On Sat, 27 May 2023 02:32:13 +0530 > > Anoob Joseph wrote: > > > > > +static inline uint32_t > > > +pdcp_atomic_inc(uint32_t *val, const bool mt_safe) { > > > + if (mt_safe) > > > + return __atomic_fetch_add(val, 1, __ATOMIC_RELAXED); > > > + else > > > + return (*val)++; > > > +} > > > > This is a bad pattern. None of the rest of DPDK does this. > > Either be thread safe or not. > > [Anoob] Most protocol implementation would have a similar issue. I've tried > to follow the approach taken in lib IPsec (please check > lib/ipsec/ipsec_sqn.h). > From the discussion we had on v3, I've tried to make it compile time constant > to remove any conditional checks in datapath. > > If you still think, this is an issue, I could drop this patch for now and re- > introduce it later once we have test applications that can work with traffic. > Please share your thoughts.
[PATCH v5 01/21] net: add PDCP header
From: Volodymyr Fialko Add PDCP protocol header to be used for supporting PDCP protocol processing. Signed-off-by: Anoob Joseph Signed-off-by: Kiran Kumar K Signed-off-by: Volodymyr Fialko Acked-by: Akhil Goyal --- doc/api/doxy-api-index.md | 3 +- lib/net/meson.build | 1 + lib/net/rte_pdcp_hdr.h| 147 ++ 3 files changed, 150 insertions(+), 1 deletion(-) create mode 100644 lib/net/rte_pdcp_hdr.h diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md index c709fd48ad..debbe4134f 100644 --- a/doc/api/doxy-api-index.md +++ b/doc/api/doxy-api-index.md @@ -127,7 +127,8 @@ The public API headers are grouped by topics: [Geneve](@ref rte_geneve.h), [eCPRI](@ref rte_ecpri.h), [L2TPv2](@ref rte_l2tpv2.h), - [PPP](@ref rte_ppp.h) + [PPP](@ref rte_ppp.h), + [PDCP hdr](@ref rte_pdcp_hdr.h) - **QoS**: [metering](@ref rte_meter.h), diff --git a/lib/net/meson.build b/lib/net/meson.build index 379d161ee0..bd56f91c22 100644 --- a/lib/net/meson.build +++ b/lib/net/meson.build @@ -22,6 +22,7 @@ headers = files( 'rte_geneve.h', 'rte_l2tpv2.h', 'rte_ppp.h', +'rte_pdcp_hdr.h', ) sources = files( diff --git a/lib/net/rte_pdcp_hdr.h b/lib/net/rte_pdcp_hdr.h new file mode 100644 index 00..72ae9a66cb --- /dev/null +++ b/lib/net/rte_pdcp_hdr.h @@ -0,0 +1,147 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(C) 2023 Marvell. + */ + +#ifndef RTE_PDCP_HDR_H +#define RTE_PDCP_HDR_H + +/** + * @file + * + * PDCP-related defines + * + * Based on - ETSI TS 138 323 V17.1.0 (2022-08) + * https://www.etsi.org/deliver/etsi_ts/138300_138399/138323/17.01.00_60/ts_138323v170100p.pdf + */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * 4.3.1 + * + * Indicate the maximum supported size of a PDCP Control PDU. + */ +#define RTE_PDCP_CTRL_PDU_SIZE_MAX 9000u + +/** + * 6.3.4 MAC-I + * + * Indicate the size of MAC-I in PDCP PDU. + */ +#define RTE_PDCP_MAC_I_LEN 4 + +/** + * Indicate type of control information included in the corresponding PDCP + * Control PDU. + */ +enum rte_pdcp_ctrl_pdu_type { + RTE_PDCP_CTRL_PDU_TYPE_STATUS_REPORT = 0, + RTE_PDCP_CTRL_PDU_TYPE_ROHC_FEEDBACK = 1, + RTE_PDCP_CTRL_PDU_TYPE_EHC_FEEDBACK = 2, + RTE_PDCP_CRTL_PDU_TYPE_UDC_FEEDBACK = 3, +}; + +/** + * 6.3.7 D/C + * + * This field indicates whether the corresponding PDCP PDU is a + * PDCP Data PDU or a PDCP Control PDU. + */ +enum rte_pdcp_pdu_type { + RTE_PDCP_PDU_TYPE_CTRL = 0, + RTE_PDCP_PDU_TYPE_DATA = 1, +}; + +/** + * 6.2.2.1 Data PDU for SRBs + */ +__extension__ +struct rte_pdcp_cp_data_pdu_sn_12_hdr { +#if RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN + uint8_t sn_11_8 : 4;/**< Sequence number bits 8-11 */ + uint8_t r : 4; /**< Reserved */ +#elif RTE_BYTE_ORDER == RTE_BIG_ENDIAN + uint8_t r : 4; /**< Reserved */ + uint8_t sn_11_8 : 4;/**< Sequence number bits 8-11 */ +#endif + uint8_t sn_7_0; /**< Sequence number bits 0-7 */ +} __rte_packed; + +/** + * 6.2.2.2 Data PDU for DRBs and MRBs with 12 bits PDCP SN + */ +__extension__ +struct rte_pdcp_up_data_pdu_sn_12_hdr { +#if RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN + uint8_t sn_11_8 : 4;/**< Sequence number bits 8-11 */ + uint8_t r : 3; /**< Reserved */ + uint8_t d_c : 1;/**< D/C bit */ +#elif RTE_BYTE_ORDER == RTE_BIG_ENDIAN + uint8_t d_c : 1;/**< D/C bit */ + uint8_t r : 3; /**< Reserved */ + uint8_t sn_11_8 : 4;/**< Sequence number bits 8-11 */ +#endif + uint8_t sn_7_0; /**< Sequence number bits 0-7 */ +} __rte_packed; + +/** + * 6.2.2.3 Data PDU for DRBs and MRBs with 18 bits PDCP SN + */ +__extension__ +struct rte_pdcp_up_data_pdu_sn_18_hdr { +#if RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN + uint8_t sn_17_16 : 2; /**< Sequence number bits 16-17 */ + uint8_t r : 5; /**< Reserved */ + uint8_t d_c : 1;/**< D/C bit */ +#elif RTE_BYTE_ORDER == RTE_BIG_ENDIAN + uint8_t d_c : 1;/**< D/C bit */ + uint8_t r : 5; /**< Reserved */ + uint8_t sn_17_16 : 2; /**< Sequence number bits 16-17 */ +#endif + uint8_t sn_15_8;/**< Sequence number bits 8-15 */ + uint8_t sn_7_0; /**< Sequence number bits 0-7 */ +} __rte_packed; + +/** + * 6.2.3.1 Control PDU for PDCP status report + */ +__extension__ +struct rte_pdcp_up_ctrl_pdu_hdr { +#if RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN + uint8_t r : 4; /**< Reserved */ + uint8_t pdu_type : 3; /**< Control PDU type */ + uint8_t d_c : 1;/**< D/C bit */ +#elif RTE_BYTE_ORDER == RTE_BIG_ENDIAN + uint8_t d_c : 1;/**< D/C bit */ + uint8_t pdu_type : 3; /**< Control PDU type */ + uint8_t r : 4; /**< Reserved */ +#endif + /** +* 6.3.9 FMC +* +* First Missing COUNT. This fiel
[PATCH v5 04/21] pdcp: add packet group
Crypto processing in PDCP is performed asynchronously by rte_cryptodev_enqueue_burst() and rte_cryptodev_dequeue_burst(). Since cryptodev dequeue can return crypto operations belonging to multiple entities, rte_pdcp_pkt_crypto_group() is added to help grouping crypto operations belonging to same entity. Signed-off-by: Anoob Joseph Signed-off-by: Kiran Kumar K Signed-off-by: Volodymyr Fialko --- lib/pdcp/meson.build | 1 + lib/pdcp/rte_pdcp.h | 6 ++ lib/pdcp/rte_pdcp_group.h | 131 ++ lib/pdcp/version.map | 3 + 4 files changed, 141 insertions(+) create mode 100644 lib/pdcp/rte_pdcp_group.h diff --git a/lib/pdcp/meson.build b/lib/pdcp/meson.build index ccaf426240..08679b743a 100644 --- a/lib/pdcp/meson.build +++ b/lib/pdcp/meson.build @@ -13,5 +13,6 @@ sources = files( 'rte_pdcp.c', ) headers = files('rte_pdcp.h') +indirect_headers += files('rte_pdcp_group.h') deps += ['mbuf', 'net', 'cryptodev', 'security'] diff --git a/lib/pdcp/rte_pdcp.h b/lib/pdcp/rte_pdcp.h index 46c3c2a416..b88cad4f64 100644 --- a/lib/pdcp/rte_pdcp.h +++ b/lib/pdcp/rte_pdcp.h @@ -257,6 +257,12 @@ rte_pdcp_pkt_post_process(const struct rte_pdcp_entity *entity, return entity->post_process(entity, in_mb, out_mb, num, nb_err); } +/** + * The header 'rte_pdcp_group.h' depends on defines in 'rte_pdcp.h'. So include + * in the end. + */ +#include + #ifdef __cplusplus } #endif diff --git a/lib/pdcp/rte_pdcp_group.h b/lib/pdcp/rte_pdcp_group.h new file mode 100644 index 00..ece3e8c0ff --- /dev/null +++ b/lib/pdcp/rte_pdcp_group.h @@ -0,0 +1,131 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(C) 2023 Marvell. + */ + +#ifndef RTE_PDCP_GROUP_H +#define RTE_PDCP_GROUP_H + +/** + * @file rte_pdcp_group.h + * + * RTE PDCP grouping support. + * It is not recommended to include this file directly, include + * instead. + * Provides helper functions to process completed crypto-ops and group related + * packets by sessions they belong to. + */ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Group packets belonging to same PDCP entity. + */ +struct rte_pdcp_group { + union { + uint64_t val; + void *ptr; + } id; /**< Grouped by value */ + struct rte_mbuf **m; /**< Start of the group */ + uint32_t cnt; /**< Number of entries in the group */ + int32_t rc; /**< Status code associated with the group */ +}; + +/** + * @warning + * @b EXPERIMENTAL: this API may change without prior notice + * + * Take crypto-op as an input and extract pointer to related PDCP entity. + * @param cop + * The address of an input *rte_crypto_op* structure. + * @return + * The pointer to the related *rte_pdcp_entity* structure. + */ +static inline struct rte_pdcp_entity * +rte_pdcp_en_from_cop(const struct rte_crypto_op *cop) +{ + void *sess = cop->sym[0].session; + + return (struct rte_pdcp_entity *)(uintptr_t) + rte_cryptodev_sym_session_opaque_data_get(sess); +} + +/** + * @warning + * @b EXPERIMENTAL: this API may change without prior notice + * + * Take as input completed crypto ops, extract related mbufs and group them by + * *rte_pdcp_entity* they belong to. Mbuf for which the crypto operation has + * failed would be flagged using *RTE_MBUF_F_RX_SEC_OFFLOAD_FAILED* flag + * in rte_mbuf.ol_flags. The crypto_ops would be freed after the grouping. + * + * Note that application must ensure only crypto-ops prepared by lib_pdcp is + * provided back to @see rte_pdcp_pkt_crypto_group(). + * + * @param cop + * The address of an array of *num* pointers to the input *rte_crypto_op* + * structures. + * @param[out] mb + * The address of an array of *num* pointers to output *rte_mbuf* structures. + * @param[out] grp + * The address of an array of *num* to output *rte_pdcp_group* structures. + * @param num + * The maximum number of crypto-ops to process. + * @return + * Number of filled elements in *grp* array. + * + */ +static inline uint16_t +rte_pdcp_pkt_crypto_group(struct rte_crypto_op *cop[], struct rte_mbuf *mb[], + struct rte_pdcp_group grp[], uint16_t num) +{ + uint32_t i, j = 0, n = 0; + void *ns, *ps = NULL; + struct rte_mbuf *m; + + for (i = 0; i != num; i++) { + m = cop[i]->sym[0].m_src; + ns = cop[i]->sym[0].session; + + m->ol_flags |= RTE_MBUF_F_RX_SEC_OFFLOAD; + if (cop[i]->status != RTE_CRYPTO_OP_STATUS_SUCCESS) + m->ol_flags |= RTE_MBUF_F_RX_SEC_OFFLOAD_FAILED; + + /* Different entity */ + if (ps != ns) { + + /* Finalize open group and start a new one */ + if (ps != NULL) { + grp[n].cnt = mb + j - grp[n].m; + n++; + } +
[PATCH v5 05/21] pdcp: add crypto session create and destroy
Add routines to create & destroy sessions. PDCP lib would take crypto transforms as input and creates the session on the corresponding device after verifying capabilities. Signed-off-by: Anoob Joseph Signed-off-by: Volodymyr Fialko Acked-by: Akhil Goyal --- lib/pdcp/pdcp_crypto.c | 223 - lib/pdcp/pdcp_crypto.h | 5 + 2 files changed, 225 insertions(+), 3 deletions(-) diff --git a/lib/pdcp/pdcp_crypto.c b/lib/pdcp/pdcp_crypto.c index 755e27ec9e..6d2a85dc7d 100644 --- a/lib/pdcp/pdcp_crypto.c +++ b/lib/pdcp/pdcp_crypto.c @@ -2,20 +2,237 @@ * Copyright(C) 2023 Marvell. */ +#include +#include +#include +#include #include +#include #include "pdcp_crypto.h" +#include "pdcp_entity.h" + +static int +pdcp_crypto_caps_cipher_verify(uint8_t dev_id, const struct rte_crypto_sym_xform *c_xfrm) +{ + const struct rte_cryptodev_symmetric_capability *cap; + struct rte_cryptodev_sym_capability_idx cap_idx; + int ret; + + cap_idx.type = RTE_CRYPTO_SYM_XFORM_CIPHER; + cap_idx.algo.cipher = c_xfrm->cipher.algo; + + cap = rte_cryptodev_sym_capability_get(dev_id, &cap_idx); + if (cap == NULL) + return -1; + + ret = rte_cryptodev_sym_capability_check_cipher(cap, c_xfrm->cipher.key.length, + c_xfrm->cipher.iv.length); + + return ret; +} + +static int +pdcp_crypto_caps_auth_verify(uint8_t dev_id, const struct rte_crypto_sym_xform *a_xfrm) +{ + const struct rte_cryptodev_symmetric_capability *cap; + struct rte_cryptodev_sym_capability_idx cap_idx; + int ret; + + cap_idx.type = RTE_CRYPTO_SYM_XFORM_AUTH; + cap_idx.algo.auth = a_xfrm->auth.algo; + + cap = rte_cryptodev_sym_capability_get(dev_id, &cap_idx); + if (cap == NULL) + return -1; + + ret = rte_cryptodev_sym_capability_check_auth(cap, a_xfrm->auth.key.length, + a_xfrm->auth.digest_length, + a_xfrm->auth.iv.length); + + return ret; +} + +static int +pdcp_crypto_xfrm_validate(const struct rte_pdcp_entity_conf *conf, +const struct rte_crypto_sym_xform *c_xfrm, +const struct rte_crypto_sym_xform *a_xfrm, +bool is_auth_then_cipher) +{ + uint16_t cipher_iv_len, auth_digest_len, auth_iv_len; + int ret; + + /* +* Uplink means PDCP entity is configured for transmit. Downlink means PDCP entity is +* configured for receive. When integrity protection is enabled, PDCP always performs +* digest-encrypted or auth-gen-encrypt for uplink (and decrypt-auth-verify for downlink). +* So for uplink, crypto chain would be auth-cipher while for downlink it would be +* cipher-auth. +* +* When integrity protection is not required, xform would be cipher only. +*/ + + if (c_xfrm == NULL) + return -EINVAL; + + if (conf->pdcp_xfrm.pkt_dir == RTE_SECURITY_PDCP_UPLINK) { + + /* With UPLINK, if auth is enabled, it should be before cipher */ + if (a_xfrm != NULL && !is_auth_then_cipher) + return -EINVAL; + + /* With UPLINK, cipher operation must be encrypt */ + if (c_xfrm->cipher.op != RTE_CRYPTO_CIPHER_OP_ENCRYPT) + return -EINVAL; + + /* With UPLINK, auth operation (if present) must be generate */ + if (a_xfrm != NULL && a_xfrm->auth.op != RTE_CRYPTO_AUTH_OP_GENERATE) + return -EINVAL; + + } else if (conf->pdcp_xfrm.pkt_dir == RTE_SECURITY_PDCP_DOWNLINK) { + + /* With DOWNLINK, if auth is enabled, it should be after cipher */ + if (a_xfrm != NULL && is_auth_then_cipher) + return -EINVAL; + + /* With DOWNLINK, cipher operation must be decrypt */ + if (c_xfrm->cipher.op != RTE_CRYPTO_CIPHER_OP_DECRYPT) + return -EINVAL; + + /* With DOWNLINK, auth operation (if present) must be verify */ + if (a_xfrm != NULL && a_xfrm->auth.op != RTE_CRYPTO_AUTH_OP_VERIFY) + return -EINVAL; + + } else { + return -EINVAL; + } + + if ((c_xfrm->cipher.algo != RTE_CRYPTO_CIPHER_NULL) && + (c_xfrm->cipher.algo != RTE_CRYPTO_CIPHER_AES_CTR) && + (c_xfrm->cipher.algo != RTE_CRYPTO_CIPHER_ZUC_EEA3) && + (c_xfrm->cipher.algo != RTE_CRYPTO_CIPHER_SNOW3G_UEA2)) + return -EINVAL; + + if (c_xfrm->cipher.algo == RTE_CRYPTO_CIPHER_NULL) + cipher_iv_len = 0; + else + cipher_iv_len = PDCP_IV_LEN; + + if (cipher_iv_len != c_xfrm->cipher.iv.length) +
[PATCH v5 09/21] app/test: add lib pdcp tests
Add tests to verify lib PDCP operations. Tests leverage existing PDCP test vectors. Signed-off-by: Anoob Joseph Signed-off-by: Volodymyr Fialko --- MAINTAINERS | 1 + app/test/meson.build | 5 + app/test/test_pdcp.c | 732 +++ 3 files changed, 738 insertions(+) create mode 100644 app/test/test_pdcp.c diff --git a/MAINTAINERS b/MAINTAINERS index 85a3b94644..15dd88af3a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1554,6 +1554,7 @@ M: Anoob Joseph M: Volodymyr Fialko T: git://dpdk.org/next/dpdk-next-crypto F: lib/pdcp/ +F: app/test/test_pdcp* Packet Framework diff --git a/app/test/meson.build b/app/test/meson.build index b9b5432496..2894793347 100644 --- a/app/test/meson.build +++ b/app/test/meson.build @@ -435,6 +435,11 @@ if dpdk_conf.has('RTE_HAS_LIBPCAP') endif endif +if dpdk_conf.has('RTE_LIB_PDCP') +test_sources += 'test_pdcp.c' +fast_tests += [['pdcp_autotest', false, true]] +endif + if cc.has_argument('-Wno-format-truncation') cflags += '-Wno-format-truncation' endif diff --git a/app/test/test_pdcp.c b/app/test/test_pdcp.c new file mode 100644 index 00..34b759eaef --- /dev/null +++ b/app/test/test_pdcp.c @@ -0,0 +1,732 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(C) 2023 Marvell. + */ + +#include +#include +#include +#include + +#include "test.h" +#include "test_cryptodev.h" +#include "test_cryptodev_security_pdcp_test_vectors.h" + +#define NB_DESC 1024 +#define CDEV_INVALID_ID UINT8_MAX +#define NB_TESTS RTE_DIM(pdcp_test_params) +#define PDCP_IV_LEN 16 + +struct pdcp_testsuite_params { + struct rte_mempool *mbuf_pool; + struct rte_mempool *cop_pool; + struct rte_mempool *sess_pool; + bool cdevs_used[RTE_CRYPTO_MAX_DEVS]; +}; + +static struct pdcp_testsuite_params testsuite_params; + +struct pdcp_test_conf { + struct rte_pdcp_entity_conf entity; + struct rte_crypto_sym_xform c_xfrm; + struct rte_crypto_sym_xform a_xfrm; + bool is_integrity_protected; + uint8_t input[RTE_PDCP_CTRL_PDU_SIZE_MAX]; + uint32_t input_len; + uint8_t output[RTE_PDCP_CTRL_PDU_SIZE_MAX]; + uint32_t output_len; +}; + +static inline int +pdcp_hdr_size_get(enum rte_security_pdcp_sn_size sn_size) +{ + return RTE_ALIGN_MUL_CEIL(sn_size, 8) / 8; +} + +static int +cryptodev_init(int dev_id) +{ + struct pdcp_testsuite_params *ts_params = &testsuite_params; + struct rte_cryptodev_qp_conf qp_conf; + struct rte_cryptodev_info dev_info; + struct rte_cryptodev_config config; + int ret, socket_id; + + /* Check if device was already initialized */ + if (ts_params->cdevs_used[dev_id]) + return 0; + + rte_cryptodev_info_get(dev_id, &dev_info); + + if (dev_info.max_nb_queue_pairs < 1) { + RTE_LOG(ERR, USER1, "Cryptodev doesn't have sufficient queue pairs available\n"); + return -ENODEV; + } + + socket_id = rte_socket_id(); + + memset(&config, 0, sizeof(config)); + config.nb_queue_pairs = 1; + config.socket_id = socket_id; + + ret = rte_cryptodev_configure(dev_id, &config); + if (ret < 0) { + RTE_LOG(ERR, USER1, "Could not configure cryptodev - %d\n", dev_id); + return -ENODEV; + } + + memset(&qp_conf, 0, sizeof(qp_conf)); + qp_conf.nb_descriptors = NB_DESC; + + ret = rte_cryptodev_queue_pair_setup(dev_id, 0, &qp_conf, socket_id); + if (ret < 0) { + RTE_LOG(ERR, USER1, "Could not configure queue pair\n"); + return -ENODEV; + } + + ret = rte_cryptodev_start(dev_id); + if (ret < 0) { + RTE_LOG(ERR, USER1, "Could not start cryptodev\n"); + return -ENODEV; + } + + /* Mark device as initialized */ + ts_params->cdevs_used[dev_id] = true; + + return 0; +} + +static void +cryptodev_fini(int dev_id) +{ + rte_cryptodev_stop(dev_id); +} + +static unsigned int +cryptodev_sess_priv_max_req_get(void) +{ + struct rte_cryptodev_info info; + unsigned int sess_priv_sz; + int i, nb_dev; + void *sec_ctx; + + nb_dev = rte_cryptodev_count(); + + sess_priv_sz = 0; + + for (i = 0; i < nb_dev; i++) { + rte_cryptodev_info_get(i, &info); + sess_priv_sz = RTE_MAX(sess_priv_sz, rte_cryptodev_sym_get_private_session_size(i)); + if (info.feature_flags & RTE_CRYPTODEV_FF_SECURITY) { + sec_ctx = rte_cryptodev_get_sec_ctx(i); + sess_priv_sz = RTE_MAX(sess_priv_sz, + rte_security_session_get_size(sec_ctx)); + } + } + + return sess_priv_sz; +} + +static int +testsuite_setup(void) +{ + struct pdcp_testsuite_params *ts_params = &testsuite_params; + int nb_cdev, sess_priv_size, nb_ses
[PATCH v5 11/21] doc: add PDCP library guide
Add guide for PDCP library. Signed-off-by: Anoob Joseph Signed-off-by: Kiran Kumar K Signed-off-by: Volodymyr Fialko --- MAINTAINERS | 1 + .../img/pdcp_functional_overview.svg | 1 + doc/guides/prog_guide/index.rst | 1 + doc/guides/prog_guide/pdcp_lib.rst| 254 ++ doc/guides/rel_notes/release_23_07.rst| 12 + 5 files changed, 269 insertions(+) create mode 100644 doc/guides/prog_guide/img/pdcp_functional_overview.svg create mode 100644 doc/guides/prog_guide/pdcp_lib.rst diff --git a/MAINTAINERS b/MAINTAINERS index 15dd88af3a..4345ae21b6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1555,6 +1555,7 @@ M: Volodymyr Fialko T: git://dpdk.org/next/dpdk-next-crypto F: lib/pdcp/ F: app/test/test_pdcp* +F: doc/guides/prog_guide/pdcp_lib.rst Packet Framework diff --git a/doc/guides/prog_guide/img/pdcp_functional_overview.svg b/doc/guides/prog_guide/img/pdcp_functional_overview.svg new file mode 100644 index 00..287daafc21 --- /dev/null +++ b/doc/guides/prog_guide/img/pdcp_functional_overview.svg @@ -0,0 +1 @@ +http://www.w3.org/2000/svg"; xmlns:xlink="http://www.w3.org/1999/xlink"; overflow="hidden">Radio Interface (Uu/PC5)UE/NG-RAN/UE ANG-RAN/UE/UE BTransmitting PDCP entityReceiving PDCP entityTransmission buffer:SequencenumberingHeader or uplink dataCompressionHeader or uplink dataDecompressionRouting / DuplicationAdd PDCP headerCipheringIntegrity protectionPackets associated to a PDCP SDUPackets not associated to a PDCP SDURemove PDCP HeaderDecipheringIntegrity VerificationReception buffer:ReorderingDuplicate discardingPackets associated to a PDCP SDUPackets not associated to a PDCP SDU \ No newline at end of file diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst index 87333ee84a..6099ff63cd 100644 --- a/doc/guides/prog_guide/index.rst +++ b/doc/guides/prog_guide/index.rst @@ -77,4 +77,5 @@ Programmer's Guide lto profile_app asan +pdcp_lib glossary diff --git a/doc/guides/prog_guide/pdcp_lib.rst b/doc/guides/prog_guide/pdcp_lib.rst new file mode 100644 index 00..8369c71600 --- /dev/null +++ b/doc/guides/prog_guide/pdcp_lib.rst @@ -0,0 +1,254 @@ +.. SPDX-License-Identifier: BSD-3-Clause +Copyright(C) 2023 Marvell. + +PDCP Protocol Processing Library + + +DPDK provides a library for PDCP protocol processing. The library utilizes +other DPDK libraries such as cryptodev, reorder, etc., to provide the +application with a transparent and high performant PDCP protocol processing +library. + +The library abstracts complete PDCP protocol processing conforming to +``ETSI TS 138 323 V17.1.0 (2022-08)``. +https://www.etsi.org/deliver/etsi_ts/138300_138399/138323/17.01.00_60/ts_138323v170100p.pdf + +PDCP would involve the following operations, + +1. Transfer of user plane data +2. Transfer of control plane data +3. Header compression +4. Uplink data compression +5. Ciphering and integrity protection + +.. _figure_pdcp_functional_overview: + +.. figure:: img/pdcp_functional_overview.* + + PDCP functional overview new + +PDCP library would abstract the protocol offload features of the cryptodev and +would provide a uniform interface and consistent API usage to work with +cryptodev irrespective of the protocol offload features supported. + +PDCP entity API +--- + +PDCP library provides following control path APIs that is used to +configure various PDCP entities, + +1. ``rte_pdcp_entity_establish()`` +2. ``rte_pdcp_entity_suspend()`` +3. ``rte_pdcp_entity_release()`` + +A PDCP entity would translate to one ``rte_cryptodev_sym_session`` or +``rte_security_session`` based on the config. The sessions would be created/ +destroyed while corresponding PDCP entity operations are performed. + +When upper layers request a PDCP entity suspend (``rte_pdcp_entity_suspend()``), +it would result in flushing out of all cached packets. Internal state variables +are updated as described in 5.1.4. + +When upper layers request a PDCP entity release (``rte_pdcp_entity_release()``), +it would result in flushing out of all cached packets and releasing of all +memory associated with the entity. It would internally free any crypto/security +sessions created. All procedures mentioned in 5.1.3 would be performed. + +PDCP PDU (Protocol Data Unit) API +- + +PDCP PDUs can be categorized as, + +1. Control PDU +2. Data PDU + +Control PDUs are used for signalling between entities on either end and can be +one of the following, + +1. PDCP status report +2. ROHC feedback +3. EHC feedback + +Control PDUs are not ciphered or authenticated, and so such packets are not +submitted to cryptodev for processing. + +Data PDUs are regular packets submitted by upper layers for transmission to +other end. Such packets would need to be ciphered and authenticated based on +the entity configu
[PATCH v5 13/21] pdcp: implement t-Reordering and packet buffering
From: Volodymyr Fialko Add in-order delivery of packets in PDCP. Delivery of packets in-order relies on t-Reordering timer. When 'out-of-order delivery' is disabled, PDCP will buffer all received packets that are out of order. The t-Reordering timer determines the time period these packets would be held in the buffer, waiting for any missing packets to arrive. Introduce packet buffering and state variables which indicate status of the timer. Signed-off-by: Anoob Joseph Signed-off-by: Volodymyr Fialko --- lib/pdcp/meson.build| 3 +- lib/pdcp/pdcp_entity.h | 19 +++ lib/pdcp/pdcp_process.c | 117 ++-- lib/pdcp/pdcp_reorder.c | 27 ++ lib/pdcp/pdcp_reorder.h | 62 + lib/pdcp/rte_pdcp.c | 53 -- lib/pdcp/rte_pdcp.h | 6 ++- 7 files changed, 252 insertions(+), 35 deletions(-) create mode 100644 lib/pdcp/pdcp_reorder.c create mode 100644 lib/pdcp/pdcp_reorder.h diff --git a/lib/pdcp/meson.build b/lib/pdcp/meson.build index 75d476bf6d..f4f9246bcb 100644 --- a/lib/pdcp/meson.build +++ b/lib/pdcp/meson.build @@ -12,9 +12,10 @@ sources = files( 'pdcp_crypto.c', 'pdcp_ctrl_pdu.c', 'pdcp_process.c', +'pdcp_reorder.c', 'rte_pdcp.c', ) headers = files('rte_pdcp.h') indirect_headers += files('rte_pdcp_group.h') -deps += ['mbuf', 'net', 'cryptodev', 'security'] +deps += ['mbuf', 'net', 'cryptodev', 'security', 'reorder'] diff --git a/lib/pdcp/pdcp_entity.h b/lib/pdcp/pdcp_entity.h index 28691a504b..34341cdc11 100644 --- a/lib/pdcp/pdcp_entity.h +++ b/lib/pdcp/pdcp_entity.h @@ -11,6 +11,8 @@ #include #include +#include "pdcp_reorder.h" + struct entity_priv; #define PDCP_HFN_MIN 0 @@ -109,6 +111,17 @@ union cipher_iv_partial { uint64_t u64[2]; }; +enum timer_state { + TIMER_STOP, + TIMER_RUNNING, + TIMER_EXPIRED, +}; + +struct pdcp_t_reordering { + /** Represent timer state */ + enum timer_state state; +}; + struct pdcp_cnt_bitmap { /** Number of entries that can be stored. */ uint32_t size; @@ -145,6 +158,8 @@ struct entity_priv { uint64_t is_null_auth : 1; /** Is status report required.*/ uint64_t is_status_report_required : 1; + /** Is out-of-order delivery enabled */ + uint64_t is_out_of_order_delivery : 1; } flags; /** Crypto op pool. */ struct rte_mempool *cop_pool; @@ -161,6 +176,10 @@ struct entity_priv { struct entity_priv_dl_part { /** PDCP would need to track the count values that are already received.*/ struct pdcp_cnt_bitmap bitmap; + /** t-Reordering handles */ + struct pdcp_t_reordering t_reorder; + /** Reorder packet buffer */ + struct pdcp_reorder reorder; }; struct entity_priv_ul_part { diff --git a/lib/pdcp/pdcp_process.c b/lib/pdcp/pdcp_process.c index ed1413db6d..84a0f3a43f 100644 --- a/lib/pdcp/pdcp_process.c +++ b/lib/pdcp/pdcp_process.c @@ -837,25 +837,88 @@ pdcp_packet_strip(struct rte_mbuf *mb, const uint32_t hdr_trim_sz, const bool tr } } -static inline bool +static inline int pdcp_post_process_update_entity_state(const struct rte_pdcp_entity *entity, - const uint32_t count) + const uint32_t count, struct rte_mbuf *mb, + struct rte_mbuf *out_mb[], + const bool trim_mac) { struct entity_priv *en_priv = entity_priv_get(entity); + struct pdcp_t_reordering *t_reorder; + struct pdcp_reorder *reorder; + uint16_t processed = 0; - if (count < en_priv->state.rx_deliv) - return false; + struct entity_priv_dl_part *dl = entity_dl_part_get(entity); + const uint32_t hdr_trim_sz = en_priv->hdr_sz + en_priv->aad_sz; - /* t-Reordering timer is not supported - SDU will be delivered immediately. -* Update RX_DELIV to the COUNT value of the first PDCP SDU which has not -* been delivered to upper layers -*/ - en_priv->state.rx_next = count + 1; + if (count < en_priv->state.rx_deliv) + return -EINVAL; if (count >= en_priv->state.rx_next) en_priv->state.rx_next = count + 1; - return true; + pdcp_packet_strip(mb, hdr_trim_sz, trim_mac); + + if (en_priv->flags.is_out_of_order_delivery) { + out_mb[0] = mb; + en_priv->state.rx_deliv = count + 1; + + return 1; + } + + reorder = &dl->reorder; + t_reorder = &dl->t_reorder; + + if (count == en_priv->state.rx_deliv) { + if (reorder->is_active) { + /* +* This insert used only to increment reorder->min_seqn +* To remove it - min_seqn_s
[PATCH v5 00/21] lib: add pdcp protocol
Add Packet Data Convergence Protocol (PDCP) processing library. The library is similar to lib_ipsec which provides IPsec processing capabilities in DPDK. PDCP would involve roughly the following operations, 1. Transfer of user plane data 2. Transfer of control plane data 3. Header compression 4. Uplink data compression 5. Ciphering and integrity protection PDCP library provides following control path APIs that is used to configure various PDCP entities, 1. rte_pdcp_entity_establish() 2. rte_pdcp_entity_suspend() 3. rte_pdcp_entity_release() PDCP process is split into 2 parts. One before crypto processing (rte_pdcp_pkt_pre_process()) and one after crypto processing (rte_pdcp_pkt_post_process()). Since cryptodev dequeue can return crypto operations belonging to multiple entities, rte_pdcp_pkt_crypto_group() is added to help grouping crypto operations belonging to same entity. Similar to lib IPsec, lib PDCP would allow application to use same API sequence while leveraging protocol offload features enabled by rte_security library. Lib PDCP would internally change the handles registered for *pre_process* and *post_process* based on features enabled in the entity. Lib PDCP would create the required sessions on the device provided in entity to minimize the application requirements. Also, the crypto_op allocation and free would also be done internally by lib PDCP to allow the library to create crypto ops as required for the input packets. For example, when control PDUs are received, no cryptodev enqueue-dequeue is expected for the same and lib PDCP is expected to handle it differently. Lib PDCP utilizes reorder library for implementing in-order delivery. It utilizes bitmap library for implementing status reports and track the COUNT value of the packets received. To allow application to choose timer implementation of choice, lib PDCP allows application to configure handles that can be used for starting & stopping timers. Upon expiry, application can call corresponding PDCP API(``rte_pdcp_t_reordering_expiry_handle``) for handling the event. Unit tests are added to verify both rte_timer based timers as well as rte_eventdev based timers. PDCP tracks the sequence number of the received packets and during events such as re-establishment, it is required to generate reports and transmit to the peer. This series introduces ``rte_pdcp_control_pdu_create`` for handling control PDU generation. Changes in v5: - Deferred patch adding thread safe processing. - Updated release notes & MAINTAINERS file. Changes in v4: - Disabled 'annotate locks' with lib PDCP - Enable PDCP autotest only when lib is enabled - Use rwlock instead of spinlock - Avoid per packet checks for thread safety (Stephen) - In DL path, save count determined during pre-process in mbuf and use the same in post-process. Determining count again may lead To errors - Simplified DL path threads to allow more common code between SN 12 & 18 Changes in v3: - Addressed review comments (Akhil) - Addressed build failure in CI (tests with lib eventdev disabled) - Addressed checkpatch issues - Set only positive values to rte_errno (Akhil) Changes in v2: - Added control PDU handling - Added t-Reordering timer - Added in-order delivery - Added status PDU generation - Rebased on top of new features added in reorder library - Split base patch - Increased test coverage - Improved thread safety Changes from RFC - Implementation for all APIs covering basic control plane & user plane packets - Unit test leveraging existing PDCP vectors available in test_cryptodev - Unit test performing both UL & DL operations to verify various protocol features - Updated documentation Sample application sequence: struct rte_mbuf **out_mb, *pkts[MAX_BURST_SIZE]; struct rte_crypto_op *cop[MAX_BURST_SIZE]; struct rte_pdcp_group grp[MAX_BURST_SIZE]; struct rte_pdcp_entity *pdcp_entity; int nb_max_out_mb, ret, nb_grp; /* Create PDCP entity */ pdcp_entity = rte_pdcp_entity_establish(&conf); /** * Allocate buffer for holding mbufs returned during PDCP suspend, * release & post-process APIs. */ /* Max packets that can be cached in entity + burst size */ nb_max_out_mb = pdcp_entity->max_pkt_cache + 1; out_mb = rte_malloc(NULL, nb_max_out_mb * sizeof(uintptr_t), 0); if (out_mb == NULL) { /* Handle error */ } while (1) { /* Receive packet and form mbuf */ /** * Prepare packets for crypto operation. Following operations * would be done, * * Transmitting entity/UL (only data PDUs): * - Perform compression * - Assign sequence number * - Add PDCP header * - Create & prepare crypto_op * - Prepare IV for crypto operation (auth_gen, encrypt)
[PATCH v5 08/21] pdcp: add IV generation routines
For PDCP, IV generated has varying formats depending on the ciphering and authentication algorithm used. Add routines to populate IV accordingly. Signed-off-by: Anoob Joseph Signed-off-by: Volodymyr Fialko --- lib/pdcp/pdcp_entity.h | 87 lib/pdcp/pdcp_process.c | 284 2 files changed, 371 insertions(+) diff --git a/lib/pdcp/pdcp_entity.h b/lib/pdcp/pdcp_entity.h index 1d4a43a3bc..10a72faae1 100644 --- a/lib/pdcp/pdcp_entity.h +++ b/lib/pdcp/pdcp_entity.h @@ -26,6 +26,89 @@ struct entity_state { uint32_t rx_reord; }; +union auth_iv_partial { + /* For AES-CMAC, there is no IV, but message gets prepended */ + struct { +#if RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN + uint64_t count : 32; + uint64_t zero_38_39 : 2; + uint64_t direction : 1; + uint64_t bearer : 5; + uint64_t zero_40_63 : 24; +#else + uint64_t count : 32; + uint64_t bearer : 5; + uint64_t direction : 1; + uint64_t zero_38_39 : 2; + uint64_t zero_40_63 : 24; +#endif + } aes_cmac; + struct { +#if RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN + uint64_t count : 32; + uint64_t zero_37_39 : 3; + uint64_t bearer : 5; + uint64_t zero_40_63 : 24; + + uint64_t rsvd_65_71 : 7; + uint64_t direction_64 : 1; + uint64_t rsvd_72_111 : 40; + uint64_t rsvd_113_119 : 7; + uint64_t direction_112 : 1; + uint64_t rsvd_120_127 : 8; +#else + uint64_t count : 32; + uint64_t bearer : 5; + uint64_t zero_37_39 : 3; + uint64_t zero_40_63 : 24; + + uint64_t direction_64 : 1; + uint64_t rsvd_65_71 : 7; + uint64_t rsvd_72_111 : 40; + uint64_t direction_112 : 1; + uint64_t rsvd_113_119 : 7; + uint64_t rsvd_120_127 : 8; +#endif + } zs; + uint64_t u64[2]; +}; + +union cipher_iv_partial { + struct { +#if RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN + uint64_t count : 32; + uint64_t zero_38_39 : 2; + uint64_t direction : 1; + uint64_t bearer : 5; + uint64_t zero_40_63 : 24; +#else + uint64_t count : 32; + uint64_t bearer : 5; + uint64_t direction : 1; + uint64_t zero_38_39 : 2; + uint64_t zero_40_63 : 24; +#endif + uint64_t zero_64_127; + } aes_ctr; + struct { +#if RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN + uint64_t count : 32; + uint64_t zero_38_39 : 2; + uint64_t direction : 1; + uint64_t bearer : 5; + uint64_t zero_40_63 : 24; +#else + uint64_t count : 32; + uint64_t bearer : 5; + uint64_t direction : 1; + uint64_t zero_38_39 : 2; + uint64_t zero_40_63 : 24; +#endif + uint64_t rsvd_64_127; + } zs; + uint64_t u64[2]; +}; + /* * Layout of PDCP entity: [rte_pdcp_entity] [entity_priv] [entity_dl/ul] */ @@ -35,6 +118,10 @@ struct entity_priv { struct rte_cryptodev_sym_session *crypto_sess; /** Entity specific IV generation function. */ iv_gen_t iv_gen; + /** Pre-prepared auth IV. */ + union auth_iv_partial auth_iv_part; + /** Pre-prepared cipher IV. */ + union cipher_iv_partial cipher_iv_part; /** Entity state variables. */ struct entity_state state; /** Flags. */ diff --git a/lib/pdcp/pdcp_process.c b/lib/pdcp/pdcp_process.c index bd75e6f802..28ac4102da 100644 --- a/lib/pdcp/pdcp_process.c +++ b/lib/pdcp/pdcp_process.c @@ -14,6 +14,181 @@ #include "pdcp_entity.h" #include "pdcp_process.h" +/* Enum of supported algorithms for ciphering */ +enum pdcp_cipher_algo { + PDCP_CIPHER_ALGO_NULL, + PDCP_CIPHER_ALGO_AES, + PDCP_CIPHER_ALGO_ZUC, + PDCP_CIPHER_ALGO_SNOW3G, + PDCP_CIPHER_ALGO_MAX +}; + +/* Enum of supported algorithms for integrity */ +enum pdcp_auth_algo { + PDCP_AUTH_ALGO_NULL, + PDCP_AUTH_ALGO_AES, + PDCP_AUTH_ALGO_ZUC, + PDCP_AUTH_ALGO_SNOW3G, + PDCP_AUTH_ALGO_MAX +}; + +/* IV generation functions based on type of operation (cipher - auth) */ + +static void +pdcp_iv_gen_null_null(struct rte_crypto_op *cop, const struct entity_priv *en_priv, uint32_t count) +{ + /* No IV required for NULL cipher + NULL auth */ + RTE_SET_USED(cop); + RTE_SET_USED(en_priv); + RTE_SET_USED(count); +} + +static void +pdcp_iv_gen_null_aes_cmac(struct rte_crypto_op *cop, const struct entity_priv *en_priv, + uint32_t count) +{ + struct rte_crypto_sym_op *op = cop->sym; + struct rt
[PATCH v5 03/21] pdcp: add pre and post-process
PDCP process is split into 2 parts. One before crypto processing (rte_pdcp_pkt_pre_process()) and one after crypto processing (rte_pdcp_pkt_post_process()). Functionality of pre-process & post-process varies based on the type of entity. Registration of entity specific function pointer allows skipping multiple checks that would come in datapath otherwise. Signed-off-by: Anoob Joseph Signed-off-by: Kiran Kumar K Signed-off-by: Volodymyr Fialko Acked-by: Akhil Goyal --- lib/pdcp/rte_pdcp.h | 97 lib/pdcp/version.map | 3 ++ 2 files changed, 100 insertions(+) diff --git a/lib/pdcp/rte_pdcp.h b/lib/pdcp/rte_pdcp.h index 1f96fdc9a1..46c3c2a416 100644 --- a/lib/pdcp/rte_pdcp.h +++ b/lib/pdcp/rte_pdcp.h @@ -22,6 +22,21 @@ extern "C" { #endif +/* Forward declarations */ +struct rte_pdcp_entity; + +/* PDCP pre-process function based on entity configuration */ +typedef uint16_t (*rte_pdcp_pre_p_t)(const struct rte_pdcp_entity *entity, +struct rte_mbuf *mb[], +struct rte_crypto_op *cop[], +uint16_t num, uint16_t *nb_err); + +/* PDCP post-process function based on entity configuration */ +typedef uint16_t (*rte_pdcp_post_p_t)(const struct rte_pdcp_entity *entity, + struct rte_mbuf *in_mb[], + struct rte_mbuf *out_mb[], + uint16_t num, uint16_t *nb_err); + /** * PDCP entity. * @@ -33,6 +48,10 @@ extern "C" { * plane depending on which radio bearer it is carrying data for. */ struct rte_pdcp_entity { + /** Entity specific pre-process handle. */ + rte_pdcp_pre_p_t pre_process; + /** Entity specific post-process handle. */ + rte_pdcp_post_p_t post_process; /** * PDCP entities may hold packets for purposes of in-order delivery (in * case of receiving PDCP entity) and re-transmission (in case of @@ -160,6 +179,84 @@ int rte_pdcp_entity_suspend(struct rte_pdcp_entity *pdcp_entity, struct rte_mbuf *out_mb[]); +/** + * @warning + * @b EXPERIMENTAL: this API may change without prior notice + * + * For input mbufs and given PDCP entity pre-process the mbufs and prepare + * crypto ops that can be enqueued to the cryptodev associated with given + * session. Only error packets would be moved returned in the input buffer, + * *mb*, and it is the responsibility of the application to free the same. + * + * @param entity + * Pointer to the *rte_pdcp_entity* object the packets belong to. + * @param[in, out] mb + * The address of an array of *num* pointers to *rte_mbuf* structures + * which contain the input packets. Any error packets would be returned in the + * same buffer. + * @param[out] cop + * The address of an array that can hold up to *num* pointers to + * *rte_crypto_op* structures. Crypto ops would be allocated by + * ``rte_pdcp_pkt_pre_process`` API. + * @param num + * The maximum number of packets to process. + * @param[out] nb_err + * Pointer to return the number of error packets returned in *mb* + * @return + * Count of crypto_ops prepared + */ +__rte_experimental +static inline uint16_t +rte_pdcp_pkt_pre_process(const struct rte_pdcp_entity *entity, +struct rte_mbuf *mb[], struct rte_crypto_op *cop[], +uint16_t num, uint16_t *nb_err) +{ + return entity->pre_process(entity, mb, cop, num, nb_err); +} + +/** + * @warning + * @b EXPERIMENTAL: this API may change without prior notice + * + * For input mbufs and given PDCP entity, perform PDCP post-processing of the + * mbufs. + * + * Input mbufs are the ones retrieved from crypto_ops dequeued from cryptodev + * and grouped by *rte_pdcp_pkt_crypto_group()*. + * + * The post-processed packets would be returned in the *out_mb* buffer. + * The resultant mbufs would be grouped into success packets and error packets. + * Error packets would be grouped in the end of the array and it is the + * responsibility of the application to handle the same. + * + * When in-order delivery is enabled, PDCP entity may buffer packets and would + * deliver packets only when all prior packets have been post-processed. That + * would result in returning more/less packets than enqueued. + * + * @param entity + * Pointer to the *rte_pdcp_entity* object the packets belong to. + * @param in_mb + * The address of an array of *num* pointers to *rte_mbuf* structures. + * @param[out] out_mb + * The address of an array of *num* pointers to *rte_mbuf* structures + * to output packets after PDCP post-processing. + * @param num + * The maximum number of packets to process. + * @param[out] nb_err + * The number of error packets returned in *out_mb* buffer. + * @return + * Count of packets returned in *out_mb* buffer. + */ +__rte_experimental +static inline uint16_t +rte_pdcp
[PATCH v5 12/21] pdcp: add control PDU handling for status report
Add control PDU handling and implement status report generation. Status report generation works only when RX_DELIV = RX_NEXT. Signed-off-by: Anoob Joseph Signed-off-by: Volodymyr Fialko --- doc/guides/prog_guide/pdcp_lib.rst | 9 ++ lib/pdcp/meson.build | 2 ++ lib/pdcp/pdcp_cnt.c| 29 ++ lib/pdcp/pdcp_cnt.h| 14 + lib/pdcp/pdcp_ctrl_pdu.c | 46 + lib/pdcp/pdcp_ctrl_pdu.h | 15 ++ lib/pdcp/pdcp_entity.h | 15 -- lib/pdcp/pdcp_process.c| 13 + lib/pdcp/rte_pdcp.c| 47 +- lib/pdcp/rte_pdcp.h| 33 + lib/pdcp/version.map | 2 ++ 11 files changed, 222 insertions(+), 3 deletions(-) create mode 100644 lib/pdcp/pdcp_cnt.c create mode 100644 lib/pdcp/pdcp_cnt.h create mode 100644 lib/pdcp/pdcp_ctrl_pdu.c create mode 100644 lib/pdcp/pdcp_ctrl_pdu.h diff --git a/doc/guides/prog_guide/pdcp_lib.rst b/doc/guides/prog_guide/pdcp_lib.rst index 8369c71600..dcb424bb1d 100644 --- a/doc/guides/prog_guide/pdcp_lib.rst +++ b/doc/guides/prog_guide/pdcp_lib.rst @@ -76,6 +76,15 @@ Data PDUs are regular packets submitted by upper layers for transmission to other end. Such packets would need to be ciphered and authenticated based on the entity configuration. +PDCP packet processing API for control PDU +~~ + +Control PDUs are used in PDCP as a communication channel between transmitting +and receiving entities. When upper layer request for operations such +re-establishment, receiving PDCP entity need to prepare a status report and +send it to the other end. The API ``rte_pdcp_control_pdu_create`` allows +application to request the same. + PDCP packet processing API for data PDU ~~~ diff --git a/lib/pdcp/meson.build b/lib/pdcp/meson.build index 08679b743a..75d476bf6d 100644 --- a/lib/pdcp/meson.build +++ b/lib/pdcp/meson.build @@ -8,7 +8,9 @@ if is_windows endif sources = files( +'pdcp_cnt.c', 'pdcp_crypto.c', +'pdcp_ctrl_pdu.c', 'pdcp_process.c', 'rte_pdcp.c', ) diff --git a/lib/pdcp/pdcp_cnt.c b/lib/pdcp/pdcp_cnt.c new file mode 100644 index 00..c9b952184b --- /dev/null +++ b/lib/pdcp/pdcp_cnt.c @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(C) 2023 Marvell. + */ + +#include + +#include "pdcp_cnt.h" +#include "pdcp_entity.h" + +int +pdcp_cnt_ring_create(struct rte_pdcp_entity *en, const struct rte_pdcp_entity_conf *conf) +{ + struct entity_priv_dl_part *en_priv_dl; + uint32_t window_sz; + + if (en == NULL || conf == NULL) + return -EINVAL; + + if (conf->pdcp_xfrm.pkt_dir == RTE_SECURITY_PDCP_UPLINK) + return 0; + + en_priv_dl = entity_dl_part_get(en); + window_sz = pdcp_window_size_get(conf->pdcp_xfrm.sn_size); + + RTE_SET_USED(window_sz); + RTE_SET_USED(en_priv_dl); + + return 0; +} diff --git a/lib/pdcp/pdcp_cnt.h b/lib/pdcp/pdcp_cnt.h new file mode 100644 index 00..bbda478b55 --- /dev/null +++ b/lib/pdcp/pdcp_cnt.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(C) 2023 Marvell. + */ + +#ifndef PDCP_CNT_H +#define PDCP_CNT_H + +#include + +#include "pdcp_entity.h" + +int pdcp_cnt_ring_create(struct rte_pdcp_entity *en, const struct rte_pdcp_entity_conf *conf); + +#endif /* PDCP_CNT_H */ diff --git a/lib/pdcp/pdcp_ctrl_pdu.c b/lib/pdcp/pdcp_ctrl_pdu.c new file mode 100644 index 00..feb05fd863 --- /dev/null +++ b/lib/pdcp/pdcp_ctrl_pdu.c @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(C) 2023 Marvell. + */ + +#include +#include +#include + +#include "pdcp_ctrl_pdu.h" +#include "pdcp_entity.h" + +static __rte_always_inline void +pdcp_hdr_fill(struct rte_pdcp_up_ctrl_pdu_hdr *pdu_hdr, uint32_t rx_deliv) +{ + pdu_hdr->d_c = RTE_PDCP_PDU_TYPE_CTRL; + pdu_hdr->pdu_type = RTE_PDCP_CTRL_PDU_TYPE_STATUS_REPORT; + pdu_hdr->r = 0; + pdu_hdr->fmc = rte_cpu_to_be_32(rx_deliv); +} + +int +pdcp_ctrl_pdu_status_gen(struct entity_priv *en_priv, struct rte_mbuf *m) +{ + struct rte_pdcp_up_ctrl_pdu_hdr *pdu_hdr; + uint32_t rx_deliv; + int pdu_sz; + + if (!en_priv->flags.is_status_report_required) + return -EINVAL; + + pdu_sz = sizeof(struct rte_pdcp_up_ctrl_pdu_hdr); + + rx_deliv = en_priv->state.rx_deliv; + + /* Zero missing PDUs - status report contains only FMC */ + if (rx_deliv >= en_priv->state.rx_next) { + pdu_hdr = (struct rte_pdcp_up_ctrl_pdu_hdr *)rte_pktmbuf_append(m, pdu_sz); + if (pdu_hdr == NULL) + return -ENOMEM; + pdcp_hdr_fill(pdu_hdr, rx_deliv); + + return 0; + } + +
[PATCH v5 06/21] pdcp: add pre and post process for UL
Add routines to perform pre & post processing based on the type of entity. To avoid checks in datapath, there are different function pointers registered based on the following, 1. Control plane v/s user plane 2. 12 bit v/s 18 bit SN For control plane only 12 bit SN need to be supported (as per PDCP specification). Signed-off-by: Anoob Joseph Signed-off-by: Kiran Kumar K Signed-off-by: Volodymyr Fialko Acked-by: Akhil Goyal --- lib/pdcp/pdcp_entity.h | 24 +++ lib/pdcp/pdcp_process.c | 334 2 files changed, 358 insertions(+) diff --git a/lib/pdcp/pdcp_entity.h b/lib/pdcp/pdcp_entity.h index 000297588f..23628ebad4 100644 --- a/lib/pdcp/pdcp_entity.h +++ b/lib/pdcp/pdcp_entity.h @@ -92,22 +92,46 @@ pdcp_hdr_size_get(enum rte_security_pdcp_sn_size sn_size) return RTE_ALIGN_MUL_CEIL(sn_size, 8) / 8; } +static inline uint32_t +pdcp_window_size_get(enum rte_security_pdcp_sn_size sn_size) +{ + return 1 << (sn_size - 1); +} + static inline uint32_t pdcp_sn_mask_get(enum rte_security_pdcp_sn_size sn_size) { return (1 << sn_size) - 1; } +static inline uint32_t +pdcp_sn_from_count_get(uint32_t count, enum rte_security_pdcp_sn_size sn_size) +{ + return (count & pdcp_sn_mask_get(sn_size)); +} + static inline uint32_t pdcp_hfn_mask_get(enum rte_security_pdcp_sn_size sn_size) { return ~pdcp_sn_mask_get(sn_size); } +static inline uint32_t +pdcp_hfn_from_count_get(uint32_t count, enum rte_security_pdcp_sn_size sn_size) +{ + return (count & pdcp_hfn_mask_get(sn_size)) >> sn_size; +} + static inline uint32_t pdcp_count_from_hfn_sn_get(uint32_t hfn, uint32_t sn, enum rte_security_pdcp_sn_size sn_size) { return (((hfn << sn_size) & pdcp_hfn_mask_get(sn_size)) | (sn & pdcp_sn_mask_get(sn_size))); } +static inline uint32_t +pdcp_hfn_max(enum rte_security_pdcp_sn_size sn_size) +{ + return (1 << (32 - sn_size)) - 1; +} + #endif /* PDCP_ENTITY_H */ diff --git a/lib/pdcp/pdcp_process.c b/lib/pdcp/pdcp_process.c index 79f5dce5db..9b7de39db6 100644 --- a/lib/pdcp/pdcp_process.c +++ b/lib/pdcp/pdcp_process.c @@ -36,6 +36,336 @@ pdcp_crypto_xfrm_get(const struct rte_pdcp_entity_conf *conf, struct rte_crypto_ return 0; } +static inline void +cop_prepare(const struct entity_priv *en_priv, struct rte_mbuf *mb, struct rte_crypto_op *cop, + uint8_t data_offset, uint32_t count, const bool is_auth) +{ + const struct rte_crypto_op cop_init = { + .type = RTE_CRYPTO_OP_TYPE_SYMMETRIC, + .status = RTE_CRYPTO_OP_STATUS_NOT_PROCESSED, + .sess_type = RTE_CRYPTO_OP_WITH_SESSION, + }; + struct rte_crypto_sym_op *op; + uint32_t pkt_len; + + const uint8_t cipher_shift = 3 * en_priv->flags.is_cipher_in_bits; + const uint8_t auth_shift = 3 * en_priv->flags.is_auth_in_bits; + + op = cop->sym; + cop->raw = cop_init.raw; + op->m_src = mb; + op->m_dst = mb; + + /* Set IV */ + en_priv->iv_gen(cop, en_priv, count); + + /* Prepare op */ + pkt_len = rte_pktmbuf_pkt_len(mb); + op->cipher.data.offset = data_offset << cipher_shift; + op->cipher.data.length = (pkt_len - data_offset) << cipher_shift; + + if (is_auth) { + op->auth.data.offset = 0; + op->auth.data.length = (pkt_len - RTE_PDCP_MAC_I_LEN) << auth_shift; + op->auth.digest.data = rte_pktmbuf_mtod_offset(mb, uint8_t *, + (pkt_len - RTE_PDCP_MAC_I_LEN)); + } + + __rte_crypto_sym_op_attach_sym_session(op, en_priv->crypto_sess); +} + +static inline bool +pdcp_pre_process_uplane_sn_12_ul_set_sn(struct entity_priv *en_priv, struct rte_mbuf *mb, + uint32_t *count) +{ + struct rte_pdcp_up_data_pdu_sn_12_hdr *pdu_hdr; + const uint8_t hdr_sz = en_priv->hdr_sz; + uint32_t sn; + + /* Prepend PDU header */ + pdu_hdr = (struct rte_pdcp_up_data_pdu_sn_12_hdr *)rte_pktmbuf_prepend(mb, hdr_sz); + if (unlikely(pdu_hdr == NULL)) + return false; + + /* Update sequence num in the PDU header */ + *count = en_priv->state.tx_next++; + sn = pdcp_sn_from_count_get(*count, RTE_SECURITY_PDCP_SN_SIZE_12); + + pdu_hdr->d_c = RTE_PDCP_PDU_TYPE_DATA; + pdu_hdr->sn_11_8 = ((sn & 0xf00) >> 8); + pdu_hdr->sn_7_0 = (sn & 0xff); + pdu_hdr->r = 0; + return true; +} + +static uint16_t +pdcp_pre_process_uplane_sn_12_ul(const struct rte_pdcp_entity *entity, struct rte_mbuf *in_mb[], +struct rte_crypto_op *cop[], uint16_t num, uint16_t *nb_err_ret) +{ + struct entity_priv *en_priv = entity_priv_get(entity); + uint16_t nb_cop, nb_prep = 0, nb_err = 0; + struct rte_mbuf *mb; + uint32_t count; + uint8_t *mac_i; + int i; + + const uint8_
[PATCH v5 02/21] lib: add pdcp protocol
Add Packet Data Convergence Protocol (PDCP) processing library. The library is similar to lib_ipsec which provides IPsec processing capabilities in DPDK. PDCP would involve roughly the following options, 1. Transfer of user plane data 2. Transfer of control plane data 3. Header compression 4. Uplink data compression 5. Ciphering and integrity protection PDCP library provides following control path APIs that is used to configure various PDCP entities, 1. rte_pdcp_entity_establish() 2. rte_pdcp_entity_suspend() 3. rte_pdcp_entity_release() Signed-off-by: Anoob Joseph Signed-off-by: Kiran Kumar K Signed-off-by: Volodymyr Fialko --- MAINTAINERS | 6 ++ doc/api/doxy-api-index.md | 3 +- doc/api/doxy-api.conf.in | 1 + lib/meson.build | 1 + lib/pdcp/meson.build | 17 lib/pdcp/pdcp_crypto.c| 21 + lib/pdcp/pdcp_crypto.h| 15 lib/pdcp/pdcp_entity.h| 113 ++ lib/pdcp/pdcp_process.c | 138 +++ lib/pdcp/pdcp_process.h | 13 +++ lib/pdcp/rte_pdcp.c | 141 lib/pdcp/rte_pdcp.h | 167 ++ lib/pdcp/version.map | 10 +++ 13 files changed, 645 insertions(+), 1 deletion(-) create mode 100644 lib/pdcp/meson.build create mode 100644 lib/pdcp/pdcp_crypto.c create mode 100644 lib/pdcp/pdcp_crypto.h create mode 100644 lib/pdcp/pdcp_entity.h create mode 100644 lib/pdcp/pdcp_process.c create mode 100644 lib/pdcp/pdcp_process.h create mode 100644 lib/pdcp/rte_pdcp.c create mode 100644 lib/pdcp/rte_pdcp.h create mode 100644 lib/pdcp/version.map diff --git a/MAINTAINERS b/MAINTAINERS index 8df23e5099..85a3b94644 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1549,6 +1549,12 @@ F: doc/guides/tools/pdump.rst F: app/dumpcap/ F: doc/guides/tools/dumpcap.rst +PDCP - EXPERIMENTAL +M: Anoob Joseph +M: Volodymyr Fialko +T: git://dpdk.org/next/dpdk-next-crypto +F: lib/pdcp/ + Packet Framework diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md index debbe4134f..cd7a6cae44 100644 --- a/doc/api/doxy-api-index.md +++ b/doc/api/doxy-api-index.md @@ -128,7 +128,8 @@ The public API headers are grouped by topics: [eCPRI](@ref rte_ecpri.h), [L2TPv2](@ref rte_l2tpv2.h), [PPP](@ref rte_ppp.h), - [PDCP hdr](@ref rte_pdcp_hdr.h) + [PDCP hdr](@ref rte_pdcp_hdr.h), + [PDCP](@ref rte_pdcp.h) - **QoS**: [metering](@ref rte_meter.h), diff --git a/doc/api/doxy-api.conf.in b/doc/api/doxy-api.conf.in index d230a19e1f..58789308a9 100644 --- a/doc/api/doxy-api.conf.in +++ b/doc/api/doxy-api.conf.in @@ -62,6 +62,7 @@ INPUT = @TOPDIR@/doc/api/doxy-api-index.md \ @TOPDIR@/lib/net \ @TOPDIR@/lib/pcapng \ @TOPDIR@/lib/pci \ + @TOPDIR@/lib/pdcp \ @TOPDIR@/lib/pdump \ @TOPDIR@/lib/pipeline \ @TOPDIR@/lib/port \ diff --git a/lib/meson.build b/lib/meson.build index dc8aa4ac84..a6a54c196c 100644 --- a/lib/meson.build +++ b/lib/meson.build @@ -64,6 +64,7 @@ libraries = [ 'flow_classify', # flow_classify lib depends on pkt framework table lib 'graph', 'node', +'pdcp', # pdcp lib depends on crypto and security ] optional_libs = [ diff --git a/lib/pdcp/meson.build b/lib/pdcp/meson.build new file mode 100644 index 00..ccaf426240 --- /dev/null +++ b/lib/pdcp/meson.build @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(C) 2023 Marvell. + +if is_windows +build = false +reason = 'not supported on Windows' +subdir_done() +endif + +sources = files( +'pdcp_crypto.c', +'pdcp_process.c', +'rte_pdcp.c', +) +headers = files('rte_pdcp.h') + +deps += ['mbuf', 'net', 'cryptodev', 'security'] diff --git a/lib/pdcp/pdcp_crypto.c b/lib/pdcp/pdcp_crypto.c new file mode 100644 index 00..755e27ec9e --- /dev/null +++ b/lib/pdcp/pdcp_crypto.c @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(C) 2023 Marvell. + */ + +#include + +#include "pdcp_crypto.h" + +int +pdcp_crypto_sess_create(struct rte_pdcp_entity *entity, const struct rte_pdcp_entity_conf *conf) +{ + RTE_SET_USED(entity); + RTE_SET_USED(conf); + return 0; +} + +void +pdcp_crypto_sess_destroy(struct rte_pdcp_entity *entity) +{ + RTE_SET_USED(entity); +} diff --git a/lib/pdcp/pdcp_crypto.h b/lib/pdcp/pdcp_crypto.h new file mode 100644 index 00..6563331d37 --- /dev/null +++ b/lib/pdcp/pdcp_crypto.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(C) 2023 Marvell. + */ + +#ifndef PDCP_CRYPTO_H +#define PDCP_CRYPTO_H + +#include + +int pdcp_crypto_sess_create(struct rte_pdcp_entity *entity, + const struct rte_pdcp_entity_conf *conf); + +
[PATCH v5 10/21] test/pdcp: pdcp HFN tests in combined mode
From: Volodymyr Fialko Add tests to verify HFN/SN behaviour. Signed-off-by: Anoob Joseph Signed-off-by: Volodymyr Fialko --- app/test/test_pdcp.c | 302 ++- 1 file changed, 299 insertions(+), 3 deletions(-) diff --git a/app/test/test_pdcp.c b/app/test/test_pdcp.c index 34b759eaef..cfe2ec6aa9 100644 --- a/app/test/test_pdcp.c +++ b/app/test/test_pdcp.c @@ -16,6 +16,9 @@ #define NB_TESTS RTE_DIM(pdcp_test_params) #define PDCP_IV_LEN 16 +/* According to formula(7.2.a Window_Size) */ +#define PDCP_WINDOW_SIZE(sn_size) (1 << (sn_size - 1)) + struct pdcp_testsuite_params { struct rte_mempool *mbuf_pool; struct rte_mempool *cop_pool; @@ -36,12 +39,69 @@ struct pdcp_test_conf { uint32_t output_len; }; +static int create_test_conf_from_index(const int index, struct pdcp_test_conf *conf); + +typedef int (*test_with_conf_t)(struct pdcp_test_conf *conf); + +static int +run_test_foreach_known_vec(test_with_conf_t test, bool stop_on_first_pass) +{ + struct pdcp_test_conf test_conf; + bool all_tests_skipped = true; + uint32_t i; + int ret; + + for (i = 0; i < NB_TESTS; i++) { + create_test_conf_from_index(i, &test_conf); + ret = test(&test_conf); + + if (ret == TEST_FAILED) { + printf("[%03i] - %s - failed\n", i, pdcp_test_params[i].name); + return TEST_FAILED; + } + + if ((ret == TEST_SKIPPED) || (ret == -ENOTSUP)) + continue; + + if (stop_on_first_pass) + return TEST_SUCCESS; + + all_tests_skipped = false; + } + + if (all_tests_skipped) + return TEST_SKIPPED; + + return TEST_SUCCESS; +} + +static int +run_test_with_all_known_vec(const void *args) +{ + test_with_conf_t test = args; + + return run_test_foreach_known_vec(test, false); +} + static inline int pdcp_hdr_size_get(enum rte_security_pdcp_sn_size sn_size) { return RTE_ALIGN_MUL_CEIL(sn_size, 8) / 8; } +static int +pktmbuf_read_into(const struct rte_mbuf *m, void *buf, size_t buf_len) +{ + if (m->pkt_len > buf_len) + return -ENOMEM; + + const void *read = rte_pktmbuf_read(m, 0, m->pkt_len, buf); + if (read != NULL && read != buf) + memcpy(buf, read, m->pkt_len); + + return 0; +} + static int cryptodev_init(int dev_id) { @@ -326,6 +386,21 @@ pdcp_sn_from_raw_get(const void *data, enum rte_security_pdcp_sn_size size) return sn; } +static void +pdcp_sn_to_raw_set(void *data, uint32_t sn, int size) +{ + if (size == RTE_SECURITY_PDCP_SN_SIZE_12) { + struct rte_pdcp_up_data_pdu_sn_12_hdr *pdu_hdr = data; + pdu_hdr->sn_11_8 = ((sn & 0xf00) >> 8); + pdu_hdr->sn_7_0 = (sn & 0xff); + } else if (size == RTE_SECURITY_PDCP_SN_SIZE_18) { + struct rte_pdcp_up_data_pdu_sn_18_hdr *pdu_hdr = data; + pdu_hdr->sn_17_16 = ((sn & 0x3) >> 16); + pdu_hdr->sn_15_8 = ((sn & 0xff00) >> 8); + pdu_hdr->sn_7_0 = (sn & 0xff); + } +} + static int create_test_conf_from_index(const int index, struct pdcp_test_conf *conf) { @@ -648,9 +723,17 @@ test_attempt_single(struct pdcp_test_conf *t_conf) goto mbuf_free; } - ret = pdcp_known_vec_verify(mbuf, t_conf->output, t_conf->output_len); - if (ret) - goto mbuf_free; + /* If expected output provided - verify, else - store for future use */ + if (t_conf->output_len) { + ret = pdcp_known_vec_verify(mbuf, t_conf->output, t_conf->output_len); + if (ret) + goto mbuf_free; + } else { + ret = pktmbuf_read_into(mbuf, t_conf->output, RTE_PDCP_CTRL_PDU_SIZE_MAX); + if (ret) + goto mbuf_free; + t_conf->output_len = mbuf->pkt_len; + } ret = rte_pdcp_entity_suspend(pdcp_entity, out_mb); if (ret) { @@ -667,6 +750,193 @@ test_attempt_single(struct pdcp_test_conf *t_conf) return ret; } +static void +uplink_to_downlink_convert(const struct pdcp_test_conf *ul_cfg, + struct pdcp_test_conf *dl_cfg) +{ + assert(ul_cfg->entity.pdcp_xfrm.pkt_dir == RTE_SECURITY_PDCP_UPLINK); + + memcpy(dl_cfg, ul_cfg, sizeof(*dl_cfg)); + dl_cfg->entity.pdcp_xfrm.pkt_dir = RTE_SECURITY_PDCP_DOWNLINK; + dl_cfg->entity.reverse_iv_direction = false; + + if (dl_cfg->is_integrity_protected) { + dl_cfg->entity.crypto_xfrm = &dl_cfg->c_xfrm; + + dl_cfg->c_xfrm.cipher.op = RTE_CRYPTO_CIPHER_OP_DECRYPT; + dl_cfg->c_xfrm.next = &dl_cfg->a_xfrm; + + dl_cfg->a_xfrm.auth.op = RTE_CRYPTO_AUTH_OP_VERIFY; + dl_cfg->a_xfrm.next = NULL;
[PATCH v5 07/21] pdcp: add pre and post process for DL
Add routines to perform pre & post processing for down link entities. Signed-off-by: Anoob Joseph Signed-off-by: Kiran Kumar K Signed-off-by: Volodymyr Fialko --- lib/pdcp/pdcp_entity.h | 2 + lib/pdcp/pdcp_process.c | 384 lib/pdcp/pdcp_process.h | 11 ++ lib/pdcp/rte_pdcp.c | 23 +++ 4 files changed, 420 insertions(+) diff --git a/lib/pdcp/pdcp_entity.h b/lib/pdcp/pdcp_entity.h index 23628ebad4..1d4a43a3bc 100644 --- a/lib/pdcp/pdcp_entity.h +++ b/lib/pdcp/pdcp_entity.h @@ -13,6 +13,8 @@ struct entity_priv; +#define PDCP_HFN_MIN 0 + /* IV generation function based on the entity configuration */ typedef void (*iv_gen_t)(struct rte_crypto_op *cop, const struct entity_priv *en_priv, uint32_t count); diff --git a/lib/pdcp/pdcp_process.c b/lib/pdcp/pdcp_process.c index 9b7de39db6..bd75e6f802 100644 --- a/lib/pdcp/pdcp_process.c +++ b/lib/pdcp/pdcp_process.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -333,9 +334,353 @@ pdcp_post_process_ul(const struct rte_pdcp_entity *entity, return nb_success; } +static inline int +pdcp_sn_count_get(const uint32_t rx_deliv, int32_t rsn, uint32_t *count, + const enum rte_security_pdcp_sn_size sn_size) +{ + const uint32_t rx_deliv_sn = pdcp_sn_from_count_get(rx_deliv, sn_size); + const uint32_t window_sz = pdcp_window_size_get(sn_size); + uint32_t rhfn; + + rhfn = pdcp_hfn_from_count_get(rx_deliv, sn_size); + + if (rsn < (int32_t)(rx_deliv_sn - window_sz)) { + if (unlikely(rhfn == pdcp_hfn_max(sn_size))) + return -ERANGE; + rhfn += 1; + } else if ((uint32_t)rsn >= (rx_deliv_sn + window_sz)) { + if (unlikely(rhfn == PDCP_HFN_MIN)) + return -ERANGE; + rhfn -= 1; + } + + *count = pdcp_count_from_hfn_sn_get(rhfn, rsn, sn_size); + + return 0; +} + +static inline uint16_t +pdcp_pre_process_uplane_sn_12_dl_flags(const struct rte_pdcp_entity *entity, + struct rte_mbuf *in_mb[], struct rte_crypto_op *cop[], + uint16_t num, uint16_t *nb_err_ret, + const bool is_integ_protected) +{ + struct entity_priv *en_priv = entity_priv_get(entity); + struct rte_pdcp_up_data_pdu_sn_12_hdr *pdu_hdr; + uint16_t nb_cop, nb_prep = 0, nb_err = 0; + rte_pdcp_dynfield_t *mb_dynfield; + struct rte_mbuf *mb; + int32_t rsn = 0; + uint32_t count; + int i; + + const uint8_t data_offset = en_priv->hdr_sz + en_priv->aad_sz; + + nb_cop = rte_crypto_op_bulk_alloc(en_priv->cop_pool, RTE_CRYPTO_OP_TYPE_SYMMETRIC, cop, + num); + + const uint32_t rx_deliv = en_priv->state.rx_deliv; + + for (i = 0; i < nb_cop; i++) { + mb = in_mb[i]; + pdu_hdr = rte_pktmbuf_mtod(mb, struct rte_pdcp_up_data_pdu_sn_12_hdr *); + + /* Check for PDU type */ + if (likely(pdu_hdr->d_c == RTE_PDCP_PDU_TYPE_DATA)) { + rsn = ((pdu_hdr->sn_11_8 << 8) | (pdu_hdr->sn_7_0)); + } else { + /** NOTE: Control PDU not handled.*/ + in_mb[nb_err++] = mb; + continue; + } + + if (unlikely(pdcp_sn_count_get(rx_deliv, rsn, &count, + RTE_SECURITY_PDCP_SN_SIZE_12))) { + in_mb[nb_err++] = mb; + continue; + } + + cop_prepare(en_priv, mb, cop[nb_prep++], data_offset, count, is_integ_protected); + + mb_dynfield = pdcp_dynfield(mb); + *mb_dynfield = count; + } + + if (unlikely(nb_err)) + rte_mempool_put_bulk(en_priv->cop_pool, (void *)&cop[nb_prep], nb_cop - nb_prep); + + *nb_err_ret = num - nb_prep; + + return nb_prep; +} + +static uint16_t +pdcp_pre_process_uplane_sn_12_dl_ip(const struct rte_pdcp_entity *entity, struct rte_mbuf *mb[], + struct rte_crypto_op *cop[], uint16_t num, uint16_t *nb_err) +{ + return pdcp_pre_process_uplane_sn_12_dl_flags(entity, mb, cop, num, nb_err, true); +} + +static uint16_t +pdcp_pre_process_uplane_sn_12_dl(const struct rte_pdcp_entity *entity, struct rte_mbuf *mb[], +struct rte_crypto_op *cop[], uint16_t num, uint16_t *nb_err) +{ + return pdcp_pre_process_uplane_sn_12_dl_flags(entity, mb, cop, num, nb_err, false); +} + +static inline uint16_t +pdcp_pre_process_uplane_sn_18_dl_flags(const struct rte_pdcp_entity *entity, + struct rte_mbuf *in_mb[], struct rte_crypto_op *cop[], + uint16_
[PATCH v5 02/21] lib: add pdcp protocol
Add Packet Data Convergence Protocol (PDCP) processing library. The library is similar to lib_ipsec which provides IPsec processing capabilities in DPDK. PDCP would involve roughly the following options, 1. Transfer of user plane data 2. Transfer of control plane data 3. Header compression 4. Uplink data compression 5. Ciphering and integrity protection PDCP library provides following control path APIs that is used to configure various PDCP entities, 1. rte_pdcp_entity_establish() 2. rte_pdcp_entity_suspend() 3. rte_pdcp_entity_release() Signed-off-by: Anoob Joseph Signed-off-by: Kiran Kumar K Signed-off-by: Volodymyr Fialko --- MAINTAINERS | 6 ++ doc/api/doxy-api-index.md | 3 +- doc/api/doxy-api.conf.in | 1 + lib/meson.build | 1 + lib/pdcp/meson.build | 17 lib/pdcp/pdcp_crypto.c| 21 + lib/pdcp/pdcp_crypto.h| 15 lib/pdcp/pdcp_entity.h| 113 ++ lib/pdcp/pdcp_process.c | 138 +++ lib/pdcp/pdcp_process.h | 13 +++ lib/pdcp/rte_pdcp.c | 141 lib/pdcp/rte_pdcp.h | 167 ++ lib/pdcp/version.map | 10 +++ 13 files changed, 645 insertions(+), 1 deletion(-) create mode 100644 lib/pdcp/meson.build create mode 100644 lib/pdcp/pdcp_crypto.c create mode 100644 lib/pdcp/pdcp_crypto.h create mode 100644 lib/pdcp/pdcp_entity.h create mode 100644 lib/pdcp/pdcp_process.c create mode 100644 lib/pdcp/pdcp_process.h create mode 100644 lib/pdcp/rte_pdcp.c create mode 100644 lib/pdcp/rte_pdcp.h create mode 100644 lib/pdcp/version.map diff --git a/MAINTAINERS b/MAINTAINERS index 8df23e5099..85a3b94644 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1549,6 +1549,12 @@ F: doc/guides/tools/pdump.rst F: app/dumpcap/ F: doc/guides/tools/dumpcap.rst +PDCP - EXPERIMENTAL +M: Anoob Joseph +M: Volodymyr Fialko +T: git://dpdk.org/next/dpdk-next-crypto +F: lib/pdcp/ + Packet Framework diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md index debbe4134f..cd7a6cae44 100644 --- a/doc/api/doxy-api-index.md +++ b/doc/api/doxy-api-index.md @@ -128,7 +128,8 @@ The public API headers are grouped by topics: [eCPRI](@ref rte_ecpri.h), [L2TPv2](@ref rte_l2tpv2.h), [PPP](@ref rte_ppp.h), - [PDCP hdr](@ref rte_pdcp_hdr.h) + [PDCP hdr](@ref rte_pdcp_hdr.h), + [PDCP](@ref rte_pdcp.h) - **QoS**: [metering](@ref rte_meter.h), diff --git a/doc/api/doxy-api.conf.in b/doc/api/doxy-api.conf.in index d230a19e1f..58789308a9 100644 --- a/doc/api/doxy-api.conf.in +++ b/doc/api/doxy-api.conf.in @@ -62,6 +62,7 @@ INPUT = @TOPDIR@/doc/api/doxy-api-index.md \ @TOPDIR@/lib/net \ @TOPDIR@/lib/pcapng \ @TOPDIR@/lib/pci \ + @TOPDIR@/lib/pdcp \ @TOPDIR@/lib/pdump \ @TOPDIR@/lib/pipeline \ @TOPDIR@/lib/port \ diff --git a/lib/meson.build b/lib/meson.build index dc8aa4ac84..a6a54c196c 100644 --- a/lib/meson.build +++ b/lib/meson.build @@ -64,6 +64,7 @@ libraries = [ 'flow_classify', # flow_classify lib depends on pkt framework table lib 'graph', 'node', +'pdcp', # pdcp lib depends on crypto and security ] optional_libs = [ diff --git a/lib/pdcp/meson.build b/lib/pdcp/meson.build new file mode 100644 index 00..ccaf426240 --- /dev/null +++ b/lib/pdcp/meson.build @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(C) 2023 Marvell. + +if is_windows +build = false +reason = 'not supported on Windows' +subdir_done() +endif + +sources = files( +'pdcp_crypto.c', +'pdcp_process.c', +'rte_pdcp.c', +) +headers = files('rte_pdcp.h') + +deps += ['mbuf', 'net', 'cryptodev', 'security'] diff --git a/lib/pdcp/pdcp_crypto.c b/lib/pdcp/pdcp_crypto.c new file mode 100644 index 00..755e27ec9e --- /dev/null +++ b/lib/pdcp/pdcp_crypto.c @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(C) 2023 Marvell. + */ + +#include + +#include "pdcp_crypto.h" + +int +pdcp_crypto_sess_create(struct rte_pdcp_entity *entity, const struct rte_pdcp_entity_conf *conf) +{ + RTE_SET_USED(entity); + RTE_SET_USED(conf); + return 0; +} + +void +pdcp_crypto_sess_destroy(struct rte_pdcp_entity *entity) +{ + RTE_SET_USED(entity); +} diff --git a/lib/pdcp/pdcp_crypto.h b/lib/pdcp/pdcp_crypto.h new file mode 100644 index 00..6563331d37 --- /dev/null +++ b/lib/pdcp/pdcp_crypto.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(C) 2023 Marvell. + */ + +#ifndef PDCP_CRYPTO_H +#define PDCP_CRYPTO_H + +#include + +int pdcp_crypto_sess_create(struct rte_pdcp_entity *entity, + const struct rte_pdcp_entity_conf *conf); + +
[PATCH v5 18/21] test/pdcp: add timer restart case
From: Volodymyr Fialko Add test to cover the case when t-reordering timer should be restarted on the same packet. Signed-off-by: Anoob Joseph Signed-off-by: Volodymyr Fialko --- app/test/test_pdcp.c | 68 1 file changed, 68 insertions(+) diff --git a/app/test/test_pdcp.c b/app/test/test_pdcp.c index 25729b2bdd..82cc25ec7a 100644 --- a/app/test/test_pdcp.c +++ b/app/test/test_pdcp.c @@ -1106,6 +1106,71 @@ test_reorder_gap_fill(struct pdcp_test_conf *ul_conf) return ret; } +static int +test_reorder_gap_in_reorder_buffer(const struct pdcp_test_conf *ul_conf) +{ + const enum rte_security_pdcp_sn_size sn_size = ul_conf->entity.pdcp_xfrm.sn_size; + struct rte_mbuf *m = NULL, *out_mb[2] = {0}; + uint16_t nb_success = 0, nb_err = 0; + struct rte_pdcp_entity *pdcp_entity; + int ret = TEST_FAILED, nb_out, i; + struct pdcp_test_conf dl_conf; + uint8_t cdev_id; + + const int start_count = 0; + + if (ul_conf->entity.pdcp_xfrm.pkt_dir == RTE_SECURITY_PDCP_DOWNLINK) + return TEST_SKIPPED; + + /* Create configuration for actual testing */ + uplink_to_downlink_convert(ul_conf, &dl_conf); + dl_conf.entity.pdcp_xfrm.hfn = pdcp_hfn_from_count_get(start_count, sn_size); + dl_conf.entity.sn = pdcp_sn_from_count_get(start_count, sn_size); + pdcp_entity = test_entity_create(&dl_conf, &ret); + if (pdcp_entity == NULL) + return ret; + + cdev_id = dl_conf.entity.dev_id; + + /* Create two gaps [NULL, m1, NULL, m3]*/ + for (i = 0; i < 2; i++) { + m = generate_packet_for_dl_with_sn(*ul_conf, start_count + 2 * i + 1); + ASSERT_TRUE_OR_GOTO(m != NULL, exit, "Could not allocate buffer for packet\n"); + nb_success = test_process_packets(pdcp_entity, cdev_id, &m, 1, out_mb, &nb_err); + ASSERT_TRUE_OR_GOTO(nb_err == 0, exit, "Error occurred during packet process\n"); + ASSERT_TRUE_OR_GOTO(nb_success == 0, exit, "Packet was not buffered as expected\n"); + m = NULL; /* Packet was moved to PDCP lib */ + } + + /* Generate packet to fill the first gap */ + m = generate_packet_for_dl_with_sn(*ul_conf, start_count); + ASSERT_TRUE_OR_GOTO(m != NULL, exit, "Could not allocate buffer for packet\n"); + + /* +* Buffered packets after insert [m0, m1, NULL, m3] +* Only first gap should be filled, timer should be restarted for second gap +*/ + nb_success = test_process_packets(pdcp_entity, cdev_id, &m, 1, out_mb, &nb_err); + ASSERT_TRUE_OR_GOTO(nb_err == 0, exit, "Error occurred during packet process\n"); + ASSERT_TRUE_OR_GOTO(nb_success == 2, exit, + "Packet count mismatch (received: %i, expected: 2)\n", nb_success); + m = NULL; + /* Check that packets in correct order */ + ASSERT_TRUE_OR_GOTO(array_asc_sorted_check(out_mb, nb_success, sn_size), + exit, "Error occurred during packet drain\n"); + ASSERT_TRUE_OR_GOTO(testsuite_params.timer_is_running == true, exit, + "Timer should be restarted after partial drain"); + + + ret = TEST_SUCCESS; +exit: + rte_pktmbuf_free(m); + rte_pktmbuf_free_bulk(out_mb, nb_success); + nb_out = rte_pdcp_entity_release(pdcp_entity, out_mb); + rte_pktmbuf_free_bulk(out_mb, nb_out); + return ret; +} + static int test_reorder_buffer_full_window_size_sn_12(const struct pdcp_test_conf *ul_conf) { @@ -1527,6 +1592,9 @@ static struct unit_test_suite reorder_test_cases = { TEST_CASE_NAMED_WITH_DATA("test_reorder_gap_fill", ut_setup_pdcp, ut_teardown_pdcp, run_test_with_all_known_vec, test_reorder_gap_fill), + TEST_CASE_NAMED_WITH_DATA("test_reorder_gap_in_reorder_buffer", + ut_setup_pdcp, ut_teardown_pdcp, + run_test_with_all_known_vec, test_reorder_gap_in_reorder_buffer), TEST_CASE_NAMED_WITH_DATA("test_reorder_buffer_full_window_size_sn_12", ut_setup_pdcp, ut_teardown_pdcp, run_test_with_all_known_vec_until_first_pass, -- 2.25.1
[PATCH v5 10/21] test/pdcp: pdcp HFN tests in combined mode
From: Volodymyr Fialko Add tests to verify HFN/SN behaviour. Signed-off-by: Anoob Joseph Signed-off-by: Volodymyr Fialko --- app/test/test_pdcp.c | 302 ++- 1 file changed, 299 insertions(+), 3 deletions(-) diff --git a/app/test/test_pdcp.c b/app/test/test_pdcp.c index 34b759eaef..cfe2ec6aa9 100644 --- a/app/test/test_pdcp.c +++ b/app/test/test_pdcp.c @@ -16,6 +16,9 @@ #define NB_TESTS RTE_DIM(pdcp_test_params) #define PDCP_IV_LEN 16 +/* According to formula(7.2.a Window_Size) */ +#define PDCP_WINDOW_SIZE(sn_size) (1 << (sn_size - 1)) + struct pdcp_testsuite_params { struct rte_mempool *mbuf_pool; struct rte_mempool *cop_pool; @@ -36,12 +39,69 @@ struct pdcp_test_conf { uint32_t output_len; }; +static int create_test_conf_from_index(const int index, struct pdcp_test_conf *conf); + +typedef int (*test_with_conf_t)(struct pdcp_test_conf *conf); + +static int +run_test_foreach_known_vec(test_with_conf_t test, bool stop_on_first_pass) +{ + struct pdcp_test_conf test_conf; + bool all_tests_skipped = true; + uint32_t i; + int ret; + + for (i = 0; i < NB_TESTS; i++) { + create_test_conf_from_index(i, &test_conf); + ret = test(&test_conf); + + if (ret == TEST_FAILED) { + printf("[%03i] - %s - failed\n", i, pdcp_test_params[i].name); + return TEST_FAILED; + } + + if ((ret == TEST_SKIPPED) || (ret == -ENOTSUP)) + continue; + + if (stop_on_first_pass) + return TEST_SUCCESS; + + all_tests_skipped = false; + } + + if (all_tests_skipped) + return TEST_SKIPPED; + + return TEST_SUCCESS; +} + +static int +run_test_with_all_known_vec(const void *args) +{ + test_with_conf_t test = args; + + return run_test_foreach_known_vec(test, false); +} + static inline int pdcp_hdr_size_get(enum rte_security_pdcp_sn_size sn_size) { return RTE_ALIGN_MUL_CEIL(sn_size, 8) / 8; } +static int +pktmbuf_read_into(const struct rte_mbuf *m, void *buf, size_t buf_len) +{ + if (m->pkt_len > buf_len) + return -ENOMEM; + + const void *read = rte_pktmbuf_read(m, 0, m->pkt_len, buf); + if (read != NULL && read != buf) + memcpy(buf, read, m->pkt_len); + + return 0; +} + static int cryptodev_init(int dev_id) { @@ -326,6 +386,21 @@ pdcp_sn_from_raw_get(const void *data, enum rte_security_pdcp_sn_size size) return sn; } +static void +pdcp_sn_to_raw_set(void *data, uint32_t sn, int size) +{ + if (size == RTE_SECURITY_PDCP_SN_SIZE_12) { + struct rte_pdcp_up_data_pdu_sn_12_hdr *pdu_hdr = data; + pdu_hdr->sn_11_8 = ((sn & 0xf00) >> 8); + pdu_hdr->sn_7_0 = (sn & 0xff); + } else if (size == RTE_SECURITY_PDCP_SN_SIZE_18) { + struct rte_pdcp_up_data_pdu_sn_18_hdr *pdu_hdr = data; + pdu_hdr->sn_17_16 = ((sn & 0x3) >> 16); + pdu_hdr->sn_15_8 = ((sn & 0xff00) >> 8); + pdu_hdr->sn_7_0 = (sn & 0xff); + } +} + static int create_test_conf_from_index(const int index, struct pdcp_test_conf *conf) { @@ -648,9 +723,17 @@ test_attempt_single(struct pdcp_test_conf *t_conf) goto mbuf_free; } - ret = pdcp_known_vec_verify(mbuf, t_conf->output, t_conf->output_len); - if (ret) - goto mbuf_free; + /* If expected output provided - verify, else - store for future use */ + if (t_conf->output_len) { + ret = pdcp_known_vec_verify(mbuf, t_conf->output, t_conf->output_len); + if (ret) + goto mbuf_free; + } else { + ret = pktmbuf_read_into(mbuf, t_conf->output, RTE_PDCP_CTRL_PDU_SIZE_MAX); + if (ret) + goto mbuf_free; + t_conf->output_len = mbuf->pkt_len; + } ret = rte_pdcp_entity_suspend(pdcp_entity, out_mb); if (ret) { @@ -667,6 +750,193 @@ test_attempt_single(struct pdcp_test_conf *t_conf) return ret; } +static void +uplink_to_downlink_convert(const struct pdcp_test_conf *ul_cfg, + struct pdcp_test_conf *dl_cfg) +{ + assert(ul_cfg->entity.pdcp_xfrm.pkt_dir == RTE_SECURITY_PDCP_UPLINK); + + memcpy(dl_cfg, ul_cfg, sizeof(*dl_cfg)); + dl_cfg->entity.pdcp_xfrm.pkt_dir = RTE_SECURITY_PDCP_DOWNLINK; + dl_cfg->entity.reverse_iv_direction = false; + + if (dl_cfg->is_integrity_protected) { + dl_cfg->entity.crypto_xfrm = &dl_cfg->c_xfrm; + + dl_cfg->c_xfrm.cipher.op = RTE_CRYPTO_CIPHER_OP_DECRYPT; + dl_cfg->c_xfrm.next = &dl_cfg->a_xfrm; + + dl_cfg->a_xfrm.auth.op = RTE_CRYPTO_AUTH_OP_VERIFY; + dl_cfg->a_xfrm.next = NULL;
[PATCH v5 20/21] pdcp: allocate reorder buffer alongside with entity
From: Volodymyr Fialko Instead of allocating reorder buffer separately on heap, allocate memory for it together with rest of entity, and then only initialize buffer via `rte_reorder_init()`. Signed-off-by: Anoob Joseph Signed-off-by: Volodymyr Fialko --- lib/pdcp/pdcp_cnt.c | 9 +++ lib/pdcp/pdcp_cnt.h | 3 ++- lib/pdcp/pdcp_entity.h | 2 +- lib/pdcp/pdcp_reorder.c | 11 ++-- lib/pdcp/pdcp_reorder.h | 12 ++--- lib/pdcp/rte_pdcp.c | 58 ++--- 6 files changed, 55 insertions(+), 40 deletions(-) diff --git a/lib/pdcp/pdcp_cnt.c b/lib/pdcp/pdcp_cnt.c index af027b00d3..e1d0634b4d 100644 --- a/lib/pdcp/pdcp_cnt.c +++ b/lib/pdcp/pdcp_cnt.c @@ -20,15 +20,14 @@ pdcp_cnt_bitmap_get_memory_footprint(const struct rte_pdcp_entity_conf *conf) } int -pdcp_cnt_bitmap_create(struct entity_priv_dl_part *dl, void *bitmap_mem, uint32_t window_size) +pdcp_cnt_bitmap_create(struct entity_priv_dl_part *dl, uint32_t nb_elem, + void *bitmap_mem, uint32_t mem_size) { - uint32_t mem_size = rte_bitmap_get_memory_footprint(window_size); - - dl->bitmap.bmp = rte_bitmap_init(window_size, bitmap_mem, mem_size); + dl->bitmap.bmp = rte_bitmap_init(nb_elem, bitmap_mem, mem_size); if (dl->bitmap.bmp == NULL) return -EINVAL; - dl->bitmap.size = window_size; + dl->bitmap.size = nb_elem; return 0; } diff --git a/lib/pdcp/pdcp_cnt.h b/lib/pdcp/pdcp_cnt.h index 5941b7a406..87b011f9dc 100644 --- a/lib/pdcp/pdcp_cnt.h +++ b/lib/pdcp/pdcp_cnt.h @@ -10,7 +10,8 @@ #include "pdcp_entity.h" uint32_t pdcp_cnt_bitmap_get_memory_footprint(const struct rte_pdcp_entity_conf *conf); -int pdcp_cnt_bitmap_create(struct entity_priv_dl_part *dl, void *bitmap_mem, uint32_t window_size); +int pdcp_cnt_bitmap_create(struct entity_priv_dl_part *dl, uint32_t nb_elem, + void *bitmap_mem, uint32_t mem_size); void pdcp_cnt_bitmap_set(struct pdcp_cnt_bitmap bitmap, uint32_t count); bool pdcp_cnt_bitmap_is_set(struct pdcp_cnt_bitmap bitmap, uint32_t count); diff --git a/lib/pdcp/pdcp_entity.h b/lib/pdcp/pdcp_entity.h index a9b1428c7a..9f74b5d0e5 100644 --- a/lib/pdcp/pdcp_entity.h +++ b/lib/pdcp/pdcp_entity.h @@ -132,7 +132,7 @@ struct pdcp_cnt_bitmap { }; /* - * Layout of PDCP entity: [rte_pdcp_entity] [entity_priv] [entity_dl/ul] + * Layout of PDCP entity: [rte_pdcp_entity] [entity_priv] [entity_dl/ul] [reorder/bitmap] */ struct entity_priv { diff --git a/lib/pdcp/pdcp_reorder.c b/lib/pdcp/pdcp_reorder.c index 5399f0dc28..bc45f2e19b 100644 --- a/lib/pdcp/pdcp_reorder.c +++ b/lib/pdcp/pdcp_reorder.c @@ -8,20 +8,13 @@ #include "pdcp_reorder.h" int -pdcp_reorder_create(struct pdcp_reorder *reorder, uint32_t window_size) +pdcp_reorder_create(struct pdcp_reorder *reorder, size_t nb_elem, void *mem, size_t mem_size) { - reorder->buf = rte_reorder_create("reorder_buffer", SOCKET_ID_ANY, window_size); + reorder->buf = rte_reorder_init(mem, mem_size, "reorder_buffer", nb_elem); if (reorder->buf == NULL) return -rte_errno; - reorder->window_size = window_size; reorder->is_active = false; return 0; } - -void -pdcp_reorder_destroy(const struct pdcp_reorder *reorder) -{ - rte_reorder_free(reorder->buf); -} diff --git a/lib/pdcp/pdcp_reorder.h b/lib/pdcp/pdcp_reorder.h index 6a2f61d6ae..7e4f079d4b 100644 --- a/lib/pdcp/pdcp_reorder.h +++ b/lib/pdcp/pdcp_reorder.h @@ -9,12 +9,18 @@ struct pdcp_reorder { struct rte_reorder_buffer *buf; - uint32_t window_size; bool is_active; }; -int pdcp_reorder_create(struct pdcp_reorder *reorder, uint32_t window_size); -void pdcp_reorder_destroy(const struct pdcp_reorder *reorder); +int pdcp_reorder_create(struct pdcp_reorder *reorder, size_t nb_elem, void *mem, size_t mem_size); + +/* NOTE: replace with `rte_reorder_memory_footprint_get` after DPDK 23.07 */ +#define SIZE_OF_REORDER_BUFFER (4 * RTE_CACHE_LINE_SIZE) +static inline size_t +pdcp_reorder_memory_footprint_get(size_t nb_elem) +{ + return SIZE_OF_REORDER_BUFFER + (2 * nb_elem * sizeof(struct rte_mbuf *)); +} static inline uint32_t pdcp_reorder_get_sequential(struct pdcp_reorder *reorder, struct rte_mbuf **mbufs, diff --git a/lib/pdcp/rte_pdcp.c b/lib/pdcp/rte_pdcp.c index 9865c620b7..1c6d2466b2 100644 --- a/lib/pdcp/rte_pdcp.c +++ b/lib/pdcp/rte_pdcp.c @@ -14,7 +14,15 @@ #define RTE_PDCP_DYNFIELD_NAME "rte_pdcp_dynfield" -static int bitmap_mem_offset; +struct entity_layout { + size_t bitmap_offset; + size_t bitmap_size; + + size_t reorder_buf_offset; + size_t reorder_buf_size; + + size_t total_size; +}; int rte_pdcp_dynfield_offset = -1; @@ -35,46 +43,54 @@ pdcp_dynfield_register(void) } static int -pdcp_entity_size_get(const struct rte_pdcp_entity_conf *conf) +pdcp_entity_layout_get(const struct rte_pdcp_entity_conf *conf, s
[PATCH v5 08/21] pdcp: add IV generation routines
For PDCP, IV generated has varying formats depending on the ciphering and authentication algorithm used. Add routines to populate IV accordingly. Signed-off-by: Anoob Joseph Signed-off-by: Volodymyr Fialko --- lib/pdcp/pdcp_entity.h | 87 lib/pdcp/pdcp_process.c | 284 2 files changed, 371 insertions(+) diff --git a/lib/pdcp/pdcp_entity.h b/lib/pdcp/pdcp_entity.h index 1d4a43a3bc..10a72faae1 100644 --- a/lib/pdcp/pdcp_entity.h +++ b/lib/pdcp/pdcp_entity.h @@ -26,6 +26,89 @@ struct entity_state { uint32_t rx_reord; }; +union auth_iv_partial { + /* For AES-CMAC, there is no IV, but message gets prepended */ + struct { +#if RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN + uint64_t count : 32; + uint64_t zero_38_39 : 2; + uint64_t direction : 1; + uint64_t bearer : 5; + uint64_t zero_40_63 : 24; +#else + uint64_t count : 32; + uint64_t bearer : 5; + uint64_t direction : 1; + uint64_t zero_38_39 : 2; + uint64_t zero_40_63 : 24; +#endif + } aes_cmac; + struct { +#if RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN + uint64_t count : 32; + uint64_t zero_37_39 : 3; + uint64_t bearer : 5; + uint64_t zero_40_63 : 24; + + uint64_t rsvd_65_71 : 7; + uint64_t direction_64 : 1; + uint64_t rsvd_72_111 : 40; + uint64_t rsvd_113_119 : 7; + uint64_t direction_112 : 1; + uint64_t rsvd_120_127 : 8; +#else + uint64_t count : 32; + uint64_t bearer : 5; + uint64_t zero_37_39 : 3; + uint64_t zero_40_63 : 24; + + uint64_t direction_64 : 1; + uint64_t rsvd_65_71 : 7; + uint64_t rsvd_72_111 : 40; + uint64_t direction_112 : 1; + uint64_t rsvd_113_119 : 7; + uint64_t rsvd_120_127 : 8; +#endif + } zs; + uint64_t u64[2]; +}; + +union cipher_iv_partial { + struct { +#if RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN + uint64_t count : 32; + uint64_t zero_38_39 : 2; + uint64_t direction : 1; + uint64_t bearer : 5; + uint64_t zero_40_63 : 24; +#else + uint64_t count : 32; + uint64_t bearer : 5; + uint64_t direction : 1; + uint64_t zero_38_39 : 2; + uint64_t zero_40_63 : 24; +#endif + uint64_t zero_64_127; + } aes_ctr; + struct { +#if RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN + uint64_t count : 32; + uint64_t zero_38_39 : 2; + uint64_t direction : 1; + uint64_t bearer : 5; + uint64_t zero_40_63 : 24; +#else + uint64_t count : 32; + uint64_t bearer : 5; + uint64_t direction : 1; + uint64_t zero_38_39 : 2; + uint64_t zero_40_63 : 24; +#endif + uint64_t rsvd_64_127; + } zs; + uint64_t u64[2]; +}; + /* * Layout of PDCP entity: [rte_pdcp_entity] [entity_priv] [entity_dl/ul] */ @@ -35,6 +118,10 @@ struct entity_priv { struct rte_cryptodev_sym_session *crypto_sess; /** Entity specific IV generation function. */ iv_gen_t iv_gen; + /** Pre-prepared auth IV. */ + union auth_iv_partial auth_iv_part; + /** Pre-prepared cipher IV. */ + union cipher_iv_partial cipher_iv_part; /** Entity state variables. */ struct entity_state state; /** Flags. */ diff --git a/lib/pdcp/pdcp_process.c b/lib/pdcp/pdcp_process.c index bd75e6f802..28ac4102da 100644 --- a/lib/pdcp/pdcp_process.c +++ b/lib/pdcp/pdcp_process.c @@ -14,6 +14,181 @@ #include "pdcp_entity.h" #include "pdcp_process.h" +/* Enum of supported algorithms for ciphering */ +enum pdcp_cipher_algo { + PDCP_CIPHER_ALGO_NULL, + PDCP_CIPHER_ALGO_AES, + PDCP_CIPHER_ALGO_ZUC, + PDCP_CIPHER_ALGO_SNOW3G, + PDCP_CIPHER_ALGO_MAX +}; + +/* Enum of supported algorithms for integrity */ +enum pdcp_auth_algo { + PDCP_AUTH_ALGO_NULL, + PDCP_AUTH_ALGO_AES, + PDCP_AUTH_ALGO_ZUC, + PDCP_AUTH_ALGO_SNOW3G, + PDCP_AUTH_ALGO_MAX +}; + +/* IV generation functions based on type of operation (cipher - auth) */ + +static void +pdcp_iv_gen_null_null(struct rte_crypto_op *cop, const struct entity_priv *en_priv, uint32_t count) +{ + /* No IV required for NULL cipher + NULL auth */ + RTE_SET_USED(cop); + RTE_SET_USED(en_priv); + RTE_SET_USED(count); +} + +static void +pdcp_iv_gen_null_aes_cmac(struct rte_crypto_op *cop, const struct entity_priv *en_priv, + uint32_t count) +{ + struct rte_crypto_sym_op *op = cop->sym; + struct rt
[PATCH v5 11/21] doc: add PDCP library guide
Add guide for PDCP library. Signed-off-by: Anoob Joseph Signed-off-by: Kiran Kumar K Signed-off-by: Volodymyr Fialko --- MAINTAINERS | 1 + .../img/pdcp_functional_overview.svg | 1 + doc/guides/prog_guide/index.rst | 1 + doc/guides/prog_guide/pdcp_lib.rst| 254 ++ doc/guides/rel_notes/release_23_07.rst| 12 + 5 files changed, 269 insertions(+) create mode 100644 doc/guides/prog_guide/img/pdcp_functional_overview.svg create mode 100644 doc/guides/prog_guide/pdcp_lib.rst diff --git a/MAINTAINERS b/MAINTAINERS index 15dd88af3a..4345ae21b6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1555,6 +1555,7 @@ M: Volodymyr Fialko T: git://dpdk.org/next/dpdk-next-crypto F: lib/pdcp/ F: app/test/test_pdcp* +F: doc/guides/prog_guide/pdcp_lib.rst Packet Framework diff --git a/doc/guides/prog_guide/img/pdcp_functional_overview.svg b/doc/guides/prog_guide/img/pdcp_functional_overview.svg new file mode 100644 index 00..287daafc21 --- /dev/null +++ b/doc/guides/prog_guide/img/pdcp_functional_overview.svg @@ -0,0 +1 @@ +http://www.w3.org/2000/svg"; xmlns:xlink="http://www.w3.org/1999/xlink"; overflow="hidden">Radio Interface (Uu/PC5)UE/NG-RAN/UE ANG-RAN/UE/UE BTransmitting PDCP entityReceiving PDCP entityTransmission buffer:SequencenumberingHeader or uplink dataCompressionHeader or uplink dataDecompressionRouting / DuplicationAdd PDCP headerCipheringIntegrity protectionPackets associated to a PDCP SDUPackets not associated to a PDCP SDURemove PDCP HeaderDecipheringIntegrity VerificationReception buffer:ReorderingDuplicate discardingPackets associated to a PDCP SDUPackets not associated to a PDCP SDU \ No newline at end of file diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst index 87333ee84a..6099ff63cd 100644 --- a/doc/guides/prog_guide/index.rst +++ b/doc/guides/prog_guide/index.rst @@ -77,4 +77,5 @@ Programmer's Guide lto profile_app asan +pdcp_lib glossary diff --git a/doc/guides/prog_guide/pdcp_lib.rst b/doc/guides/prog_guide/pdcp_lib.rst new file mode 100644 index 00..8369c71600 --- /dev/null +++ b/doc/guides/prog_guide/pdcp_lib.rst @@ -0,0 +1,254 @@ +.. SPDX-License-Identifier: BSD-3-Clause +Copyright(C) 2023 Marvell. + +PDCP Protocol Processing Library + + +DPDK provides a library for PDCP protocol processing. The library utilizes +other DPDK libraries such as cryptodev, reorder, etc., to provide the +application with a transparent and high performant PDCP protocol processing +library. + +The library abstracts complete PDCP protocol processing conforming to +``ETSI TS 138 323 V17.1.0 (2022-08)``. +https://www.etsi.org/deliver/etsi_ts/138300_138399/138323/17.01.00_60/ts_138323v170100p.pdf + +PDCP would involve the following operations, + +1. Transfer of user plane data +2. Transfer of control plane data +3. Header compression +4. Uplink data compression +5. Ciphering and integrity protection + +.. _figure_pdcp_functional_overview: + +.. figure:: img/pdcp_functional_overview.* + + PDCP functional overview new + +PDCP library would abstract the protocol offload features of the cryptodev and +would provide a uniform interface and consistent API usage to work with +cryptodev irrespective of the protocol offload features supported. + +PDCP entity API +--- + +PDCP library provides following control path APIs that is used to +configure various PDCP entities, + +1. ``rte_pdcp_entity_establish()`` +2. ``rte_pdcp_entity_suspend()`` +3. ``rte_pdcp_entity_release()`` + +A PDCP entity would translate to one ``rte_cryptodev_sym_session`` or +``rte_security_session`` based on the config. The sessions would be created/ +destroyed while corresponding PDCP entity operations are performed. + +When upper layers request a PDCP entity suspend (``rte_pdcp_entity_suspend()``), +it would result in flushing out of all cached packets. Internal state variables +are updated as described in 5.1.4. + +When upper layers request a PDCP entity release (``rte_pdcp_entity_release()``), +it would result in flushing out of all cached packets and releasing of all +memory associated with the entity. It would internally free any crypto/security +sessions created. All procedures mentioned in 5.1.3 would be performed. + +PDCP PDU (Protocol Data Unit) API +- + +PDCP PDUs can be categorized as, + +1. Control PDU +2. Data PDU + +Control PDUs are used for signalling between entities on either end and can be +one of the following, + +1. PDCP status report +2. ROHC feedback +3. EHC feedback + +Control PDUs are not ciphered or authenticated, and so such packets are not +submitted to cryptodev for processing. + +Data PDUs are regular packets submitted by upper layers for transmission to +other end. Such packets would need to be ciphered and authenticated based on +the entity configu
[PATCH v5 01/21] net: add PDCP header
From: Volodymyr Fialko Add PDCP protocol header to be used for supporting PDCP protocol processing. Signed-off-by: Anoob Joseph Signed-off-by: Kiran Kumar K Signed-off-by: Volodymyr Fialko Acked-by: Akhil Goyal --- doc/api/doxy-api-index.md | 3 +- lib/net/meson.build | 1 + lib/net/rte_pdcp_hdr.h| 147 ++ 3 files changed, 150 insertions(+), 1 deletion(-) create mode 100644 lib/net/rte_pdcp_hdr.h diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md index c709fd48ad..debbe4134f 100644 --- a/doc/api/doxy-api-index.md +++ b/doc/api/doxy-api-index.md @@ -127,7 +127,8 @@ The public API headers are grouped by topics: [Geneve](@ref rte_geneve.h), [eCPRI](@ref rte_ecpri.h), [L2TPv2](@ref rte_l2tpv2.h), - [PPP](@ref rte_ppp.h) + [PPP](@ref rte_ppp.h), + [PDCP hdr](@ref rte_pdcp_hdr.h) - **QoS**: [metering](@ref rte_meter.h), diff --git a/lib/net/meson.build b/lib/net/meson.build index 379d161ee0..bd56f91c22 100644 --- a/lib/net/meson.build +++ b/lib/net/meson.build @@ -22,6 +22,7 @@ headers = files( 'rte_geneve.h', 'rte_l2tpv2.h', 'rte_ppp.h', +'rte_pdcp_hdr.h', ) sources = files( diff --git a/lib/net/rte_pdcp_hdr.h b/lib/net/rte_pdcp_hdr.h new file mode 100644 index 00..72ae9a66cb --- /dev/null +++ b/lib/net/rte_pdcp_hdr.h @@ -0,0 +1,147 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(C) 2023 Marvell. + */ + +#ifndef RTE_PDCP_HDR_H +#define RTE_PDCP_HDR_H + +/** + * @file + * + * PDCP-related defines + * + * Based on - ETSI TS 138 323 V17.1.0 (2022-08) + * https://www.etsi.org/deliver/etsi_ts/138300_138399/138323/17.01.00_60/ts_138323v170100p.pdf + */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * 4.3.1 + * + * Indicate the maximum supported size of a PDCP Control PDU. + */ +#define RTE_PDCP_CTRL_PDU_SIZE_MAX 9000u + +/** + * 6.3.4 MAC-I + * + * Indicate the size of MAC-I in PDCP PDU. + */ +#define RTE_PDCP_MAC_I_LEN 4 + +/** + * Indicate type of control information included in the corresponding PDCP + * Control PDU. + */ +enum rte_pdcp_ctrl_pdu_type { + RTE_PDCP_CTRL_PDU_TYPE_STATUS_REPORT = 0, + RTE_PDCP_CTRL_PDU_TYPE_ROHC_FEEDBACK = 1, + RTE_PDCP_CTRL_PDU_TYPE_EHC_FEEDBACK = 2, + RTE_PDCP_CRTL_PDU_TYPE_UDC_FEEDBACK = 3, +}; + +/** + * 6.3.7 D/C + * + * This field indicates whether the corresponding PDCP PDU is a + * PDCP Data PDU or a PDCP Control PDU. + */ +enum rte_pdcp_pdu_type { + RTE_PDCP_PDU_TYPE_CTRL = 0, + RTE_PDCP_PDU_TYPE_DATA = 1, +}; + +/** + * 6.2.2.1 Data PDU for SRBs + */ +__extension__ +struct rte_pdcp_cp_data_pdu_sn_12_hdr { +#if RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN + uint8_t sn_11_8 : 4;/**< Sequence number bits 8-11 */ + uint8_t r : 4; /**< Reserved */ +#elif RTE_BYTE_ORDER == RTE_BIG_ENDIAN + uint8_t r : 4; /**< Reserved */ + uint8_t sn_11_8 : 4;/**< Sequence number bits 8-11 */ +#endif + uint8_t sn_7_0; /**< Sequence number bits 0-7 */ +} __rte_packed; + +/** + * 6.2.2.2 Data PDU for DRBs and MRBs with 12 bits PDCP SN + */ +__extension__ +struct rte_pdcp_up_data_pdu_sn_12_hdr { +#if RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN + uint8_t sn_11_8 : 4;/**< Sequence number bits 8-11 */ + uint8_t r : 3; /**< Reserved */ + uint8_t d_c : 1;/**< D/C bit */ +#elif RTE_BYTE_ORDER == RTE_BIG_ENDIAN + uint8_t d_c : 1;/**< D/C bit */ + uint8_t r : 3; /**< Reserved */ + uint8_t sn_11_8 : 4;/**< Sequence number bits 8-11 */ +#endif + uint8_t sn_7_0; /**< Sequence number bits 0-7 */ +} __rte_packed; + +/** + * 6.2.2.3 Data PDU for DRBs and MRBs with 18 bits PDCP SN + */ +__extension__ +struct rte_pdcp_up_data_pdu_sn_18_hdr { +#if RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN + uint8_t sn_17_16 : 2; /**< Sequence number bits 16-17 */ + uint8_t r : 5; /**< Reserved */ + uint8_t d_c : 1;/**< D/C bit */ +#elif RTE_BYTE_ORDER == RTE_BIG_ENDIAN + uint8_t d_c : 1;/**< D/C bit */ + uint8_t r : 5; /**< Reserved */ + uint8_t sn_17_16 : 2; /**< Sequence number bits 16-17 */ +#endif + uint8_t sn_15_8;/**< Sequence number bits 8-15 */ + uint8_t sn_7_0; /**< Sequence number bits 0-7 */ +} __rte_packed; + +/** + * 6.2.3.1 Control PDU for PDCP status report + */ +__extension__ +struct rte_pdcp_up_ctrl_pdu_hdr { +#if RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN + uint8_t r : 4; /**< Reserved */ + uint8_t pdu_type : 3; /**< Control PDU type */ + uint8_t d_c : 1;/**< D/C bit */ +#elif RTE_BYTE_ORDER == RTE_BIG_ENDIAN + uint8_t d_c : 1;/**< D/C bit */ + uint8_t pdu_type : 3; /**< Control PDU type */ + uint8_t r : 4; /**< Reserved */ +#endif + /** +* 6.3.9 FMC +* +* First Missing COUNT. This fiel
[PATCH v5 17/21] test/pdcp: add timer expiry cases
From: Volodymyr Fialko Add test cases for handling the expiry with rte_timer and rte_event_timer. Signed-off-by: Anoob Joseph Signed-off-by: Volodymyr Fialko --- app/test/test_pdcp.c | 350 +++ 1 file changed, 350 insertions(+) diff --git a/app/test/test_pdcp.c b/app/test/test_pdcp.c index 24d7826bc2..25729b2bdd 100644 --- a/app/test/test_pdcp.c +++ b/app/test/test_pdcp.c @@ -3,15 +3,24 @@ */ #include +#ifdef RTE_LIB_EVENTDEV +#include +#include +#endif /* RTE_LIB_EVENTDEV */ #include #include #include +#include #include "test.h" #include "test_cryptodev.h" #include "test_cryptodev_security_pdcp_test_vectors.h" +#define NSECPERSEC 1E9 #define NB_DESC 1024 +#define TIMER_ADAPTER_ID 0 +#define TEST_EV_QUEUE_ID 0 +#define TEST_EV_PORT_ID 0 #define CDEV_INVALID_ID UINT8_MAX #define NB_TESTS RTE_DIM(pdcp_test_params) #define PDCP_IV_LEN 16 @@ -33,10 +42,21 @@ struct pdcp_testsuite_params { struct rte_mempool *cop_pool; struct rte_mempool *sess_pool; bool cdevs_used[RTE_CRYPTO_MAX_DEVS]; + int evdev; +#ifdef RTE_LIB_EVENTDEV + struct rte_event_timer_adapter *timdev; +#endif /* RTE_LIB_EVENTDEV */ + bool timer_is_running; + uint64_t min_resolution_ns; }; static struct pdcp_testsuite_params testsuite_params; +struct test_rte_timer_args { + int status; + struct rte_pdcp_entity *pdcp_entity; +}; + struct pdcp_test_conf { struct rte_pdcp_entity_conf entity; struct rte_crypto_sym_xform c_xfrm; @@ -124,6 +144,30 @@ pdcp_hfn_from_count_get(uint32_t count, enum rte_security_pdcp_sn_size sn_size) return (count & pdcp_hfn_mask_get(sn_size)) >> sn_size; } +static void +pdcp_timer_start_cb(void *timer, void *args) +{ + bool *is_timer_running = timer; + + RTE_SET_USED(args); + *is_timer_running = true; +} + +static void +pdcp_timer_stop_cb(void *timer, void *args) +{ + bool *is_timer_running = timer; + + RTE_SET_USED(args); + *is_timer_running = false; +} + +static struct rte_pdcp_t_reordering t_reorder_timer = { + .timer = &testsuite_params.timer_is_running, + .start = pdcp_timer_start_cb, + .stop = pdcp_timer_stop_cb, +}; + static inline int pdcp_hdr_size_get(enum rte_security_pdcp_sn_size sn_size) { @@ -462,6 +506,7 @@ create_test_conf_from_index(const int index, struct pdcp_test_conf *conf) conf->entity.pdcp_xfrm.en_ordering = 0; conf->entity.pdcp_xfrm.remove_duplicates = 0; conf->entity.pdcp_xfrm.domain = pdcp_test_params[index].domain; + conf->entity.t_reordering = t_reorder_timer; if (pdcp_test_packet_direction[index] == PDCP_DIR_UPLINK) conf->entity.pdcp_xfrm.pkt_dir = RTE_SECURITY_PDCP_UPLINK; @@ -1048,6 +1093,8 @@ test_reorder_gap_fill(struct pdcp_test_conf *ul_conf) /* Check that packets in correct order */ ASSERT_TRUE_OR_GOTO(array_asc_sorted_check(out_mb, nb_success, sn_size), exit, "Error occurred during packet drain\n"); + ASSERT_TRUE_OR_GOTO(testsuite_params.timer_is_running == false, exit, + "Timer should be stopped after full drain\n"); ret = TEST_SUCCESS; exit: @@ -1123,6 +1170,181 @@ test_reorder_buffer_full_window_size_sn_12(const struct pdcp_test_conf *ul_conf) return ret; } +#ifdef RTE_LIB_EVENTDEV +static void +event_timer_start_cb(void *timer, void *args) +{ + struct rte_event_timer *evtims = args; + int ret = 0; + + ret = rte_event_timer_arm_burst(timer, &evtims, 1); + assert(ret == 1); +} +#endif /* RTE_LIB_EVENTDEV */ + +static int +test_expiry_with_event_timer(const struct pdcp_test_conf *ul_conf) +{ +#ifdef RTE_LIB_EVENTDEV + const enum rte_security_pdcp_sn_size sn_size = ul_conf->entity.pdcp_xfrm.sn_size; + struct rte_mbuf *m1 = NULL, *out_mb[1] = {0}; + uint16_t n = 0, nb_err = 0, nb_try = 5; + struct rte_pdcp_entity *pdcp_entity; + struct pdcp_test_conf dl_conf; + int ret = TEST_FAILED, nb_out; + struct rte_event event; + + const int start_count = 0; + struct rte_event_timer evtim = { + .ev.op = RTE_EVENT_OP_NEW, + .ev.queue_id = TEST_EV_QUEUE_ID, + .ev.sched_type = RTE_SCHED_TYPE_ATOMIC, + .ev.priority = RTE_EVENT_DEV_PRIORITY_NORMAL, + .ev.event_type = RTE_EVENT_TYPE_TIMER, + .state = RTE_EVENT_TIMER_NOT_ARMED, + .timeout_ticks = 1, + }; + + if (ul_conf->entity.pdcp_xfrm.pkt_dir == RTE_SECURITY_PDCP_DOWNLINK) + return TEST_SKIPPED; + + /* Create configuration for actual testing */ + uplink_to_downlink_convert(ul_conf, &dl_conf); + dl_conf.entity.pdcp_xfrm.hfn = pdcp_hfn_from_count_get(start_count, sn_size); + dl_conf.entity.sn = pdcp_sn_from_count_get(start_count, sn_size); + dl_conf.entity.t_r
[PATCH v5 14/21] test/pdcp: add in-order delivery cases
From: Volodymyr Fialko Add test cases to verify behaviour when in-order delivery is enabled and packets arrive in out-of-order. PDCP library is expected to buffer the packets and return packets in-order when the missing packet arrives. Signed-off-by: Anoob Joseph Signed-off-by: Volodymyr Fialko --- app/test/test_pdcp.c | 223 +++ 1 file changed, 223 insertions(+) diff --git a/app/test/test_pdcp.c b/app/test/test_pdcp.c index cfe2ec6aa9..24d7826bc2 100644 --- a/app/test/test_pdcp.c +++ b/app/test/test_pdcp.c @@ -16,6 +16,15 @@ #define NB_TESTS RTE_DIM(pdcp_test_params) #define PDCP_IV_LEN 16 +/* Assert that condition is true, or goto the mark */ +#define ASSERT_TRUE_OR_GOTO(cond, mark, ...) do {\ + if (!(cond)) { \ + RTE_LOG(ERR, USER1, "Error at: %s:%d\n", __func__, __LINE__); \ + RTE_LOG(ERR, USER1, __VA_ARGS__); \ + goto mark; \ + } \ +} while (0) + /* According to formula(7.2.a Window_Size) */ #define PDCP_WINDOW_SIZE(sn_size) (1 << (sn_size - 1)) @@ -83,6 +92,38 @@ run_test_with_all_known_vec(const void *args) return run_test_foreach_known_vec(test, false); } +static int +run_test_with_all_known_vec_until_first_pass(const void *args) +{ + test_with_conf_t test = args; + + return run_test_foreach_known_vec(test, true); +} + +static inline uint32_t +pdcp_sn_mask_get(enum rte_security_pdcp_sn_size sn_size) +{ + return (1 << sn_size) - 1; +} + +static inline uint32_t +pdcp_sn_from_count_get(uint32_t count, enum rte_security_pdcp_sn_size sn_size) +{ + return (count & pdcp_sn_mask_get(sn_size)); +} + +static inline uint32_t +pdcp_hfn_mask_get(enum rte_security_pdcp_sn_size sn_size) +{ + return ~pdcp_sn_mask_get(sn_size); +} + +static inline uint32_t +pdcp_hfn_from_count_get(uint32_t count, enum rte_security_pdcp_sn_size sn_size) +{ + return (count & pdcp_hfn_mask_get(sn_size)) >> sn_size; +} + static inline int pdcp_hdr_size_get(enum rte_security_pdcp_sn_size sn_size) { @@ -416,6 +457,7 @@ create_test_conf_from_index(const int index, struct pdcp_test_conf *conf) conf->entity.sess_mpool = ts_params->sess_pool; conf->entity.cop_pool = ts_params->cop_pool; + conf->entity.ctrl_pdu_pool = ts_params->mbuf_pool; conf->entity.pdcp_xfrm.bearer = pdcp_test_bearer[index]; conf->entity.pdcp_xfrm.en_ordering = 0; conf->entity.pdcp_xfrm.remove_duplicates = 0; @@ -868,6 +910,7 @@ test_sn_range_type(enum sn_range_type type, struct pdcp_test_conf *conf) /* Configure Uplink to generate expected, encrypted packet */ pdcp_sn_to_raw_set(conf->input, new_sn, conf->entity.pdcp_xfrm.sn_size); + conf->entity.out_of_order_delivery = true; conf->entity.reverse_iv_direction = true; conf->entity.pdcp_xfrm.hfn = new_hfn; conf->entity.sn = new_sn; @@ -915,6 +958,171 @@ test_sn_minus_outside(struct pdcp_test_conf *t_conf) return test_sn_range_type(SN_RANGE_MINUS_OUTSIDE, t_conf); } +static struct rte_mbuf * +generate_packet_for_dl_with_sn(struct pdcp_test_conf ul_conf, uint32_t count) +{ + enum rte_security_pdcp_sn_size sn_size = ul_conf.entity.pdcp_xfrm.sn_size; + int ret; + + ul_conf.entity.pdcp_xfrm.hfn = pdcp_hfn_from_count_get(count, sn_size); + ul_conf.entity.sn = pdcp_sn_from_count_get(count, sn_size); + ul_conf.entity.out_of_order_delivery = true; + ul_conf.entity.reverse_iv_direction = true; + ul_conf.output_len = 0; + + ret = test_attempt_single(&ul_conf); + if (ret != TEST_SUCCESS) + return NULL; + + return mbuf_from_data_create(ul_conf.output, ul_conf.output_len); +} + +static bool +array_asc_sorted_check(struct rte_mbuf *m[], uint32_t len, enum rte_security_pdcp_sn_size sn_size) +{ + uint32_t i; + + if (len < 2) + return true; + + for (i = 0; i < (len - 1); i++) { + if (pdcp_sn_from_raw_get(rte_pktmbuf_mtod(m[i], void *), sn_size) > + pdcp_sn_from_raw_get(rte_pktmbuf_mtod(m[i + 1], void *), sn_size)) + return false; + } + + return true; +} + +static int +test_reorder_gap_fill(struct pdcp_test_conf *ul_conf) +{ + const enum rte_security_pdcp_sn_size sn_size = ul_conf->entity.pdcp_xfrm.sn_size; + struct rte_mbuf *m0 = NULL, *m1 = NULL, *out_mb[2] = {0}; + uint16_t nb_success = 0, nb_err = 0; + struct rte_pdcp_entity *pdcp_entity; + struct pdcp_test_conf dl_conf; + int ret = TEST_FAILED, nb_out; + uint8_t cdev_id; + + const int start_count = 0; + + if (ul_conf->entity.pdcp_xfrm.pkt_dir == RTE_SECURITY_PDCP_DOWNLINK) + return TEST_SKIPPED; + + /* Create configuration for actual testing */ + uplink_to_downlink_convert(ul_conf, &dl_conf); + dl_conf.entity.pdcp_xfrm.hfn = pdcp_hfn_from_count_get(start_count, sn_
[PATCH v5 16/21] pdcp: add timer expiry handle
From: Volodymyr Fialko The PDCP protocol requires usage of timers to keep track of how long an out-of-order packet should be buffered while waiting for missing packets. Applications can register a desired timer implementation with the PDCP library. Once the timer expires, the application will be notified, and further handling of the event will be performed in the PDCP library. When the timer expires, the PDCP library will return the cached packets, and PDCP internal state variables (like RX_REORD, RX_DELIV etc) will be updated accordingly. Signed-off-by: Anoob Joseph Signed-off-by: Volodymyr Fialko --- doc/guides/prog_guide/pdcp_lib.rst | 30 ++ lib/pdcp/rte_pdcp.c| 49 ++ lib/pdcp/rte_pdcp.h| 31 +++ lib/pdcp/version.map | 2 ++ 4 files changed, 112 insertions(+) diff --git a/doc/guides/prog_guide/pdcp_lib.rst b/doc/guides/prog_guide/pdcp_lib.rst index dcb424bb1d..16deaead15 100644 --- a/doc/guides/prog_guide/pdcp_lib.rst +++ b/doc/guides/prog_guide/pdcp_lib.rst @@ -130,6 +130,36 @@ Supported integrity protection algorithms - SNOW3G-AUTH - ZUC-AUTH +Timers +-- + +PDCP utilizes a reception window mechanism to limit the bits of COUNT value +transmitted in the packet. It utilizes state variables such as RX_REORD, +RX_DELIV to define the window and uses RX_DELIV as the lower pivot point of the +window. + +RX_DELIV would be updated only when packets are received in-order. Any missing +packet would mean RX_DELIV won't be updated. A timer, t-Reordering, helps PDCP +to slide the window if the missing packet is not received in a specified time +duration. + +While starting and stopping the timer need to be done by lib PDCP, application +could register its own timer implementation. This is to make sure application +can choose between timers such as rte_timer and rte_event based timers. Starting +and stopping of timer would happen during pre & post process API. + +When the t-Reordering timer expires, application would receive the expiry event. +To perform the PDCP handling of the expiry event, +``rte_pdcp_t_reordering_expiry_handle`` can be used. Expiry handling would +involve sliding the window by updating state variables and passing the expired +packets to the application. + +.. literalinclude:: ../../../lib/pdcp/rte_pdcp.h + :language: c + :start-after: Structure rte_pdcp_t_reordering 8< + :end-before: >8 End of structure rte_pdcp_t_reordering. + + Sample API usage diff --git a/lib/pdcp/rte_pdcp.c b/lib/pdcp/rte_pdcp.c index a0558b99ae..819c66bd08 100644 --- a/lib/pdcp/rte_pdcp.c +++ b/lib/pdcp/rte_pdcp.c @@ -251,3 +251,52 @@ rte_pdcp_control_pdu_create(struct rte_pdcp_entity *pdcp_entity, return m; } + +uint16_t +rte_pdcp_t_reordering_expiry_handle(const struct rte_pdcp_entity *entity, struct rte_mbuf *out_mb[]) +{ + struct entity_priv_dl_part *dl = entity_dl_part_get(entity); + struct entity_priv *en_priv = entity_priv_get(entity); + uint16_t capacity = entity->max_pkt_cache; + uint16_t nb_out, nb_seq; + + /* 5.2.2.2 Actions when a t-Reordering expires */ + + /* +* - deliver to upper layers in ascending order of the associated COUNT value after +* performing header decompression, if not decompressed before: +*/ + + /* - all stored PDCP SDU(s) with associated COUNT value(s) < RX_REORD; */ + nb_out = pdcp_reorder_up_to_get(&dl->reorder, out_mb, capacity, en_priv->state.rx_reord); + capacity -= nb_out; + out_mb = &out_mb[nb_out]; + + /* +* - all stored PDCP SDU(s) with consecutively associated COUNT value(s) starting from +* RX_REORD; +*/ + nb_seq = pdcp_reorder_get_sequential(&dl->reorder, out_mb, capacity); + nb_out += nb_seq; + + /* +* - update RX_DELIV to the COUNT value of the first PDCP SDU which has not been delivered +* to upper layers, with COUNT value >= RX_REORD; +*/ + en_priv->state.rx_deliv = en_priv->state.rx_reord + nb_seq; + + /* +* - if RX_DELIV < RX_NEXT: +* - update RX_REORD to RX_NEXT; +* - start t-Reordering. +*/ + if (en_priv->state.rx_deliv < en_priv->state.rx_next) { + en_priv->state.rx_reord = en_priv->state.rx_next; + dl->t_reorder.state = TIMER_RUNNING; + dl->t_reorder.handle.start(dl->t_reorder.handle.timer, dl->t_reorder.handle.args); + } else { + dl->t_reorder.state = TIMER_EXPIRED; + } + + return nb_out; +} diff --git a/lib/pdcp/rte_pdcp.h b/lib/pdcp/rte_pdcp.h index 05c922819e..b926b0df29 100644 --- a/lib/pdcp/rte_pdcp.h +++ b/lib/pdcp/rte_pdcp.h @@ -101,6 +101,7 @@ typedef void (*rte_pdcp_t_reordering_stop_cb_t)(void *timer, void *args); * * Configuration provided by user, that PDCP library will invoke according to ti
[PATCH v5 04/21] pdcp: add packet group
Crypto processing in PDCP is performed asynchronously by rte_cryptodev_enqueue_burst() and rte_cryptodev_dequeue_burst(). Since cryptodev dequeue can return crypto operations belonging to multiple entities, rte_pdcp_pkt_crypto_group() is added to help grouping crypto operations belonging to same entity. Signed-off-by: Anoob Joseph Signed-off-by: Kiran Kumar K Signed-off-by: Volodymyr Fialko --- lib/pdcp/meson.build | 1 + lib/pdcp/rte_pdcp.h | 6 ++ lib/pdcp/rte_pdcp_group.h | 131 ++ lib/pdcp/version.map | 3 + 4 files changed, 141 insertions(+) create mode 100644 lib/pdcp/rte_pdcp_group.h diff --git a/lib/pdcp/meson.build b/lib/pdcp/meson.build index ccaf426240..08679b743a 100644 --- a/lib/pdcp/meson.build +++ b/lib/pdcp/meson.build @@ -13,5 +13,6 @@ sources = files( 'rte_pdcp.c', ) headers = files('rte_pdcp.h') +indirect_headers += files('rte_pdcp_group.h') deps += ['mbuf', 'net', 'cryptodev', 'security'] diff --git a/lib/pdcp/rte_pdcp.h b/lib/pdcp/rte_pdcp.h index 46c3c2a416..b88cad4f64 100644 --- a/lib/pdcp/rte_pdcp.h +++ b/lib/pdcp/rte_pdcp.h @@ -257,6 +257,12 @@ rte_pdcp_pkt_post_process(const struct rte_pdcp_entity *entity, return entity->post_process(entity, in_mb, out_mb, num, nb_err); } +/** + * The header 'rte_pdcp_group.h' depends on defines in 'rte_pdcp.h'. So include + * in the end. + */ +#include + #ifdef __cplusplus } #endif diff --git a/lib/pdcp/rte_pdcp_group.h b/lib/pdcp/rte_pdcp_group.h new file mode 100644 index 00..ece3e8c0ff --- /dev/null +++ b/lib/pdcp/rte_pdcp_group.h @@ -0,0 +1,131 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(C) 2023 Marvell. + */ + +#ifndef RTE_PDCP_GROUP_H +#define RTE_PDCP_GROUP_H + +/** + * @file rte_pdcp_group.h + * + * RTE PDCP grouping support. + * It is not recommended to include this file directly, include + * instead. + * Provides helper functions to process completed crypto-ops and group related + * packets by sessions they belong to. + */ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Group packets belonging to same PDCP entity. + */ +struct rte_pdcp_group { + union { + uint64_t val; + void *ptr; + } id; /**< Grouped by value */ + struct rte_mbuf **m; /**< Start of the group */ + uint32_t cnt; /**< Number of entries in the group */ + int32_t rc; /**< Status code associated with the group */ +}; + +/** + * @warning + * @b EXPERIMENTAL: this API may change without prior notice + * + * Take crypto-op as an input and extract pointer to related PDCP entity. + * @param cop + * The address of an input *rte_crypto_op* structure. + * @return + * The pointer to the related *rte_pdcp_entity* structure. + */ +static inline struct rte_pdcp_entity * +rte_pdcp_en_from_cop(const struct rte_crypto_op *cop) +{ + void *sess = cop->sym[0].session; + + return (struct rte_pdcp_entity *)(uintptr_t) + rte_cryptodev_sym_session_opaque_data_get(sess); +} + +/** + * @warning + * @b EXPERIMENTAL: this API may change without prior notice + * + * Take as input completed crypto ops, extract related mbufs and group them by + * *rte_pdcp_entity* they belong to. Mbuf for which the crypto operation has + * failed would be flagged using *RTE_MBUF_F_RX_SEC_OFFLOAD_FAILED* flag + * in rte_mbuf.ol_flags. The crypto_ops would be freed after the grouping. + * + * Note that application must ensure only crypto-ops prepared by lib_pdcp is + * provided back to @see rte_pdcp_pkt_crypto_group(). + * + * @param cop + * The address of an array of *num* pointers to the input *rte_crypto_op* + * structures. + * @param[out] mb + * The address of an array of *num* pointers to output *rte_mbuf* structures. + * @param[out] grp + * The address of an array of *num* to output *rte_pdcp_group* structures. + * @param num + * The maximum number of crypto-ops to process. + * @return + * Number of filled elements in *grp* array. + * + */ +static inline uint16_t +rte_pdcp_pkt_crypto_group(struct rte_crypto_op *cop[], struct rte_mbuf *mb[], + struct rte_pdcp_group grp[], uint16_t num) +{ + uint32_t i, j = 0, n = 0; + void *ns, *ps = NULL; + struct rte_mbuf *m; + + for (i = 0; i != num; i++) { + m = cop[i]->sym[0].m_src; + ns = cop[i]->sym[0].session; + + m->ol_flags |= RTE_MBUF_F_RX_SEC_OFFLOAD; + if (cop[i]->status != RTE_CRYPTO_OP_STATUS_SUCCESS) + m->ol_flags |= RTE_MBUF_F_RX_SEC_OFFLOAD_FAILED; + + /* Different entity */ + if (ps != ns) { + + /* Finalize open group and start a new one */ + if (ps != NULL) { + grp[n].cnt = mb + j - grp[n].m; + n++; + } +
[PATCH v5 16/21] pdcp: add timer expiry handle
From: Volodymyr Fialko The PDCP protocol requires usage of timers to keep track of how long an out-of-order packet should be buffered while waiting for missing packets. Applications can register a desired timer implementation with the PDCP library. Once the timer expires, the application will be notified, and further handling of the event will be performed in the PDCP library. When the timer expires, the PDCP library will return the cached packets, and PDCP internal state variables (like RX_REORD, RX_DELIV etc) will be updated accordingly. Signed-off-by: Anoob Joseph Signed-off-by: Volodymyr Fialko --- doc/guides/prog_guide/pdcp_lib.rst | 30 ++ lib/pdcp/rte_pdcp.c| 49 ++ lib/pdcp/rte_pdcp.h| 31 +++ lib/pdcp/version.map | 2 ++ 4 files changed, 112 insertions(+) diff --git a/doc/guides/prog_guide/pdcp_lib.rst b/doc/guides/prog_guide/pdcp_lib.rst index dcb424bb1d..16deaead15 100644 --- a/doc/guides/prog_guide/pdcp_lib.rst +++ b/doc/guides/prog_guide/pdcp_lib.rst @@ -130,6 +130,36 @@ Supported integrity protection algorithms - SNOW3G-AUTH - ZUC-AUTH +Timers +-- + +PDCP utilizes a reception window mechanism to limit the bits of COUNT value +transmitted in the packet. It utilizes state variables such as RX_REORD, +RX_DELIV to define the window and uses RX_DELIV as the lower pivot point of the +window. + +RX_DELIV would be updated only when packets are received in-order. Any missing +packet would mean RX_DELIV won't be updated. A timer, t-Reordering, helps PDCP +to slide the window if the missing packet is not received in a specified time +duration. + +While starting and stopping the timer need to be done by lib PDCP, application +could register its own timer implementation. This is to make sure application +can choose between timers such as rte_timer and rte_event based timers. Starting +and stopping of timer would happen during pre & post process API. + +When the t-Reordering timer expires, application would receive the expiry event. +To perform the PDCP handling of the expiry event, +``rte_pdcp_t_reordering_expiry_handle`` can be used. Expiry handling would +involve sliding the window by updating state variables and passing the expired +packets to the application. + +.. literalinclude:: ../../../lib/pdcp/rte_pdcp.h + :language: c + :start-after: Structure rte_pdcp_t_reordering 8< + :end-before: >8 End of structure rte_pdcp_t_reordering. + + Sample API usage diff --git a/lib/pdcp/rte_pdcp.c b/lib/pdcp/rte_pdcp.c index a0558b99ae..819c66bd08 100644 --- a/lib/pdcp/rte_pdcp.c +++ b/lib/pdcp/rte_pdcp.c @@ -251,3 +251,52 @@ rte_pdcp_control_pdu_create(struct rte_pdcp_entity *pdcp_entity, return m; } + +uint16_t +rte_pdcp_t_reordering_expiry_handle(const struct rte_pdcp_entity *entity, struct rte_mbuf *out_mb[]) +{ + struct entity_priv_dl_part *dl = entity_dl_part_get(entity); + struct entity_priv *en_priv = entity_priv_get(entity); + uint16_t capacity = entity->max_pkt_cache; + uint16_t nb_out, nb_seq; + + /* 5.2.2.2 Actions when a t-Reordering expires */ + + /* +* - deliver to upper layers in ascending order of the associated COUNT value after +* performing header decompression, if not decompressed before: +*/ + + /* - all stored PDCP SDU(s) with associated COUNT value(s) < RX_REORD; */ + nb_out = pdcp_reorder_up_to_get(&dl->reorder, out_mb, capacity, en_priv->state.rx_reord); + capacity -= nb_out; + out_mb = &out_mb[nb_out]; + + /* +* - all stored PDCP SDU(s) with consecutively associated COUNT value(s) starting from +* RX_REORD; +*/ + nb_seq = pdcp_reorder_get_sequential(&dl->reorder, out_mb, capacity); + nb_out += nb_seq; + + /* +* - update RX_DELIV to the COUNT value of the first PDCP SDU which has not been delivered +* to upper layers, with COUNT value >= RX_REORD; +*/ + en_priv->state.rx_deliv = en_priv->state.rx_reord + nb_seq; + + /* +* - if RX_DELIV < RX_NEXT: +* - update RX_REORD to RX_NEXT; +* - start t-Reordering. +*/ + if (en_priv->state.rx_deliv < en_priv->state.rx_next) { + en_priv->state.rx_reord = en_priv->state.rx_next; + dl->t_reorder.state = TIMER_RUNNING; + dl->t_reorder.handle.start(dl->t_reorder.handle.timer, dl->t_reorder.handle.args); + } else { + dl->t_reorder.state = TIMER_EXPIRED; + } + + return nb_out; +} diff --git a/lib/pdcp/rte_pdcp.h b/lib/pdcp/rte_pdcp.h index 05c922819e..b926b0df29 100644 --- a/lib/pdcp/rte_pdcp.h +++ b/lib/pdcp/rte_pdcp.h @@ -101,6 +101,7 @@ typedef void (*rte_pdcp_t_reordering_stop_cb_t)(void *timer, void *args); * * Configuration provided by user, that PDCP library will invoke according to ti
[PATCH v5 05/21] pdcp: add crypto session create and destroy
Add routines to create & destroy sessions. PDCP lib would take crypto transforms as input and creates the session on the corresponding device after verifying capabilities. Signed-off-by: Anoob Joseph Signed-off-by: Volodymyr Fialko Acked-by: Akhil Goyal --- lib/pdcp/pdcp_crypto.c | 223 - lib/pdcp/pdcp_crypto.h | 5 + 2 files changed, 225 insertions(+), 3 deletions(-) diff --git a/lib/pdcp/pdcp_crypto.c b/lib/pdcp/pdcp_crypto.c index 755e27ec9e..6d2a85dc7d 100644 --- a/lib/pdcp/pdcp_crypto.c +++ b/lib/pdcp/pdcp_crypto.c @@ -2,20 +2,237 @@ * Copyright(C) 2023 Marvell. */ +#include +#include +#include +#include #include +#include #include "pdcp_crypto.h" +#include "pdcp_entity.h" + +static int +pdcp_crypto_caps_cipher_verify(uint8_t dev_id, const struct rte_crypto_sym_xform *c_xfrm) +{ + const struct rte_cryptodev_symmetric_capability *cap; + struct rte_cryptodev_sym_capability_idx cap_idx; + int ret; + + cap_idx.type = RTE_CRYPTO_SYM_XFORM_CIPHER; + cap_idx.algo.cipher = c_xfrm->cipher.algo; + + cap = rte_cryptodev_sym_capability_get(dev_id, &cap_idx); + if (cap == NULL) + return -1; + + ret = rte_cryptodev_sym_capability_check_cipher(cap, c_xfrm->cipher.key.length, + c_xfrm->cipher.iv.length); + + return ret; +} + +static int +pdcp_crypto_caps_auth_verify(uint8_t dev_id, const struct rte_crypto_sym_xform *a_xfrm) +{ + const struct rte_cryptodev_symmetric_capability *cap; + struct rte_cryptodev_sym_capability_idx cap_idx; + int ret; + + cap_idx.type = RTE_CRYPTO_SYM_XFORM_AUTH; + cap_idx.algo.auth = a_xfrm->auth.algo; + + cap = rte_cryptodev_sym_capability_get(dev_id, &cap_idx); + if (cap == NULL) + return -1; + + ret = rte_cryptodev_sym_capability_check_auth(cap, a_xfrm->auth.key.length, + a_xfrm->auth.digest_length, + a_xfrm->auth.iv.length); + + return ret; +} + +static int +pdcp_crypto_xfrm_validate(const struct rte_pdcp_entity_conf *conf, +const struct rte_crypto_sym_xform *c_xfrm, +const struct rte_crypto_sym_xform *a_xfrm, +bool is_auth_then_cipher) +{ + uint16_t cipher_iv_len, auth_digest_len, auth_iv_len; + int ret; + + /* +* Uplink means PDCP entity is configured for transmit. Downlink means PDCP entity is +* configured for receive. When integrity protection is enabled, PDCP always performs +* digest-encrypted or auth-gen-encrypt for uplink (and decrypt-auth-verify for downlink). +* So for uplink, crypto chain would be auth-cipher while for downlink it would be +* cipher-auth. +* +* When integrity protection is not required, xform would be cipher only. +*/ + + if (c_xfrm == NULL) + return -EINVAL; + + if (conf->pdcp_xfrm.pkt_dir == RTE_SECURITY_PDCP_UPLINK) { + + /* With UPLINK, if auth is enabled, it should be before cipher */ + if (a_xfrm != NULL && !is_auth_then_cipher) + return -EINVAL; + + /* With UPLINK, cipher operation must be encrypt */ + if (c_xfrm->cipher.op != RTE_CRYPTO_CIPHER_OP_ENCRYPT) + return -EINVAL; + + /* With UPLINK, auth operation (if present) must be generate */ + if (a_xfrm != NULL && a_xfrm->auth.op != RTE_CRYPTO_AUTH_OP_GENERATE) + return -EINVAL; + + } else if (conf->pdcp_xfrm.pkt_dir == RTE_SECURITY_PDCP_DOWNLINK) { + + /* With DOWNLINK, if auth is enabled, it should be after cipher */ + if (a_xfrm != NULL && is_auth_then_cipher) + return -EINVAL; + + /* With DOWNLINK, cipher operation must be decrypt */ + if (c_xfrm->cipher.op != RTE_CRYPTO_CIPHER_OP_DECRYPT) + return -EINVAL; + + /* With DOWNLINK, auth operation (if present) must be verify */ + if (a_xfrm != NULL && a_xfrm->auth.op != RTE_CRYPTO_AUTH_OP_VERIFY) + return -EINVAL; + + } else { + return -EINVAL; + } + + if ((c_xfrm->cipher.algo != RTE_CRYPTO_CIPHER_NULL) && + (c_xfrm->cipher.algo != RTE_CRYPTO_CIPHER_AES_CTR) && + (c_xfrm->cipher.algo != RTE_CRYPTO_CIPHER_ZUC_EEA3) && + (c_xfrm->cipher.algo != RTE_CRYPTO_CIPHER_SNOW3G_UEA2)) + return -EINVAL; + + if (c_xfrm->cipher.algo == RTE_CRYPTO_CIPHER_NULL) + cipher_iv_len = 0; + else + cipher_iv_len = PDCP_IV_LEN; + + if (cipher_iv_len != c_xfrm->cipher.iv.length) +
[PATCH v5 15/21] pdcp: add timer callback handlers
From: Volodymyr Fialko PDCP has a windowing mechanism which allows only packets that fall in a reception window. The pivot point for this window is RX_REORD which happens to be the first missing or next expected packet. If the missing packet is not received after a specified time, then the RX_REORD state variable needs to be moved up to slide the reception window. PDCP relies on timers for such operations. The timer needs to be armed when PDCP library doesn't receive all packets in-order and starts buffering packets that arrived after a missing packet. The timer needs to be cancelled when a missing packet is received. To avoid dependency on particular timer implementation, PDCP library allows application to register two callbacks, timer_start() and timer_stop() that will be called later by library. Signed-off-by: Anoob Joseph Signed-off-by: Volodymyr Fialko --- lib/pdcp/pdcp_entity.h | 2 ++ lib/pdcp/pdcp_process.c | 2 ++ lib/pdcp/rte_pdcp.c | 1 + lib/pdcp/rte_pdcp.h | 47 + 4 files changed, 52 insertions(+) diff --git a/lib/pdcp/pdcp_entity.h b/lib/pdcp/pdcp_entity.h index 34341cdc11..efc74ba9b9 100644 --- a/lib/pdcp/pdcp_entity.h +++ b/lib/pdcp/pdcp_entity.h @@ -120,6 +120,8 @@ enum timer_state { struct pdcp_t_reordering { /** Represent timer state */ enum timer_state state; + /** User defined callback handles */ + struct rte_pdcp_t_reordering handle; }; struct pdcp_cnt_bitmap { diff --git a/lib/pdcp/pdcp_process.c b/lib/pdcp/pdcp_process.c index 84a0f3a43f..daf2c27363 100644 --- a/lib/pdcp/pdcp_process.c +++ b/lib/pdcp/pdcp_process.c @@ -902,6 +902,7 @@ pdcp_post_process_update_entity_state(const struct rte_pdcp_entity *entity, if (t_reorder->state == TIMER_RUNNING && en_priv->state.rx_deliv >= en_priv->state.rx_reord) { t_reorder->state = TIMER_STOP; + t_reorder->handle.stop(t_reorder->handle.timer, t_reorder->handle.args); /* Stop reorder buffer, only if it's empty */ if (en_priv->state.rx_deliv == en_priv->state.rx_next) pdcp_reorder_stop(reorder); @@ -916,6 +917,7 @@ pdcp_post_process_update_entity_state(const struct rte_pdcp_entity *entity, en_priv->state.rx_reord = en_priv->state.rx_next; /* Start t-Reordering */ t_reorder->state = TIMER_RUNNING; + t_reorder->handle.start(t_reorder->handle.timer, t_reorder->handle.args); } return processed; diff --git a/lib/pdcp/rte_pdcp.c b/lib/pdcp/rte_pdcp.c index be37ff392c..a0558b99ae 100644 --- a/lib/pdcp/rte_pdcp.c +++ b/lib/pdcp/rte_pdcp.c @@ -56,6 +56,7 @@ pdcp_dl_establish(struct rte_pdcp_entity *entity, const struct rte_pdcp_entity_c struct entity_priv_dl_part *dl = entity_dl_part_get(entity); entity->max_pkt_cache = RTE_MAX(entity->max_pkt_cache, window_size); + dl->t_reorder.handle = conf->t_reordering; return pdcp_reorder_create(&dl->reorder, window_size); } diff --git a/lib/pdcp/rte_pdcp.h b/lib/pdcp/rte_pdcp.h index 980086a93a..05c922819e 100644 --- a/lib/pdcp/rte_pdcp.h +++ b/lib/pdcp/rte_pdcp.h @@ -67,6 +67,51 @@ struct rte_pdcp_entity { uint32_t max_pkt_cache; } __rte_cache_aligned; +/** + * Callback function type for t-Reordering timer start, set during PDCP entity establish. + * This callback is invoked by PDCP library, during t-Reordering timer start event. + * Only one t-Reordering per receiving PDCP entity would be running at a given time. + * + * @see struct rte_pdcp_timer + * @see rte_pdcp_entity_establish() + * + * @param timer + * Pointer to timer. + * @param args + * Pointer to timer arguments. + */ +typedef void (*rte_pdcp_t_reordering_start_cb_t)(void *timer, void *args); + +/** + * Callback function type for t-Reordering timer stop, set during PDCP entity establish. + * This callback will be invoked by PDCP library, during t-Reordering timer stop event. + * + * @see struct rte_pdcp_timer + * @see rte_pdcp_entity_establish() + * + * @param timer + * Pointer to timer. + * @param args + * Pointer to timer arguments. + */ +typedef void (*rte_pdcp_t_reordering_stop_cb_t)(void *timer, void *args); + +/** + * PDCP t-Reordering timer interface + * + * Configuration provided by user, that PDCP library will invoke according to timer behaviour. + */ +struct rte_pdcp_t_reordering { + /** Timer pointer, stored for later use in callback functions */ + void *timer; + /** Timer arguments, stored for later use in callback functions */ + void *args; + /** Timer start callback handle */ + rte_pdcp_t_reordering_start_cb_t start; + /** Timer stop callback handle */ + rte_pdcp_t_reordering_stop_cb_t stop; +}; + /** * PDCP entity configuration to be used for establishing an entity. */ @@ -113,6 +158,8 @@ struct rte_pdcp_entity_conf { bool stat
[PATCH v5 12/21] pdcp: add control PDU handling for status report
Add control PDU handling and implement status report generation. Status report generation works only when RX_DELIV = RX_NEXT. Signed-off-by: Anoob Joseph Signed-off-by: Volodymyr Fialko --- doc/guides/prog_guide/pdcp_lib.rst | 9 ++ lib/pdcp/meson.build | 2 ++ lib/pdcp/pdcp_cnt.c| 29 ++ lib/pdcp/pdcp_cnt.h| 14 + lib/pdcp/pdcp_ctrl_pdu.c | 46 + lib/pdcp/pdcp_ctrl_pdu.h | 15 ++ lib/pdcp/pdcp_entity.h | 15 -- lib/pdcp/pdcp_process.c| 13 + lib/pdcp/rte_pdcp.c| 47 +- lib/pdcp/rte_pdcp.h| 33 + lib/pdcp/version.map | 2 ++ 11 files changed, 222 insertions(+), 3 deletions(-) create mode 100644 lib/pdcp/pdcp_cnt.c create mode 100644 lib/pdcp/pdcp_cnt.h create mode 100644 lib/pdcp/pdcp_ctrl_pdu.c create mode 100644 lib/pdcp/pdcp_ctrl_pdu.h diff --git a/doc/guides/prog_guide/pdcp_lib.rst b/doc/guides/prog_guide/pdcp_lib.rst index 8369c71600..dcb424bb1d 100644 --- a/doc/guides/prog_guide/pdcp_lib.rst +++ b/doc/guides/prog_guide/pdcp_lib.rst @@ -76,6 +76,15 @@ Data PDUs are regular packets submitted by upper layers for transmission to other end. Such packets would need to be ciphered and authenticated based on the entity configuration. +PDCP packet processing API for control PDU +~~ + +Control PDUs are used in PDCP as a communication channel between transmitting +and receiving entities. When upper layer request for operations such +re-establishment, receiving PDCP entity need to prepare a status report and +send it to the other end. The API ``rte_pdcp_control_pdu_create`` allows +application to request the same. + PDCP packet processing API for data PDU ~~~ diff --git a/lib/pdcp/meson.build b/lib/pdcp/meson.build index 08679b743a..75d476bf6d 100644 --- a/lib/pdcp/meson.build +++ b/lib/pdcp/meson.build @@ -8,7 +8,9 @@ if is_windows endif sources = files( +'pdcp_cnt.c', 'pdcp_crypto.c', +'pdcp_ctrl_pdu.c', 'pdcp_process.c', 'rte_pdcp.c', ) diff --git a/lib/pdcp/pdcp_cnt.c b/lib/pdcp/pdcp_cnt.c new file mode 100644 index 00..c9b952184b --- /dev/null +++ b/lib/pdcp/pdcp_cnt.c @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(C) 2023 Marvell. + */ + +#include + +#include "pdcp_cnt.h" +#include "pdcp_entity.h" + +int +pdcp_cnt_ring_create(struct rte_pdcp_entity *en, const struct rte_pdcp_entity_conf *conf) +{ + struct entity_priv_dl_part *en_priv_dl; + uint32_t window_sz; + + if (en == NULL || conf == NULL) + return -EINVAL; + + if (conf->pdcp_xfrm.pkt_dir == RTE_SECURITY_PDCP_UPLINK) + return 0; + + en_priv_dl = entity_dl_part_get(en); + window_sz = pdcp_window_size_get(conf->pdcp_xfrm.sn_size); + + RTE_SET_USED(window_sz); + RTE_SET_USED(en_priv_dl); + + return 0; +} diff --git a/lib/pdcp/pdcp_cnt.h b/lib/pdcp/pdcp_cnt.h new file mode 100644 index 00..bbda478b55 --- /dev/null +++ b/lib/pdcp/pdcp_cnt.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(C) 2023 Marvell. + */ + +#ifndef PDCP_CNT_H +#define PDCP_CNT_H + +#include + +#include "pdcp_entity.h" + +int pdcp_cnt_ring_create(struct rte_pdcp_entity *en, const struct rte_pdcp_entity_conf *conf); + +#endif /* PDCP_CNT_H */ diff --git a/lib/pdcp/pdcp_ctrl_pdu.c b/lib/pdcp/pdcp_ctrl_pdu.c new file mode 100644 index 00..feb05fd863 --- /dev/null +++ b/lib/pdcp/pdcp_ctrl_pdu.c @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(C) 2023 Marvell. + */ + +#include +#include +#include + +#include "pdcp_ctrl_pdu.h" +#include "pdcp_entity.h" + +static __rte_always_inline void +pdcp_hdr_fill(struct rte_pdcp_up_ctrl_pdu_hdr *pdu_hdr, uint32_t rx_deliv) +{ + pdu_hdr->d_c = RTE_PDCP_PDU_TYPE_CTRL; + pdu_hdr->pdu_type = RTE_PDCP_CTRL_PDU_TYPE_STATUS_REPORT; + pdu_hdr->r = 0; + pdu_hdr->fmc = rte_cpu_to_be_32(rx_deliv); +} + +int +pdcp_ctrl_pdu_status_gen(struct entity_priv *en_priv, struct rte_mbuf *m) +{ + struct rte_pdcp_up_ctrl_pdu_hdr *pdu_hdr; + uint32_t rx_deliv; + int pdu_sz; + + if (!en_priv->flags.is_status_report_required) + return -EINVAL; + + pdu_sz = sizeof(struct rte_pdcp_up_ctrl_pdu_hdr); + + rx_deliv = en_priv->state.rx_deliv; + + /* Zero missing PDUs - status report contains only FMC */ + if (rx_deliv >= en_priv->state.rx_next) { + pdu_hdr = (struct rte_pdcp_up_ctrl_pdu_hdr *)rte_pktmbuf_append(m, pdu_sz); + if (pdu_hdr == NULL) + return -ENOMEM; + pdcp_hdr_fill(pdu_hdr, rx_deliv); + + return 0; + } + +
[PATCH v5 07/21] pdcp: add pre and post process for DL
Add routines to perform pre & post processing for down link entities. Signed-off-by: Anoob Joseph Signed-off-by: Kiran Kumar K Signed-off-by: Volodymyr Fialko --- lib/pdcp/pdcp_entity.h | 2 + lib/pdcp/pdcp_process.c | 384 lib/pdcp/pdcp_process.h | 11 ++ lib/pdcp/rte_pdcp.c | 23 +++ 4 files changed, 420 insertions(+) diff --git a/lib/pdcp/pdcp_entity.h b/lib/pdcp/pdcp_entity.h index 23628ebad4..1d4a43a3bc 100644 --- a/lib/pdcp/pdcp_entity.h +++ b/lib/pdcp/pdcp_entity.h @@ -13,6 +13,8 @@ struct entity_priv; +#define PDCP_HFN_MIN 0 + /* IV generation function based on the entity configuration */ typedef void (*iv_gen_t)(struct rte_crypto_op *cop, const struct entity_priv *en_priv, uint32_t count); diff --git a/lib/pdcp/pdcp_process.c b/lib/pdcp/pdcp_process.c index 9b7de39db6..bd75e6f802 100644 --- a/lib/pdcp/pdcp_process.c +++ b/lib/pdcp/pdcp_process.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -333,9 +334,353 @@ pdcp_post_process_ul(const struct rte_pdcp_entity *entity, return nb_success; } +static inline int +pdcp_sn_count_get(const uint32_t rx_deliv, int32_t rsn, uint32_t *count, + const enum rte_security_pdcp_sn_size sn_size) +{ + const uint32_t rx_deliv_sn = pdcp_sn_from_count_get(rx_deliv, sn_size); + const uint32_t window_sz = pdcp_window_size_get(sn_size); + uint32_t rhfn; + + rhfn = pdcp_hfn_from_count_get(rx_deliv, sn_size); + + if (rsn < (int32_t)(rx_deliv_sn - window_sz)) { + if (unlikely(rhfn == pdcp_hfn_max(sn_size))) + return -ERANGE; + rhfn += 1; + } else if ((uint32_t)rsn >= (rx_deliv_sn + window_sz)) { + if (unlikely(rhfn == PDCP_HFN_MIN)) + return -ERANGE; + rhfn -= 1; + } + + *count = pdcp_count_from_hfn_sn_get(rhfn, rsn, sn_size); + + return 0; +} + +static inline uint16_t +pdcp_pre_process_uplane_sn_12_dl_flags(const struct rte_pdcp_entity *entity, + struct rte_mbuf *in_mb[], struct rte_crypto_op *cop[], + uint16_t num, uint16_t *nb_err_ret, + const bool is_integ_protected) +{ + struct entity_priv *en_priv = entity_priv_get(entity); + struct rte_pdcp_up_data_pdu_sn_12_hdr *pdu_hdr; + uint16_t nb_cop, nb_prep = 0, nb_err = 0; + rte_pdcp_dynfield_t *mb_dynfield; + struct rte_mbuf *mb; + int32_t rsn = 0; + uint32_t count; + int i; + + const uint8_t data_offset = en_priv->hdr_sz + en_priv->aad_sz; + + nb_cop = rte_crypto_op_bulk_alloc(en_priv->cop_pool, RTE_CRYPTO_OP_TYPE_SYMMETRIC, cop, + num); + + const uint32_t rx_deliv = en_priv->state.rx_deliv; + + for (i = 0; i < nb_cop; i++) { + mb = in_mb[i]; + pdu_hdr = rte_pktmbuf_mtod(mb, struct rte_pdcp_up_data_pdu_sn_12_hdr *); + + /* Check for PDU type */ + if (likely(pdu_hdr->d_c == RTE_PDCP_PDU_TYPE_DATA)) { + rsn = ((pdu_hdr->sn_11_8 << 8) | (pdu_hdr->sn_7_0)); + } else { + /** NOTE: Control PDU not handled.*/ + in_mb[nb_err++] = mb; + continue; + } + + if (unlikely(pdcp_sn_count_get(rx_deliv, rsn, &count, + RTE_SECURITY_PDCP_SN_SIZE_12))) { + in_mb[nb_err++] = mb; + continue; + } + + cop_prepare(en_priv, mb, cop[nb_prep++], data_offset, count, is_integ_protected); + + mb_dynfield = pdcp_dynfield(mb); + *mb_dynfield = count; + } + + if (unlikely(nb_err)) + rte_mempool_put_bulk(en_priv->cop_pool, (void *)&cop[nb_prep], nb_cop - nb_prep); + + *nb_err_ret = num - nb_prep; + + return nb_prep; +} + +static uint16_t +pdcp_pre_process_uplane_sn_12_dl_ip(const struct rte_pdcp_entity *entity, struct rte_mbuf *mb[], + struct rte_crypto_op *cop[], uint16_t num, uint16_t *nb_err) +{ + return pdcp_pre_process_uplane_sn_12_dl_flags(entity, mb, cop, num, nb_err, true); +} + +static uint16_t +pdcp_pre_process_uplane_sn_12_dl(const struct rte_pdcp_entity *entity, struct rte_mbuf *mb[], +struct rte_crypto_op *cop[], uint16_t num, uint16_t *nb_err) +{ + return pdcp_pre_process_uplane_sn_12_dl_flags(entity, mb, cop, num, nb_err, false); +} + +static inline uint16_t +pdcp_pre_process_uplane_sn_18_dl_flags(const struct rte_pdcp_entity *entity, + struct rte_mbuf *in_mb[], struct rte_crypto_op *cop[], + uint16_
[PATCH v5 00/21] lib: add pdcp protocol
Add Packet Data Convergence Protocol (PDCP) processing library. The library is similar to lib_ipsec which provides IPsec processing capabilities in DPDK. PDCP would involve roughly the following operations, 1. Transfer of user plane data 2. Transfer of control plane data 3. Header compression 4. Uplink data compression 5. Ciphering and integrity protection PDCP library provides following control path APIs that is used to configure various PDCP entities, 1. rte_pdcp_entity_establish() 2. rte_pdcp_entity_suspend() 3. rte_pdcp_entity_release() PDCP process is split into 2 parts. One before crypto processing (rte_pdcp_pkt_pre_process()) and one after crypto processing (rte_pdcp_pkt_post_process()). Since cryptodev dequeue can return crypto operations belonging to multiple entities, rte_pdcp_pkt_crypto_group() is added to help grouping crypto operations belonging to same entity. Similar to lib IPsec, lib PDCP would allow application to use same API sequence while leveraging protocol offload features enabled by rte_security library. Lib PDCP would internally change the handles registered for *pre_process* and *post_process* based on features enabled in the entity. Lib PDCP would create the required sessions on the device provided in entity to minimize the application requirements. Also, the crypto_op allocation and free would also be done internally by lib PDCP to allow the library to create crypto ops as required for the input packets. For example, when control PDUs are received, no cryptodev enqueue-dequeue is expected for the same and lib PDCP is expected to handle it differently. Lib PDCP utilizes reorder library for implementing in-order delivery. It utilizes bitmap library for implementing status reports and track the COUNT value of the packets received. To allow application to choose timer implementation of choice, lib PDCP allows application to configure handles that can be used for starting & stopping timers. Upon expiry, application can call corresponding PDCP API(``rte_pdcp_t_reordering_expiry_handle``) for handling the event. Unit tests are added to verify both rte_timer based timers as well as rte_eventdev based timers. PDCP tracks the sequence number of the received packets and during events such as re-establishment, it is required to generate reports and transmit to the peer. This series introduces ``rte_pdcp_control_pdu_create`` for handling control PDU generation. Changes in v5: - Deferred patch adding thread safe processing. - Updated release notes & MAINTAINERS file. Changes in v4: - Disabled 'annotate locks' with lib PDCP - Enable PDCP autotest only when lib is enabled - Use rwlock instead of spinlock - Avoid per packet checks for thread safety (Stephen) - In DL path, save count determined during pre-process in mbuf and use the same in post-process. Determining count again may lead To errors - Simplified DL path threads to allow more common code between SN 12 & 18 Changes in v3: - Addressed review comments (Akhil) - Addressed build failure in CI (tests with lib eventdev disabled) - Addressed checkpatch issues - Set only positive values to rte_errno (Akhil) Changes in v2: - Added control PDU handling - Added t-Reordering timer - Added in-order delivery - Added status PDU generation - Rebased on top of new features added in reorder library - Split base patch - Increased test coverage - Improved thread safety Changes from RFC - Implementation for all APIs covering basic control plane & user plane packets - Unit test leveraging existing PDCP vectors available in test_cryptodev - Unit test performing both UL & DL operations to verify various protocol features - Updated documentation Sample application sequence: struct rte_mbuf **out_mb, *pkts[MAX_BURST_SIZE]; struct rte_crypto_op *cop[MAX_BURST_SIZE]; struct rte_pdcp_group grp[MAX_BURST_SIZE]; struct rte_pdcp_entity *pdcp_entity; int nb_max_out_mb, ret, nb_grp; /* Create PDCP entity */ pdcp_entity = rte_pdcp_entity_establish(&conf); /** * Allocate buffer for holding mbufs returned during PDCP suspend, * release & post-process APIs. */ /* Max packets that can be cached in entity + burst size */ nb_max_out_mb = pdcp_entity->max_pkt_cache + 1; out_mb = rte_malloc(NULL, nb_max_out_mb * sizeof(uintptr_t), 0); if (out_mb == NULL) { /* Handle error */ } while (1) { /* Receive packet and form mbuf */ /** * Prepare packets for crypto operation. Following operations * would be done, * * Transmitting entity/UL (only data PDUs): * - Perform compression * - Assign sequence number * - Add PDCP header * - Create & prepare crypto_op * - Prepare IV for crypto operation (auth_gen, encrypt)
[PATCH v5 03/21] pdcp: add pre and post-process
PDCP process is split into 2 parts. One before crypto processing (rte_pdcp_pkt_pre_process()) and one after crypto processing (rte_pdcp_pkt_post_process()). Functionality of pre-process & post-process varies based on the type of entity. Registration of entity specific function pointer allows skipping multiple checks that would come in datapath otherwise. Signed-off-by: Anoob Joseph Signed-off-by: Kiran Kumar K Signed-off-by: Volodymyr Fialko Acked-by: Akhil Goyal --- lib/pdcp/rte_pdcp.h | 97 lib/pdcp/version.map | 3 ++ 2 files changed, 100 insertions(+) diff --git a/lib/pdcp/rte_pdcp.h b/lib/pdcp/rte_pdcp.h index 1f96fdc9a1..46c3c2a416 100644 --- a/lib/pdcp/rte_pdcp.h +++ b/lib/pdcp/rte_pdcp.h @@ -22,6 +22,21 @@ extern "C" { #endif +/* Forward declarations */ +struct rte_pdcp_entity; + +/* PDCP pre-process function based on entity configuration */ +typedef uint16_t (*rte_pdcp_pre_p_t)(const struct rte_pdcp_entity *entity, +struct rte_mbuf *mb[], +struct rte_crypto_op *cop[], +uint16_t num, uint16_t *nb_err); + +/* PDCP post-process function based on entity configuration */ +typedef uint16_t (*rte_pdcp_post_p_t)(const struct rte_pdcp_entity *entity, + struct rte_mbuf *in_mb[], + struct rte_mbuf *out_mb[], + uint16_t num, uint16_t *nb_err); + /** * PDCP entity. * @@ -33,6 +48,10 @@ extern "C" { * plane depending on which radio bearer it is carrying data for. */ struct rte_pdcp_entity { + /** Entity specific pre-process handle. */ + rte_pdcp_pre_p_t pre_process; + /** Entity specific post-process handle. */ + rte_pdcp_post_p_t post_process; /** * PDCP entities may hold packets for purposes of in-order delivery (in * case of receiving PDCP entity) and re-transmission (in case of @@ -160,6 +179,84 @@ int rte_pdcp_entity_suspend(struct rte_pdcp_entity *pdcp_entity, struct rte_mbuf *out_mb[]); +/** + * @warning + * @b EXPERIMENTAL: this API may change without prior notice + * + * For input mbufs and given PDCP entity pre-process the mbufs and prepare + * crypto ops that can be enqueued to the cryptodev associated with given + * session. Only error packets would be moved returned in the input buffer, + * *mb*, and it is the responsibility of the application to free the same. + * + * @param entity + * Pointer to the *rte_pdcp_entity* object the packets belong to. + * @param[in, out] mb + * The address of an array of *num* pointers to *rte_mbuf* structures + * which contain the input packets. Any error packets would be returned in the + * same buffer. + * @param[out] cop + * The address of an array that can hold up to *num* pointers to + * *rte_crypto_op* structures. Crypto ops would be allocated by + * ``rte_pdcp_pkt_pre_process`` API. + * @param num + * The maximum number of packets to process. + * @param[out] nb_err + * Pointer to return the number of error packets returned in *mb* + * @return + * Count of crypto_ops prepared + */ +__rte_experimental +static inline uint16_t +rte_pdcp_pkt_pre_process(const struct rte_pdcp_entity *entity, +struct rte_mbuf *mb[], struct rte_crypto_op *cop[], +uint16_t num, uint16_t *nb_err) +{ + return entity->pre_process(entity, mb, cop, num, nb_err); +} + +/** + * @warning + * @b EXPERIMENTAL: this API may change without prior notice + * + * For input mbufs and given PDCP entity, perform PDCP post-processing of the + * mbufs. + * + * Input mbufs are the ones retrieved from crypto_ops dequeued from cryptodev + * and grouped by *rte_pdcp_pkt_crypto_group()*. + * + * The post-processed packets would be returned in the *out_mb* buffer. + * The resultant mbufs would be grouped into success packets and error packets. + * Error packets would be grouped in the end of the array and it is the + * responsibility of the application to handle the same. + * + * When in-order delivery is enabled, PDCP entity may buffer packets and would + * deliver packets only when all prior packets have been post-processed. That + * would result in returning more/less packets than enqueued. + * + * @param entity + * Pointer to the *rte_pdcp_entity* object the packets belong to. + * @param in_mb + * The address of an array of *num* pointers to *rte_mbuf* structures. + * @param[out] out_mb + * The address of an array of *num* pointers to *rte_mbuf* structures + * to output packets after PDCP post-processing. + * @param num + * The maximum number of packets to process. + * @param[out] nb_err + * The number of error packets returned in *out_mb* buffer. + * @return + * Count of packets returned in *out_mb* buffer. + */ +__rte_experimental +static inline uint16_t +rte_pdcp
[PATCH v5 14/21] test/pdcp: add in-order delivery cases
From: Volodymyr Fialko Add test cases to verify behaviour when in-order delivery is enabled and packets arrive in out-of-order. PDCP library is expected to buffer the packets and return packets in-order when the missing packet arrives. Signed-off-by: Anoob Joseph Signed-off-by: Volodymyr Fialko --- app/test/test_pdcp.c | 223 +++ 1 file changed, 223 insertions(+) diff --git a/app/test/test_pdcp.c b/app/test/test_pdcp.c index cfe2ec6aa9..24d7826bc2 100644 --- a/app/test/test_pdcp.c +++ b/app/test/test_pdcp.c @@ -16,6 +16,15 @@ #define NB_TESTS RTE_DIM(pdcp_test_params) #define PDCP_IV_LEN 16 +/* Assert that condition is true, or goto the mark */ +#define ASSERT_TRUE_OR_GOTO(cond, mark, ...) do {\ + if (!(cond)) { \ + RTE_LOG(ERR, USER1, "Error at: %s:%d\n", __func__, __LINE__); \ + RTE_LOG(ERR, USER1, __VA_ARGS__); \ + goto mark; \ + } \ +} while (0) + /* According to formula(7.2.a Window_Size) */ #define PDCP_WINDOW_SIZE(sn_size) (1 << (sn_size - 1)) @@ -83,6 +92,38 @@ run_test_with_all_known_vec(const void *args) return run_test_foreach_known_vec(test, false); } +static int +run_test_with_all_known_vec_until_first_pass(const void *args) +{ + test_with_conf_t test = args; + + return run_test_foreach_known_vec(test, true); +} + +static inline uint32_t +pdcp_sn_mask_get(enum rte_security_pdcp_sn_size sn_size) +{ + return (1 << sn_size) - 1; +} + +static inline uint32_t +pdcp_sn_from_count_get(uint32_t count, enum rte_security_pdcp_sn_size sn_size) +{ + return (count & pdcp_sn_mask_get(sn_size)); +} + +static inline uint32_t +pdcp_hfn_mask_get(enum rte_security_pdcp_sn_size sn_size) +{ + return ~pdcp_sn_mask_get(sn_size); +} + +static inline uint32_t +pdcp_hfn_from_count_get(uint32_t count, enum rte_security_pdcp_sn_size sn_size) +{ + return (count & pdcp_hfn_mask_get(sn_size)) >> sn_size; +} + static inline int pdcp_hdr_size_get(enum rte_security_pdcp_sn_size sn_size) { @@ -416,6 +457,7 @@ create_test_conf_from_index(const int index, struct pdcp_test_conf *conf) conf->entity.sess_mpool = ts_params->sess_pool; conf->entity.cop_pool = ts_params->cop_pool; + conf->entity.ctrl_pdu_pool = ts_params->mbuf_pool; conf->entity.pdcp_xfrm.bearer = pdcp_test_bearer[index]; conf->entity.pdcp_xfrm.en_ordering = 0; conf->entity.pdcp_xfrm.remove_duplicates = 0; @@ -868,6 +910,7 @@ test_sn_range_type(enum sn_range_type type, struct pdcp_test_conf *conf) /* Configure Uplink to generate expected, encrypted packet */ pdcp_sn_to_raw_set(conf->input, new_sn, conf->entity.pdcp_xfrm.sn_size); + conf->entity.out_of_order_delivery = true; conf->entity.reverse_iv_direction = true; conf->entity.pdcp_xfrm.hfn = new_hfn; conf->entity.sn = new_sn; @@ -915,6 +958,171 @@ test_sn_minus_outside(struct pdcp_test_conf *t_conf) return test_sn_range_type(SN_RANGE_MINUS_OUTSIDE, t_conf); } +static struct rte_mbuf * +generate_packet_for_dl_with_sn(struct pdcp_test_conf ul_conf, uint32_t count) +{ + enum rte_security_pdcp_sn_size sn_size = ul_conf.entity.pdcp_xfrm.sn_size; + int ret; + + ul_conf.entity.pdcp_xfrm.hfn = pdcp_hfn_from_count_get(count, sn_size); + ul_conf.entity.sn = pdcp_sn_from_count_get(count, sn_size); + ul_conf.entity.out_of_order_delivery = true; + ul_conf.entity.reverse_iv_direction = true; + ul_conf.output_len = 0; + + ret = test_attempt_single(&ul_conf); + if (ret != TEST_SUCCESS) + return NULL; + + return mbuf_from_data_create(ul_conf.output, ul_conf.output_len); +} + +static bool +array_asc_sorted_check(struct rte_mbuf *m[], uint32_t len, enum rte_security_pdcp_sn_size sn_size) +{ + uint32_t i; + + if (len < 2) + return true; + + for (i = 0; i < (len - 1); i++) { + if (pdcp_sn_from_raw_get(rte_pktmbuf_mtod(m[i], void *), sn_size) > + pdcp_sn_from_raw_get(rte_pktmbuf_mtod(m[i + 1], void *), sn_size)) + return false; + } + + return true; +} + +static int +test_reorder_gap_fill(struct pdcp_test_conf *ul_conf) +{ + const enum rte_security_pdcp_sn_size sn_size = ul_conf->entity.pdcp_xfrm.sn_size; + struct rte_mbuf *m0 = NULL, *m1 = NULL, *out_mb[2] = {0}; + uint16_t nb_success = 0, nb_err = 0; + struct rte_pdcp_entity *pdcp_entity; + struct pdcp_test_conf dl_conf; + int ret = TEST_FAILED, nb_out; + uint8_t cdev_id; + + const int start_count = 0; + + if (ul_conf->entity.pdcp_xfrm.pkt_dir == RTE_SECURITY_PDCP_DOWNLINK) + return TEST_SKIPPED; + + /* Create configuration for actual testing */ + uplink_to_downlink_convert(ul_conf, &dl_conf); + dl_conf.entity.pdcp_xfrm.hfn = pdcp_hfn_from_count_get(start_count, sn_
[PATCH v5 20/21] pdcp: allocate reorder buffer alongside with entity
From: Volodymyr Fialko Instead of allocating reorder buffer separately on heap, allocate memory for it together with rest of entity, and then only initialize buffer via `rte_reorder_init()`. Signed-off-by: Anoob Joseph Signed-off-by: Volodymyr Fialko --- lib/pdcp/pdcp_cnt.c | 9 +++ lib/pdcp/pdcp_cnt.h | 3 ++- lib/pdcp/pdcp_entity.h | 2 +- lib/pdcp/pdcp_reorder.c | 11 ++-- lib/pdcp/pdcp_reorder.h | 12 ++--- lib/pdcp/rte_pdcp.c | 58 ++--- 6 files changed, 55 insertions(+), 40 deletions(-) diff --git a/lib/pdcp/pdcp_cnt.c b/lib/pdcp/pdcp_cnt.c index af027b00d3..e1d0634b4d 100644 --- a/lib/pdcp/pdcp_cnt.c +++ b/lib/pdcp/pdcp_cnt.c @@ -20,15 +20,14 @@ pdcp_cnt_bitmap_get_memory_footprint(const struct rte_pdcp_entity_conf *conf) } int -pdcp_cnt_bitmap_create(struct entity_priv_dl_part *dl, void *bitmap_mem, uint32_t window_size) +pdcp_cnt_bitmap_create(struct entity_priv_dl_part *dl, uint32_t nb_elem, + void *bitmap_mem, uint32_t mem_size) { - uint32_t mem_size = rte_bitmap_get_memory_footprint(window_size); - - dl->bitmap.bmp = rte_bitmap_init(window_size, bitmap_mem, mem_size); + dl->bitmap.bmp = rte_bitmap_init(nb_elem, bitmap_mem, mem_size); if (dl->bitmap.bmp == NULL) return -EINVAL; - dl->bitmap.size = window_size; + dl->bitmap.size = nb_elem; return 0; } diff --git a/lib/pdcp/pdcp_cnt.h b/lib/pdcp/pdcp_cnt.h index 5941b7a406..87b011f9dc 100644 --- a/lib/pdcp/pdcp_cnt.h +++ b/lib/pdcp/pdcp_cnt.h @@ -10,7 +10,8 @@ #include "pdcp_entity.h" uint32_t pdcp_cnt_bitmap_get_memory_footprint(const struct rte_pdcp_entity_conf *conf); -int pdcp_cnt_bitmap_create(struct entity_priv_dl_part *dl, void *bitmap_mem, uint32_t window_size); +int pdcp_cnt_bitmap_create(struct entity_priv_dl_part *dl, uint32_t nb_elem, + void *bitmap_mem, uint32_t mem_size); void pdcp_cnt_bitmap_set(struct pdcp_cnt_bitmap bitmap, uint32_t count); bool pdcp_cnt_bitmap_is_set(struct pdcp_cnt_bitmap bitmap, uint32_t count); diff --git a/lib/pdcp/pdcp_entity.h b/lib/pdcp/pdcp_entity.h index a9b1428c7a..9f74b5d0e5 100644 --- a/lib/pdcp/pdcp_entity.h +++ b/lib/pdcp/pdcp_entity.h @@ -132,7 +132,7 @@ struct pdcp_cnt_bitmap { }; /* - * Layout of PDCP entity: [rte_pdcp_entity] [entity_priv] [entity_dl/ul] + * Layout of PDCP entity: [rte_pdcp_entity] [entity_priv] [entity_dl/ul] [reorder/bitmap] */ struct entity_priv { diff --git a/lib/pdcp/pdcp_reorder.c b/lib/pdcp/pdcp_reorder.c index 5399f0dc28..bc45f2e19b 100644 --- a/lib/pdcp/pdcp_reorder.c +++ b/lib/pdcp/pdcp_reorder.c @@ -8,20 +8,13 @@ #include "pdcp_reorder.h" int -pdcp_reorder_create(struct pdcp_reorder *reorder, uint32_t window_size) +pdcp_reorder_create(struct pdcp_reorder *reorder, size_t nb_elem, void *mem, size_t mem_size) { - reorder->buf = rte_reorder_create("reorder_buffer", SOCKET_ID_ANY, window_size); + reorder->buf = rte_reorder_init(mem, mem_size, "reorder_buffer", nb_elem); if (reorder->buf == NULL) return -rte_errno; - reorder->window_size = window_size; reorder->is_active = false; return 0; } - -void -pdcp_reorder_destroy(const struct pdcp_reorder *reorder) -{ - rte_reorder_free(reorder->buf); -} diff --git a/lib/pdcp/pdcp_reorder.h b/lib/pdcp/pdcp_reorder.h index 6a2f61d6ae..7e4f079d4b 100644 --- a/lib/pdcp/pdcp_reorder.h +++ b/lib/pdcp/pdcp_reorder.h @@ -9,12 +9,18 @@ struct pdcp_reorder { struct rte_reorder_buffer *buf; - uint32_t window_size; bool is_active; }; -int pdcp_reorder_create(struct pdcp_reorder *reorder, uint32_t window_size); -void pdcp_reorder_destroy(const struct pdcp_reorder *reorder); +int pdcp_reorder_create(struct pdcp_reorder *reorder, size_t nb_elem, void *mem, size_t mem_size); + +/* NOTE: replace with `rte_reorder_memory_footprint_get` after DPDK 23.07 */ +#define SIZE_OF_REORDER_BUFFER (4 * RTE_CACHE_LINE_SIZE) +static inline size_t +pdcp_reorder_memory_footprint_get(size_t nb_elem) +{ + return SIZE_OF_REORDER_BUFFER + (2 * nb_elem * sizeof(struct rte_mbuf *)); +} static inline uint32_t pdcp_reorder_get_sequential(struct pdcp_reorder *reorder, struct rte_mbuf **mbufs, diff --git a/lib/pdcp/rte_pdcp.c b/lib/pdcp/rte_pdcp.c index 9865c620b7..1c6d2466b2 100644 --- a/lib/pdcp/rte_pdcp.c +++ b/lib/pdcp/rte_pdcp.c @@ -14,7 +14,15 @@ #define RTE_PDCP_DYNFIELD_NAME "rte_pdcp_dynfield" -static int bitmap_mem_offset; +struct entity_layout { + size_t bitmap_offset; + size_t bitmap_size; + + size_t reorder_buf_offset; + size_t reorder_buf_size; + + size_t total_size; +}; int rte_pdcp_dynfield_offset = -1; @@ -35,46 +43,54 @@ pdcp_dynfield_register(void) } static int -pdcp_entity_size_get(const struct rte_pdcp_entity_conf *conf) +pdcp_entity_layout_get(const struct rte_pdcp_entity_conf *conf, s
[PATCH v5 09/21] app/test: add lib pdcp tests
Add tests to verify lib PDCP operations. Tests leverage existing PDCP test vectors. Signed-off-by: Anoob Joseph Signed-off-by: Volodymyr Fialko --- MAINTAINERS | 1 + app/test/meson.build | 5 + app/test/test_pdcp.c | 732 +++ 3 files changed, 738 insertions(+) create mode 100644 app/test/test_pdcp.c diff --git a/MAINTAINERS b/MAINTAINERS index 85a3b94644..15dd88af3a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1554,6 +1554,7 @@ M: Anoob Joseph M: Volodymyr Fialko T: git://dpdk.org/next/dpdk-next-crypto F: lib/pdcp/ +F: app/test/test_pdcp* Packet Framework diff --git a/app/test/meson.build b/app/test/meson.build index b9b5432496..2894793347 100644 --- a/app/test/meson.build +++ b/app/test/meson.build @@ -435,6 +435,11 @@ if dpdk_conf.has('RTE_HAS_LIBPCAP') endif endif +if dpdk_conf.has('RTE_LIB_PDCP') +test_sources += 'test_pdcp.c' +fast_tests += [['pdcp_autotest', false, true]] +endif + if cc.has_argument('-Wno-format-truncation') cflags += '-Wno-format-truncation' endif diff --git a/app/test/test_pdcp.c b/app/test/test_pdcp.c new file mode 100644 index 00..34b759eaef --- /dev/null +++ b/app/test/test_pdcp.c @@ -0,0 +1,732 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(C) 2023 Marvell. + */ + +#include +#include +#include +#include + +#include "test.h" +#include "test_cryptodev.h" +#include "test_cryptodev_security_pdcp_test_vectors.h" + +#define NB_DESC 1024 +#define CDEV_INVALID_ID UINT8_MAX +#define NB_TESTS RTE_DIM(pdcp_test_params) +#define PDCP_IV_LEN 16 + +struct pdcp_testsuite_params { + struct rte_mempool *mbuf_pool; + struct rte_mempool *cop_pool; + struct rte_mempool *sess_pool; + bool cdevs_used[RTE_CRYPTO_MAX_DEVS]; +}; + +static struct pdcp_testsuite_params testsuite_params; + +struct pdcp_test_conf { + struct rte_pdcp_entity_conf entity; + struct rte_crypto_sym_xform c_xfrm; + struct rte_crypto_sym_xform a_xfrm; + bool is_integrity_protected; + uint8_t input[RTE_PDCP_CTRL_PDU_SIZE_MAX]; + uint32_t input_len; + uint8_t output[RTE_PDCP_CTRL_PDU_SIZE_MAX]; + uint32_t output_len; +}; + +static inline int +pdcp_hdr_size_get(enum rte_security_pdcp_sn_size sn_size) +{ + return RTE_ALIGN_MUL_CEIL(sn_size, 8) / 8; +} + +static int +cryptodev_init(int dev_id) +{ + struct pdcp_testsuite_params *ts_params = &testsuite_params; + struct rte_cryptodev_qp_conf qp_conf; + struct rte_cryptodev_info dev_info; + struct rte_cryptodev_config config; + int ret, socket_id; + + /* Check if device was already initialized */ + if (ts_params->cdevs_used[dev_id]) + return 0; + + rte_cryptodev_info_get(dev_id, &dev_info); + + if (dev_info.max_nb_queue_pairs < 1) { + RTE_LOG(ERR, USER1, "Cryptodev doesn't have sufficient queue pairs available\n"); + return -ENODEV; + } + + socket_id = rte_socket_id(); + + memset(&config, 0, sizeof(config)); + config.nb_queue_pairs = 1; + config.socket_id = socket_id; + + ret = rte_cryptodev_configure(dev_id, &config); + if (ret < 0) { + RTE_LOG(ERR, USER1, "Could not configure cryptodev - %d\n", dev_id); + return -ENODEV; + } + + memset(&qp_conf, 0, sizeof(qp_conf)); + qp_conf.nb_descriptors = NB_DESC; + + ret = rte_cryptodev_queue_pair_setup(dev_id, 0, &qp_conf, socket_id); + if (ret < 0) { + RTE_LOG(ERR, USER1, "Could not configure queue pair\n"); + return -ENODEV; + } + + ret = rte_cryptodev_start(dev_id); + if (ret < 0) { + RTE_LOG(ERR, USER1, "Could not start cryptodev\n"); + return -ENODEV; + } + + /* Mark device as initialized */ + ts_params->cdevs_used[dev_id] = true; + + return 0; +} + +static void +cryptodev_fini(int dev_id) +{ + rte_cryptodev_stop(dev_id); +} + +static unsigned int +cryptodev_sess_priv_max_req_get(void) +{ + struct rte_cryptodev_info info; + unsigned int sess_priv_sz; + int i, nb_dev; + void *sec_ctx; + + nb_dev = rte_cryptodev_count(); + + sess_priv_sz = 0; + + for (i = 0; i < nb_dev; i++) { + rte_cryptodev_info_get(i, &info); + sess_priv_sz = RTE_MAX(sess_priv_sz, rte_cryptodev_sym_get_private_session_size(i)); + if (info.feature_flags & RTE_CRYPTODEV_FF_SECURITY) { + sec_ctx = rte_cryptodev_get_sec_ctx(i); + sess_priv_sz = RTE_MAX(sess_priv_sz, + rte_security_session_get_size(sec_ctx)); + } + } + + return sess_priv_sz; +} + +static int +testsuite_setup(void) +{ + struct pdcp_testsuite_params *ts_params = &testsuite_params; + int nb_cdev, sess_priv_size, nb_ses
[PATCH v5 19/21] pdcp: add support for status report
From: Volodymyr Fialko Implement status report generation for PDCP entity. Signed-off-by: Anoob Joseph Signed-off-by: Volodymyr Fialko --- lib/pdcp/pdcp_cnt.c | 158 --- lib/pdcp/pdcp_cnt.h | 11 ++- lib/pdcp/pdcp_ctrl_pdu.c | 34 - lib/pdcp/pdcp_ctrl_pdu.h | 3 +- lib/pdcp/pdcp_entity.h | 2 + lib/pdcp/pdcp_process.c | 9 ++- lib/pdcp/pdcp_process.h | 13 lib/pdcp/rte_pdcp.c | 34 ++--- 8 files changed, 236 insertions(+), 28 deletions(-) diff --git a/lib/pdcp/pdcp_cnt.c b/lib/pdcp/pdcp_cnt.c index c9b952184b..af027b00d3 100644 --- a/lib/pdcp/pdcp_cnt.c +++ b/lib/pdcp/pdcp_cnt.c @@ -2,28 +2,164 @@ * Copyright(C) 2023 Marvell. */ +#include #include #include "pdcp_cnt.h" +#include "pdcp_ctrl_pdu.h" #include "pdcp_entity.h" +#define SLAB_BYTE_SIZE (RTE_BITMAP_SLAB_BIT_SIZE / 8) + +uint32_t +pdcp_cnt_bitmap_get_memory_footprint(const struct rte_pdcp_entity_conf *conf) +{ + uint32_t n_bits = pdcp_window_size_get(conf->pdcp_xfrm.sn_size); + + return rte_bitmap_get_memory_footprint(n_bits); +} + int -pdcp_cnt_ring_create(struct rte_pdcp_entity *en, const struct rte_pdcp_entity_conf *conf) +pdcp_cnt_bitmap_create(struct entity_priv_dl_part *dl, void *bitmap_mem, uint32_t window_size) { - struct entity_priv_dl_part *en_priv_dl; - uint32_t window_sz; + uint32_t mem_size = rte_bitmap_get_memory_footprint(window_size); - if (en == NULL || conf == NULL) + dl->bitmap.bmp = rte_bitmap_init(window_size, bitmap_mem, mem_size); + if (dl->bitmap.bmp == NULL) return -EINVAL; - if (conf->pdcp_xfrm.pkt_dir == RTE_SECURITY_PDCP_UPLINK) - return 0; + dl->bitmap.size = window_size; - en_priv_dl = entity_dl_part_get(en); - window_sz = pdcp_window_size_get(conf->pdcp_xfrm.sn_size); + return 0; +} - RTE_SET_USED(window_sz); - RTE_SET_USED(en_priv_dl); +void +pdcp_cnt_bitmap_set(struct pdcp_cnt_bitmap bitmap, uint32_t count) +{ + rte_bitmap_set(bitmap.bmp, count % bitmap.size); +} - return 0; +bool +pdcp_cnt_bitmap_is_set(struct pdcp_cnt_bitmap bitmap, uint32_t count) +{ + return rte_bitmap_get(bitmap.bmp, count % bitmap.size); +} + +void +pdcp_cnt_bitmap_range_clear(struct pdcp_cnt_bitmap bitmap, uint32_t start, uint32_t stop) +{ + uint32_t i; + + for (i = start; i < stop; i++) + rte_bitmap_clear(bitmap.bmp, i % bitmap.size); +} + +uint16_t +pdcp_cnt_get_bitmap_size(uint32_t pending_bytes) +{ + /* +* Round up bitmap size to slab size to operate only on slabs sizes, instead of individual +* bytes +*/ + return RTE_ALIGN_MUL_CEIL(pending_bytes, SLAB_BYTE_SIZE); +} + +static __rte_always_inline uint64_t +leftover_get(uint64_t slab, uint32_t shift, uint64_t mask) +{ + return (slab & mask) << shift; +} + +void +pdcp_cnt_report_fill(struct pdcp_cnt_bitmap bitmap, struct entity_state state, +uint8_t *data, uint16_t data_len) +{ + uint64_t slab = 0, next_slab = 0, leftover; + uint32_t zeros, report_len, diff; + uint32_t slab_id, next_slab_id; + uint32_t pos = 0, next_pos = 0; + + const uint32_t start_count = state.rx_deliv + 1; + const uint32_t nb_slabs = bitmap.size / RTE_BITMAP_SLAB_BIT_SIZE; + const uint32_t nb_data_slabs = data_len / SLAB_BYTE_SIZE; + const uint32_t start_slab_id = start_count / RTE_BITMAP_SLAB_BIT_SIZE; + const uint32_t stop_slab_id = (start_slab_id + nb_data_slabs) % nb_slabs; + const uint32_t shift = start_count % RTE_BITMAP_SLAB_BIT_SIZE; + const uint32_t leftover_shift = shift ? RTE_BITMAP_SLAB_BIT_SIZE - shift : 0; + const uint8_t *data_end = RTE_PTR_ADD(data, data_len + SLAB_BYTE_SIZE); + + /* NOTE: Mask required to workaround case - when shift is not needed */ + const uint64_t leftover_mask = shift ? ~0 : 0; + + /* NOTE: implement scan init at to set custom position */ + __rte_bitmap_scan_init(bitmap.bmp); + while (true) { + assert(rte_bitmap_scan(bitmap.bmp, &pos, &slab) == 1); + slab_id = pos / RTE_BITMAP_SLAB_BIT_SIZE; + if (slab_id >= start_slab_id) + break; + } + + report_len = nb_data_slabs; + + if (slab_id > start_slab_id) { + /* Zero slabs at beginning */ + zeros = (slab_id - start_slab_id - 1) * SLAB_BYTE_SIZE; + memset(data, 0, zeros); + data = RTE_PTR_ADD(data, zeros); + leftover = leftover_get(slab, leftover_shift, leftover_mask); + memcpy(data, &leftover, SLAB_BYTE_SIZE); + data = RTE_PTR_ADD(data, SLAB_BYTE_SIZE); + report_len -= (slab_id - start_slab_id); + } + + while (report_len) { + rte_bitmap_scan(bitmap.bmp, &next_pos, &next_slab); +
[PATCH v5 15/21] pdcp: add timer callback handlers
From: Volodymyr Fialko PDCP has a windowing mechanism which allows only packets that fall in a reception window. The pivot point for this window is RX_REORD which happens to be the first missing or next expected packet. If the missing packet is not received after a specified time, then the RX_REORD state variable needs to be moved up to slide the reception window. PDCP relies on timers for such operations. The timer needs to be armed when PDCP library doesn't receive all packets in-order and starts buffering packets that arrived after a missing packet. The timer needs to be cancelled when a missing packet is received. To avoid dependency on particular timer implementation, PDCP library allows application to register two callbacks, timer_start() and timer_stop() that will be called later by library. Signed-off-by: Anoob Joseph Signed-off-by: Volodymyr Fialko --- lib/pdcp/pdcp_entity.h | 2 ++ lib/pdcp/pdcp_process.c | 2 ++ lib/pdcp/rte_pdcp.c | 1 + lib/pdcp/rte_pdcp.h | 47 + 4 files changed, 52 insertions(+) diff --git a/lib/pdcp/pdcp_entity.h b/lib/pdcp/pdcp_entity.h index 34341cdc11..efc74ba9b9 100644 --- a/lib/pdcp/pdcp_entity.h +++ b/lib/pdcp/pdcp_entity.h @@ -120,6 +120,8 @@ enum timer_state { struct pdcp_t_reordering { /** Represent timer state */ enum timer_state state; + /** User defined callback handles */ + struct rte_pdcp_t_reordering handle; }; struct pdcp_cnt_bitmap { diff --git a/lib/pdcp/pdcp_process.c b/lib/pdcp/pdcp_process.c index 84a0f3a43f..daf2c27363 100644 --- a/lib/pdcp/pdcp_process.c +++ b/lib/pdcp/pdcp_process.c @@ -902,6 +902,7 @@ pdcp_post_process_update_entity_state(const struct rte_pdcp_entity *entity, if (t_reorder->state == TIMER_RUNNING && en_priv->state.rx_deliv >= en_priv->state.rx_reord) { t_reorder->state = TIMER_STOP; + t_reorder->handle.stop(t_reorder->handle.timer, t_reorder->handle.args); /* Stop reorder buffer, only if it's empty */ if (en_priv->state.rx_deliv == en_priv->state.rx_next) pdcp_reorder_stop(reorder); @@ -916,6 +917,7 @@ pdcp_post_process_update_entity_state(const struct rte_pdcp_entity *entity, en_priv->state.rx_reord = en_priv->state.rx_next; /* Start t-Reordering */ t_reorder->state = TIMER_RUNNING; + t_reorder->handle.start(t_reorder->handle.timer, t_reorder->handle.args); } return processed; diff --git a/lib/pdcp/rte_pdcp.c b/lib/pdcp/rte_pdcp.c index be37ff392c..a0558b99ae 100644 --- a/lib/pdcp/rte_pdcp.c +++ b/lib/pdcp/rte_pdcp.c @@ -56,6 +56,7 @@ pdcp_dl_establish(struct rte_pdcp_entity *entity, const struct rte_pdcp_entity_c struct entity_priv_dl_part *dl = entity_dl_part_get(entity); entity->max_pkt_cache = RTE_MAX(entity->max_pkt_cache, window_size); + dl->t_reorder.handle = conf->t_reordering; return pdcp_reorder_create(&dl->reorder, window_size); } diff --git a/lib/pdcp/rte_pdcp.h b/lib/pdcp/rte_pdcp.h index 980086a93a..05c922819e 100644 --- a/lib/pdcp/rte_pdcp.h +++ b/lib/pdcp/rte_pdcp.h @@ -67,6 +67,51 @@ struct rte_pdcp_entity { uint32_t max_pkt_cache; } __rte_cache_aligned; +/** + * Callback function type for t-Reordering timer start, set during PDCP entity establish. + * This callback is invoked by PDCP library, during t-Reordering timer start event. + * Only one t-Reordering per receiving PDCP entity would be running at a given time. + * + * @see struct rte_pdcp_timer + * @see rte_pdcp_entity_establish() + * + * @param timer + * Pointer to timer. + * @param args + * Pointer to timer arguments. + */ +typedef void (*rte_pdcp_t_reordering_start_cb_t)(void *timer, void *args); + +/** + * Callback function type for t-Reordering timer stop, set during PDCP entity establish. + * This callback will be invoked by PDCP library, during t-Reordering timer stop event. + * + * @see struct rte_pdcp_timer + * @see rte_pdcp_entity_establish() + * + * @param timer + * Pointer to timer. + * @param args + * Pointer to timer arguments. + */ +typedef void (*rte_pdcp_t_reordering_stop_cb_t)(void *timer, void *args); + +/** + * PDCP t-Reordering timer interface + * + * Configuration provided by user, that PDCP library will invoke according to timer behaviour. + */ +struct rte_pdcp_t_reordering { + /** Timer pointer, stored for later use in callback functions */ + void *timer; + /** Timer arguments, stored for later use in callback functions */ + void *args; + /** Timer start callback handle */ + rte_pdcp_t_reordering_start_cb_t start; + /** Timer stop callback handle */ + rte_pdcp_t_reordering_stop_cb_t stop; +}; + /** * PDCP entity configuration to be used for establishing an entity. */ @@ -113,6 +158,8 @@ struct rte_pdcp_entity_conf { bool stat
[PATCH v5 21/21] test/pdcp: add PDCP status report cases
From: Volodymyr Fialko Test PDCP status report generation. Signed-off-by: Anoob Joseph Signed-off-by: Volodymyr Fialko --- app/test/test_pdcp.c | 312 +++ 1 file changed, 312 insertions(+) diff --git a/app/test/test_pdcp.c b/app/test/test_pdcp.c index 82cc25ec7a..423526380f 100644 --- a/app/test/test_pdcp.c +++ b/app/test/test_pdcp.c @@ -2,6 +2,7 @@ * Copyright(C) 2023 Marvell. */ +#include #include #ifdef RTE_LIB_EVENTDEV #include @@ -48,6 +49,9 @@ struct pdcp_testsuite_params { #endif /* RTE_LIB_EVENTDEV */ bool timer_is_running; uint64_t min_resolution_ns; + struct rte_pdcp_up_ctrl_pdu_hdr *status_report; + uint32_t status_report_bitmask_capacity; + uint8_t *ctrl_pdu_buf; }; static struct pdcp_testsuite_params testsuite_params; @@ -168,6 +172,18 @@ static struct rte_pdcp_t_reordering t_reorder_timer = { .stop = pdcp_timer_stop_cb, }; +static inline void +bitmask_set_bit(uint8_t *mask, uint32_t bit) +{ + mask[bit / 8] |= (1 << bit % 8); +} + +static inline bool +bitmask_is_bit_set(const uint8_t *mask, uint32_t bit) +{ + return mask[bit / 8] & (1 << (bit % 8)); +} + static inline int pdcp_hdr_size_get(enum rte_security_pdcp_sn_size sn_size) { @@ -314,6 +330,21 @@ testsuite_setup(void) goto cop_pool_free; } + /* Allocate memory for longest possible status report */ + ts_params->status_report_bitmask_capacity = RTE_PDCP_CTRL_PDU_SIZE_MAX - + sizeof(struct rte_pdcp_up_ctrl_pdu_hdr); + ts_params->status_report = rte_zmalloc(NULL, RTE_PDCP_CTRL_PDU_SIZE_MAX, 0); + if (ts_params->status_report == NULL) { + RTE_LOG(ERR, USER1, "Could not allocate status report\n"); + goto cop_pool_free; + } + + ts_params->ctrl_pdu_buf = rte_zmalloc(NULL, RTE_PDCP_CTRL_PDU_SIZE_MAX, 0); + if (ts_params->ctrl_pdu_buf == NULL) { + RTE_LOG(ERR, USER1, "Could not allocate status report data\n"); + goto cop_pool_free; + } + return 0; cop_pool_free: @@ -322,6 +353,8 @@ testsuite_setup(void) mbuf_pool_free: rte_mempool_free(ts_params->mbuf_pool); ts_params->mbuf_pool = NULL; + rte_free(ts_params->status_report); + rte_free(ts_params->ctrl_pdu_buf); return TEST_FAILED; } @@ -344,6 +377,9 @@ testsuite_teardown(void) rte_mempool_free(ts_params->mbuf_pool); ts_params->mbuf_pool = NULL; + + rte_free(ts_params->status_report); + rte_free(ts_params->ctrl_pdu_buf); } static int @@ -1410,6 +1446,246 @@ test_expiry_with_rte_timer(const struct pdcp_test_conf *ul_conf) return ret; } +static struct rte_pdcp_up_ctrl_pdu_hdr * +pdcp_status_report_init(uint32_t fmc) +{ + struct rte_pdcp_up_ctrl_pdu_hdr *hdr = testsuite_params.status_report; + + hdr->d_c = RTE_PDCP_PDU_TYPE_CTRL; + hdr->pdu_type = RTE_PDCP_CTRL_PDU_TYPE_STATUS_REPORT; + hdr->fmc = rte_cpu_to_be_32(fmc); + hdr->r = 0; + memset(hdr->bitmap, 0, testsuite_params.status_report_bitmask_capacity); + + return hdr; +} + +static uint32_t +pdcp_status_report_len(void) +{ + struct rte_pdcp_up_ctrl_pdu_hdr *hdr = testsuite_params.status_report; + uint32_t i; + + for (i = testsuite_params.status_report_bitmask_capacity; i != 0; i--) { + if (hdr->bitmap[i - 1]) + return i; + } + + return 0; +} + +static int +pdcp_status_report_verify(struct rte_mbuf *status_report, +const struct rte_pdcp_up_ctrl_pdu_hdr *expected_hdr, uint32_t expected_len) +{ + uint32_t received_len = rte_pktmbuf_pkt_len(status_report); + uint8_t *received_buf = testsuite_params.ctrl_pdu_buf; + int ret; + + ret = pktmbuf_read_into(status_report, received_buf, RTE_PDCP_CTRL_PDU_SIZE_MAX); + TEST_ASSERT_SUCCESS(ret, "Failed to copy status report pkt into continuous buffer"); + + debug_hexdump(stdout, "Received:", received_buf, received_len); + debug_hexdump(stdout, "Expected:", expected_hdr, expected_len); + + TEST_ASSERT_EQUAL(expected_len, received_len, + "Mismatch in packet lengths [expected: %d, received: %d]", + expected_len, received_len); + + TEST_ASSERT_BUFFERS_ARE_EQUAL(received_buf, expected_hdr, expected_len, +"Generated packet not as expected"); + + return 0; +} + +static int +test_status_report_gen(const struct pdcp_test_conf *ul_conf, + const struct rte_pdcp_up_ctrl_pdu_hdr *hdr, + uint32_t bitmap_len) +{ + const enum rte_security_pdcp_sn_size sn_size = ul_conf->entity.pdcp_xfrm.sn_size; + struct rte_mbuf *status_report = NULL, **out_mb, *m; + uint16_t nb_success = 0, nb_err = 0; + struct rte_pdcp_entity *pdcp_entity; +
[PATCH v5 06/21] pdcp: add pre and post process for UL
Add routines to perform pre & post processing based on the type of entity. To avoid checks in datapath, there are different function pointers registered based on the following, 1. Control plane v/s user plane 2. 12 bit v/s 18 bit SN For control plane only 12 bit SN need to be supported (as per PDCP specification). Signed-off-by: Anoob Joseph Signed-off-by: Kiran Kumar K Signed-off-by: Volodymyr Fialko Acked-by: Akhil Goyal --- lib/pdcp/pdcp_entity.h | 24 +++ lib/pdcp/pdcp_process.c | 334 2 files changed, 358 insertions(+) diff --git a/lib/pdcp/pdcp_entity.h b/lib/pdcp/pdcp_entity.h index 000297588f..23628ebad4 100644 --- a/lib/pdcp/pdcp_entity.h +++ b/lib/pdcp/pdcp_entity.h @@ -92,22 +92,46 @@ pdcp_hdr_size_get(enum rte_security_pdcp_sn_size sn_size) return RTE_ALIGN_MUL_CEIL(sn_size, 8) / 8; } +static inline uint32_t +pdcp_window_size_get(enum rte_security_pdcp_sn_size sn_size) +{ + return 1 << (sn_size - 1); +} + static inline uint32_t pdcp_sn_mask_get(enum rte_security_pdcp_sn_size sn_size) { return (1 << sn_size) - 1; } +static inline uint32_t +pdcp_sn_from_count_get(uint32_t count, enum rte_security_pdcp_sn_size sn_size) +{ + return (count & pdcp_sn_mask_get(sn_size)); +} + static inline uint32_t pdcp_hfn_mask_get(enum rte_security_pdcp_sn_size sn_size) { return ~pdcp_sn_mask_get(sn_size); } +static inline uint32_t +pdcp_hfn_from_count_get(uint32_t count, enum rte_security_pdcp_sn_size sn_size) +{ + return (count & pdcp_hfn_mask_get(sn_size)) >> sn_size; +} + static inline uint32_t pdcp_count_from_hfn_sn_get(uint32_t hfn, uint32_t sn, enum rte_security_pdcp_sn_size sn_size) { return (((hfn << sn_size) & pdcp_hfn_mask_get(sn_size)) | (sn & pdcp_sn_mask_get(sn_size))); } +static inline uint32_t +pdcp_hfn_max(enum rte_security_pdcp_sn_size sn_size) +{ + return (1 << (32 - sn_size)) - 1; +} + #endif /* PDCP_ENTITY_H */ diff --git a/lib/pdcp/pdcp_process.c b/lib/pdcp/pdcp_process.c index 79f5dce5db..9b7de39db6 100644 --- a/lib/pdcp/pdcp_process.c +++ b/lib/pdcp/pdcp_process.c @@ -36,6 +36,336 @@ pdcp_crypto_xfrm_get(const struct rte_pdcp_entity_conf *conf, struct rte_crypto_ return 0; } +static inline void +cop_prepare(const struct entity_priv *en_priv, struct rte_mbuf *mb, struct rte_crypto_op *cop, + uint8_t data_offset, uint32_t count, const bool is_auth) +{ + const struct rte_crypto_op cop_init = { + .type = RTE_CRYPTO_OP_TYPE_SYMMETRIC, + .status = RTE_CRYPTO_OP_STATUS_NOT_PROCESSED, + .sess_type = RTE_CRYPTO_OP_WITH_SESSION, + }; + struct rte_crypto_sym_op *op; + uint32_t pkt_len; + + const uint8_t cipher_shift = 3 * en_priv->flags.is_cipher_in_bits; + const uint8_t auth_shift = 3 * en_priv->flags.is_auth_in_bits; + + op = cop->sym; + cop->raw = cop_init.raw; + op->m_src = mb; + op->m_dst = mb; + + /* Set IV */ + en_priv->iv_gen(cop, en_priv, count); + + /* Prepare op */ + pkt_len = rte_pktmbuf_pkt_len(mb); + op->cipher.data.offset = data_offset << cipher_shift; + op->cipher.data.length = (pkt_len - data_offset) << cipher_shift; + + if (is_auth) { + op->auth.data.offset = 0; + op->auth.data.length = (pkt_len - RTE_PDCP_MAC_I_LEN) << auth_shift; + op->auth.digest.data = rte_pktmbuf_mtod_offset(mb, uint8_t *, + (pkt_len - RTE_PDCP_MAC_I_LEN)); + } + + __rte_crypto_sym_op_attach_sym_session(op, en_priv->crypto_sess); +} + +static inline bool +pdcp_pre_process_uplane_sn_12_ul_set_sn(struct entity_priv *en_priv, struct rte_mbuf *mb, + uint32_t *count) +{ + struct rte_pdcp_up_data_pdu_sn_12_hdr *pdu_hdr; + const uint8_t hdr_sz = en_priv->hdr_sz; + uint32_t sn; + + /* Prepend PDU header */ + pdu_hdr = (struct rte_pdcp_up_data_pdu_sn_12_hdr *)rte_pktmbuf_prepend(mb, hdr_sz); + if (unlikely(pdu_hdr == NULL)) + return false; + + /* Update sequence num in the PDU header */ + *count = en_priv->state.tx_next++; + sn = pdcp_sn_from_count_get(*count, RTE_SECURITY_PDCP_SN_SIZE_12); + + pdu_hdr->d_c = RTE_PDCP_PDU_TYPE_DATA; + pdu_hdr->sn_11_8 = ((sn & 0xf00) >> 8); + pdu_hdr->sn_7_0 = (sn & 0xff); + pdu_hdr->r = 0; + return true; +} + +static uint16_t +pdcp_pre_process_uplane_sn_12_ul(const struct rte_pdcp_entity *entity, struct rte_mbuf *in_mb[], +struct rte_crypto_op *cop[], uint16_t num, uint16_t *nb_err_ret) +{ + struct entity_priv *en_priv = entity_priv_get(entity); + uint16_t nb_cop, nb_prep = 0, nb_err = 0; + struct rte_mbuf *mb; + uint32_t count; + uint8_t *mac_i; + int i; + + const uint8_
[PATCH v5 17/21] test/pdcp: add timer expiry cases
From: Volodymyr Fialko Add test cases for handling the expiry with rte_timer and rte_event_timer. Signed-off-by: Anoob Joseph Signed-off-by: Volodymyr Fialko --- app/test/test_pdcp.c | 350 +++ 1 file changed, 350 insertions(+) diff --git a/app/test/test_pdcp.c b/app/test/test_pdcp.c index 24d7826bc2..25729b2bdd 100644 --- a/app/test/test_pdcp.c +++ b/app/test/test_pdcp.c @@ -3,15 +3,24 @@ */ #include +#ifdef RTE_LIB_EVENTDEV +#include +#include +#endif /* RTE_LIB_EVENTDEV */ #include #include #include +#include #include "test.h" #include "test_cryptodev.h" #include "test_cryptodev_security_pdcp_test_vectors.h" +#define NSECPERSEC 1E9 #define NB_DESC 1024 +#define TIMER_ADAPTER_ID 0 +#define TEST_EV_QUEUE_ID 0 +#define TEST_EV_PORT_ID 0 #define CDEV_INVALID_ID UINT8_MAX #define NB_TESTS RTE_DIM(pdcp_test_params) #define PDCP_IV_LEN 16 @@ -33,10 +42,21 @@ struct pdcp_testsuite_params { struct rte_mempool *cop_pool; struct rte_mempool *sess_pool; bool cdevs_used[RTE_CRYPTO_MAX_DEVS]; + int evdev; +#ifdef RTE_LIB_EVENTDEV + struct rte_event_timer_adapter *timdev; +#endif /* RTE_LIB_EVENTDEV */ + bool timer_is_running; + uint64_t min_resolution_ns; }; static struct pdcp_testsuite_params testsuite_params; +struct test_rte_timer_args { + int status; + struct rte_pdcp_entity *pdcp_entity; +}; + struct pdcp_test_conf { struct rte_pdcp_entity_conf entity; struct rte_crypto_sym_xform c_xfrm; @@ -124,6 +144,30 @@ pdcp_hfn_from_count_get(uint32_t count, enum rte_security_pdcp_sn_size sn_size) return (count & pdcp_hfn_mask_get(sn_size)) >> sn_size; } +static void +pdcp_timer_start_cb(void *timer, void *args) +{ + bool *is_timer_running = timer; + + RTE_SET_USED(args); + *is_timer_running = true; +} + +static void +pdcp_timer_stop_cb(void *timer, void *args) +{ + bool *is_timer_running = timer; + + RTE_SET_USED(args); + *is_timer_running = false; +} + +static struct rte_pdcp_t_reordering t_reorder_timer = { + .timer = &testsuite_params.timer_is_running, + .start = pdcp_timer_start_cb, + .stop = pdcp_timer_stop_cb, +}; + static inline int pdcp_hdr_size_get(enum rte_security_pdcp_sn_size sn_size) { @@ -462,6 +506,7 @@ create_test_conf_from_index(const int index, struct pdcp_test_conf *conf) conf->entity.pdcp_xfrm.en_ordering = 0; conf->entity.pdcp_xfrm.remove_duplicates = 0; conf->entity.pdcp_xfrm.domain = pdcp_test_params[index].domain; + conf->entity.t_reordering = t_reorder_timer; if (pdcp_test_packet_direction[index] == PDCP_DIR_UPLINK) conf->entity.pdcp_xfrm.pkt_dir = RTE_SECURITY_PDCP_UPLINK; @@ -1048,6 +1093,8 @@ test_reorder_gap_fill(struct pdcp_test_conf *ul_conf) /* Check that packets in correct order */ ASSERT_TRUE_OR_GOTO(array_asc_sorted_check(out_mb, nb_success, sn_size), exit, "Error occurred during packet drain\n"); + ASSERT_TRUE_OR_GOTO(testsuite_params.timer_is_running == false, exit, + "Timer should be stopped after full drain\n"); ret = TEST_SUCCESS; exit: @@ -1123,6 +1170,181 @@ test_reorder_buffer_full_window_size_sn_12(const struct pdcp_test_conf *ul_conf) return ret; } +#ifdef RTE_LIB_EVENTDEV +static void +event_timer_start_cb(void *timer, void *args) +{ + struct rte_event_timer *evtims = args; + int ret = 0; + + ret = rte_event_timer_arm_burst(timer, &evtims, 1); + assert(ret == 1); +} +#endif /* RTE_LIB_EVENTDEV */ + +static int +test_expiry_with_event_timer(const struct pdcp_test_conf *ul_conf) +{ +#ifdef RTE_LIB_EVENTDEV + const enum rte_security_pdcp_sn_size sn_size = ul_conf->entity.pdcp_xfrm.sn_size; + struct rte_mbuf *m1 = NULL, *out_mb[1] = {0}; + uint16_t n = 0, nb_err = 0, nb_try = 5; + struct rte_pdcp_entity *pdcp_entity; + struct pdcp_test_conf dl_conf; + int ret = TEST_FAILED, nb_out; + struct rte_event event; + + const int start_count = 0; + struct rte_event_timer evtim = { + .ev.op = RTE_EVENT_OP_NEW, + .ev.queue_id = TEST_EV_QUEUE_ID, + .ev.sched_type = RTE_SCHED_TYPE_ATOMIC, + .ev.priority = RTE_EVENT_DEV_PRIORITY_NORMAL, + .ev.event_type = RTE_EVENT_TYPE_TIMER, + .state = RTE_EVENT_TIMER_NOT_ARMED, + .timeout_ticks = 1, + }; + + if (ul_conf->entity.pdcp_xfrm.pkt_dir == RTE_SECURITY_PDCP_DOWNLINK) + return TEST_SKIPPED; + + /* Create configuration for actual testing */ + uplink_to_downlink_convert(ul_conf, &dl_conf); + dl_conf.entity.pdcp_xfrm.hfn = pdcp_hfn_from_count_get(start_count, sn_size); + dl_conf.entity.sn = pdcp_sn_from_count_get(start_count, sn_size); + dl_conf.entity.t_r
[PATCH v5 19/21] pdcp: add support for status report
From: Volodymyr Fialko Implement status report generation for PDCP entity. Signed-off-by: Anoob Joseph Signed-off-by: Volodymyr Fialko --- lib/pdcp/pdcp_cnt.c | 158 --- lib/pdcp/pdcp_cnt.h | 11 ++- lib/pdcp/pdcp_ctrl_pdu.c | 34 - lib/pdcp/pdcp_ctrl_pdu.h | 3 +- lib/pdcp/pdcp_entity.h | 2 + lib/pdcp/pdcp_process.c | 9 ++- lib/pdcp/pdcp_process.h | 13 lib/pdcp/rte_pdcp.c | 34 ++--- 8 files changed, 236 insertions(+), 28 deletions(-) diff --git a/lib/pdcp/pdcp_cnt.c b/lib/pdcp/pdcp_cnt.c index c9b952184b..af027b00d3 100644 --- a/lib/pdcp/pdcp_cnt.c +++ b/lib/pdcp/pdcp_cnt.c @@ -2,28 +2,164 @@ * Copyright(C) 2023 Marvell. */ +#include #include #include "pdcp_cnt.h" +#include "pdcp_ctrl_pdu.h" #include "pdcp_entity.h" +#define SLAB_BYTE_SIZE (RTE_BITMAP_SLAB_BIT_SIZE / 8) + +uint32_t +pdcp_cnt_bitmap_get_memory_footprint(const struct rte_pdcp_entity_conf *conf) +{ + uint32_t n_bits = pdcp_window_size_get(conf->pdcp_xfrm.sn_size); + + return rte_bitmap_get_memory_footprint(n_bits); +} + int -pdcp_cnt_ring_create(struct rte_pdcp_entity *en, const struct rte_pdcp_entity_conf *conf) +pdcp_cnt_bitmap_create(struct entity_priv_dl_part *dl, void *bitmap_mem, uint32_t window_size) { - struct entity_priv_dl_part *en_priv_dl; - uint32_t window_sz; + uint32_t mem_size = rte_bitmap_get_memory_footprint(window_size); - if (en == NULL || conf == NULL) + dl->bitmap.bmp = rte_bitmap_init(window_size, bitmap_mem, mem_size); + if (dl->bitmap.bmp == NULL) return -EINVAL; - if (conf->pdcp_xfrm.pkt_dir == RTE_SECURITY_PDCP_UPLINK) - return 0; + dl->bitmap.size = window_size; - en_priv_dl = entity_dl_part_get(en); - window_sz = pdcp_window_size_get(conf->pdcp_xfrm.sn_size); + return 0; +} - RTE_SET_USED(window_sz); - RTE_SET_USED(en_priv_dl); +void +pdcp_cnt_bitmap_set(struct pdcp_cnt_bitmap bitmap, uint32_t count) +{ + rte_bitmap_set(bitmap.bmp, count % bitmap.size); +} - return 0; +bool +pdcp_cnt_bitmap_is_set(struct pdcp_cnt_bitmap bitmap, uint32_t count) +{ + return rte_bitmap_get(bitmap.bmp, count % bitmap.size); +} + +void +pdcp_cnt_bitmap_range_clear(struct pdcp_cnt_bitmap bitmap, uint32_t start, uint32_t stop) +{ + uint32_t i; + + for (i = start; i < stop; i++) + rte_bitmap_clear(bitmap.bmp, i % bitmap.size); +} + +uint16_t +pdcp_cnt_get_bitmap_size(uint32_t pending_bytes) +{ + /* +* Round up bitmap size to slab size to operate only on slabs sizes, instead of individual +* bytes +*/ + return RTE_ALIGN_MUL_CEIL(pending_bytes, SLAB_BYTE_SIZE); +} + +static __rte_always_inline uint64_t +leftover_get(uint64_t slab, uint32_t shift, uint64_t mask) +{ + return (slab & mask) << shift; +} + +void +pdcp_cnt_report_fill(struct pdcp_cnt_bitmap bitmap, struct entity_state state, +uint8_t *data, uint16_t data_len) +{ + uint64_t slab = 0, next_slab = 0, leftover; + uint32_t zeros, report_len, diff; + uint32_t slab_id, next_slab_id; + uint32_t pos = 0, next_pos = 0; + + const uint32_t start_count = state.rx_deliv + 1; + const uint32_t nb_slabs = bitmap.size / RTE_BITMAP_SLAB_BIT_SIZE; + const uint32_t nb_data_slabs = data_len / SLAB_BYTE_SIZE; + const uint32_t start_slab_id = start_count / RTE_BITMAP_SLAB_BIT_SIZE; + const uint32_t stop_slab_id = (start_slab_id + nb_data_slabs) % nb_slabs; + const uint32_t shift = start_count % RTE_BITMAP_SLAB_BIT_SIZE; + const uint32_t leftover_shift = shift ? RTE_BITMAP_SLAB_BIT_SIZE - shift : 0; + const uint8_t *data_end = RTE_PTR_ADD(data, data_len + SLAB_BYTE_SIZE); + + /* NOTE: Mask required to workaround case - when shift is not needed */ + const uint64_t leftover_mask = shift ? ~0 : 0; + + /* NOTE: implement scan init at to set custom position */ + __rte_bitmap_scan_init(bitmap.bmp); + while (true) { + assert(rte_bitmap_scan(bitmap.bmp, &pos, &slab) == 1); + slab_id = pos / RTE_BITMAP_SLAB_BIT_SIZE; + if (slab_id >= start_slab_id) + break; + } + + report_len = nb_data_slabs; + + if (slab_id > start_slab_id) { + /* Zero slabs at beginning */ + zeros = (slab_id - start_slab_id - 1) * SLAB_BYTE_SIZE; + memset(data, 0, zeros); + data = RTE_PTR_ADD(data, zeros); + leftover = leftover_get(slab, leftover_shift, leftover_mask); + memcpy(data, &leftover, SLAB_BYTE_SIZE); + data = RTE_PTR_ADD(data, SLAB_BYTE_SIZE); + report_len -= (slab_id - start_slab_id); + } + + while (report_len) { + rte_bitmap_scan(bitmap.bmp, &next_pos, &next_slab); +
[PATCH v5 13/21] pdcp: implement t-Reordering and packet buffering
From: Volodymyr Fialko Add in-order delivery of packets in PDCP. Delivery of packets in-order relies on t-Reordering timer. When 'out-of-order delivery' is disabled, PDCP will buffer all received packets that are out of order. The t-Reordering timer determines the time period these packets would be held in the buffer, waiting for any missing packets to arrive. Introduce packet buffering and state variables which indicate status of the timer. Signed-off-by: Anoob Joseph Signed-off-by: Volodymyr Fialko --- lib/pdcp/meson.build| 3 +- lib/pdcp/pdcp_entity.h | 19 +++ lib/pdcp/pdcp_process.c | 117 ++-- lib/pdcp/pdcp_reorder.c | 27 ++ lib/pdcp/pdcp_reorder.h | 62 + lib/pdcp/rte_pdcp.c | 53 -- lib/pdcp/rte_pdcp.h | 6 ++- 7 files changed, 252 insertions(+), 35 deletions(-) create mode 100644 lib/pdcp/pdcp_reorder.c create mode 100644 lib/pdcp/pdcp_reorder.h diff --git a/lib/pdcp/meson.build b/lib/pdcp/meson.build index 75d476bf6d..f4f9246bcb 100644 --- a/lib/pdcp/meson.build +++ b/lib/pdcp/meson.build @@ -12,9 +12,10 @@ sources = files( 'pdcp_crypto.c', 'pdcp_ctrl_pdu.c', 'pdcp_process.c', +'pdcp_reorder.c', 'rte_pdcp.c', ) headers = files('rte_pdcp.h') indirect_headers += files('rte_pdcp_group.h') -deps += ['mbuf', 'net', 'cryptodev', 'security'] +deps += ['mbuf', 'net', 'cryptodev', 'security', 'reorder'] diff --git a/lib/pdcp/pdcp_entity.h b/lib/pdcp/pdcp_entity.h index 28691a504b..34341cdc11 100644 --- a/lib/pdcp/pdcp_entity.h +++ b/lib/pdcp/pdcp_entity.h @@ -11,6 +11,8 @@ #include #include +#include "pdcp_reorder.h" + struct entity_priv; #define PDCP_HFN_MIN 0 @@ -109,6 +111,17 @@ union cipher_iv_partial { uint64_t u64[2]; }; +enum timer_state { + TIMER_STOP, + TIMER_RUNNING, + TIMER_EXPIRED, +}; + +struct pdcp_t_reordering { + /** Represent timer state */ + enum timer_state state; +}; + struct pdcp_cnt_bitmap { /** Number of entries that can be stored. */ uint32_t size; @@ -145,6 +158,8 @@ struct entity_priv { uint64_t is_null_auth : 1; /** Is status report required.*/ uint64_t is_status_report_required : 1; + /** Is out-of-order delivery enabled */ + uint64_t is_out_of_order_delivery : 1; } flags; /** Crypto op pool. */ struct rte_mempool *cop_pool; @@ -161,6 +176,10 @@ struct entity_priv { struct entity_priv_dl_part { /** PDCP would need to track the count values that are already received.*/ struct pdcp_cnt_bitmap bitmap; + /** t-Reordering handles */ + struct pdcp_t_reordering t_reorder; + /** Reorder packet buffer */ + struct pdcp_reorder reorder; }; struct entity_priv_ul_part { diff --git a/lib/pdcp/pdcp_process.c b/lib/pdcp/pdcp_process.c index ed1413db6d..84a0f3a43f 100644 --- a/lib/pdcp/pdcp_process.c +++ b/lib/pdcp/pdcp_process.c @@ -837,25 +837,88 @@ pdcp_packet_strip(struct rte_mbuf *mb, const uint32_t hdr_trim_sz, const bool tr } } -static inline bool +static inline int pdcp_post_process_update_entity_state(const struct rte_pdcp_entity *entity, - const uint32_t count) + const uint32_t count, struct rte_mbuf *mb, + struct rte_mbuf *out_mb[], + const bool trim_mac) { struct entity_priv *en_priv = entity_priv_get(entity); + struct pdcp_t_reordering *t_reorder; + struct pdcp_reorder *reorder; + uint16_t processed = 0; - if (count < en_priv->state.rx_deliv) - return false; + struct entity_priv_dl_part *dl = entity_dl_part_get(entity); + const uint32_t hdr_trim_sz = en_priv->hdr_sz + en_priv->aad_sz; - /* t-Reordering timer is not supported - SDU will be delivered immediately. -* Update RX_DELIV to the COUNT value of the first PDCP SDU which has not -* been delivered to upper layers -*/ - en_priv->state.rx_next = count + 1; + if (count < en_priv->state.rx_deliv) + return -EINVAL; if (count >= en_priv->state.rx_next) en_priv->state.rx_next = count + 1; - return true; + pdcp_packet_strip(mb, hdr_trim_sz, trim_mac); + + if (en_priv->flags.is_out_of_order_delivery) { + out_mb[0] = mb; + en_priv->state.rx_deliv = count + 1; + + return 1; + } + + reorder = &dl->reorder; + t_reorder = &dl->t_reorder; + + if (count == en_priv->state.rx_deliv) { + if (reorder->is_active) { + /* +* This insert used only to increment reorder->min_seqn +* To remove it - min_seqn_s
[PATCH v5 18/21] test/pdcp: add timer restart case
From: Volodymyr Fialko Add test to cover the case when t-reordering timer should be restarted on the same packet. Signed-off-by: Anoob Joseph Signed-off-by: Volodymyr Fialko --- app/test/test_pdcp.c | 68 1 file changed, 68 insertions(+) diff --git a/app/test/test_pdcp.c b/app/test/test_pdcp.c index 25729b2bdd..82cc25ec7a 100644 --- a/app/test/test_pdcp.c +++ b/app/test/test_pdcp.c @@ -1106,6 +1106,71 @@ test_reorder_gap_fill(struct pdcp_test_conf *ul_conf) return ret; } +static int +test_reorder_gap_in_reorder_buffer(const struct pdcp_test_conf *ul_conf) +{ + const enum rte_security_pdcp_sn_size sn_size = ul_conf->entity.pdcp_xfrm.sn_size; + struct rte_mbuf *m = NULL, *out_mb[2] = {0}; + uint16_t nb_success = 0, nb_err = 0; + struct rte_pdcp_entity *pdcp_entity; + int ret = TEST_FAILED, nb_out, i; + struct pdcp_test_conf dl_conf; + uint8_t cdev_id; + + const int start_count = 0; + + if (ul_conf->entity.pdcp_xfrm.pkt_dir == RTE_SECURITY_PDCP_DOWNLINK) + return TEST_SKIPPED; + + /* Create configuration for actual testing */ + uplink_to_downlink_convert(ul_conf, &dl_conf); + dl_conf.entity.pdcp_xfrm.hfn = pdcp_hfn_from_count_get(start_count, sn_size); + dl_conf.entity.sn = pdcp_sn_from_count_get(start_count, sn_size); + pdcp_entity = test_entity_create(&dl_conf, &ret); + if (pdcp_entity == NULL) + return ret; + + cdev_id = dl_conf.entity.dev_id; + + /* Create two gaps [NULL, m1, NULL, m3]*/ + for (i = 0; i < 2; i++) { + m = generate_packet_for_dl_with_sn(*ul_conf, start_count + 2 * i + 1); + ASSERT_TRUE_OR_GOTO(m != NULL, exit, "Could not allocate buffer for packet\n"); + nb_success = test_process_packets(pdcp_entity, cdev_id, &m, 1, out_mb, &nb_err); + ASSERT_TRUE_OR_GOTO(nb_err == 0, exit, "Error occurred during packet process\n"); + ASSERT_TRUE_OR_GOTO(nb_success == 0, exit, "Packet was not buffered as expected\n"); + m = NULL; /* Packet was moved to PDCP lib */ + } + + /* Generate packet to fill the first gap */ + m = generate_packet_for_dl_with_sn(*ul_conf, start_count); + ASSERT_TRUE_OR_GOTO(m != NULL, exit, "Could not allocate buffer for packet\n"); + + /* +* Buffered packets after insert [m0, m1, NULL, m3] +* Only first gap should be filled, timer should be restarted for second gap +*/ + nb_success = test_process_packets(pdcp_entity, cdev_id, &m, 1, out_mb, &nb_err); + ASSERT_TRUE_OR_GOTO(nb_err == 0, exit, "Error occurred during packet process\n"); + ASSERT_TRUE_OR_GOTO(nb_success == 2, exit, + "Packet count mismatch (received: %i, expected: 2)\n", nb_success); + m = NULL; + /* Check that packets in correct order */ + ASSERT_TRUE_OR_GOTO(array_asc_sorted_check(out_mb, nb_success, sn_size), + exit, "Error occurred during packet drain\n"); + ASSERT_TRUE_OR_GOTO(testsuite_params.timer_is_running == true, exit, + "Timer should be restarted after partial drain"); + + + ret = TEST_SUCCESS; +exit: + rte_pktmbuf_free(m); + rte_pktmbuf_free_bulk(out_mb, nb_success); + nb_out = rte_pdcp_entity_release(pdcp_entity, out_mb); + rte_pktmbuf_free_bulk(out_mb, nb_out); + return ret; +} + static int test_reorder_buffer_full_window_size_sn_12(const struct pdcp_test_conf *ul_conf) { @@ -1527,6 +1592,9 @@ static struct unit_test_suite reorder_test_cases = { TEST_CASE_NAMED_WITH_DATA("test_reorder_gap_fill", ut_setup_pdcp, ut_teardown_pdcp, run_test_with_all_known_vec, test_reorder_gap_fill), + TEST_CASE_NAMED_WITH_DATA("test_reorder_gap_in_reorder_buffer", + ut_setup_pdcp, ut_teardown_pdcp, + run_test_with_all_known_vec, test_reorder_gap_in_reorder_buffer), TEST_CASE_NAMED_WITH_DATA("test_reorder_buffer_full_window_size_sn_12", ut_setup_pdcp, ut_teardown_pdcp, run_test_with_all_known_vec_until_first_pass, -- 2.25.1
[PATCH v5 21/21] test/pdcp: add PDCP status report cases
From: Volodymyr Fialko Test PDCP status report generation. Signed-off-by: Anoob Joseph Signed-off-by: Volodymyr Fialko --- app/test/test_pdcp.c | 312 +++ 1 file changed, 312 insertions(+) diff --git a/app/test/test_pdcp.c b/app/test/test_pdcp.c index 82cc25ec7a..423526380f 100644 --- a/app/test/test_pdcp.c +++ b/app/test/test_pdcp.c @@ -2,6 +2,7 @@ * Copyright(C) 2023 Marvell. */ +#include #include #ifdef RTE_LIB_EVENTDEV #include @@ -48,6 +49,9 @@ struct pdcp_testsuite_params { #endif /* RTE_LIB_EVENTDEV */ bool timer_is_running; uint64_t min_resolution_ns; + struct rte_pdcp_up_ctrl_pdu_hdr *status_report; + uint32_t status_report_bitmask_capacity; + uint8_t *ctrl_pdu_buf; }; static struct pdcp_testsuite_params testsuite_params; @@ -168,6 +172,18 @@ static struct rte_pdcp_t_reordering t_reorder_timer = { .stop = pdcp_timer_stop_cb, }; +static inline void +bitmask_set_bit(uint8_t *mask, uint32_t bit) +{ + mask[bit / 8] |= (1 << bit % 8); +} + +static inline bool +bitmask_is_bit_set(const uint8_t *mask, uint32_t bit) +{ + return mask[bit / 8] & (1 << (bit % 8)); +} + static inline int pdcp_hdr_size_get(enum rte_security_pdcp_sn_size sn_size) { @@ -314,6 +330,21 @@ testsuite_setup(void) goto cop_pool_free; } + /* Allocate memory for longest possible status report */ + ts_params->status_report_bitmask_capacity = RTE_PDCP_CTRL_PDU_SIZE_MAX - + sizeof(struct rte_pdcp_up_ctrl_pdu_hdr); + ts_params->status_report = rte_zmalloc(NULL, RTE_PDCP_CTRL_PDU_SIZE_MAX, 0); + if (ts_params->status_report == NULL) { + RTE_LOG(ERR, USER1, "Could not allocate status report\n"); + goto cop_pool_free; + } + + ts_params->ctrl_pdu_buf = rte_zmalloc(NULL, RTE_PDCP_CTRL_PDU_SIZE_MAX, 0); + if (ts_params->ctrl_pdu_buf == NULL) { + RTE_LOG(ERR, USER1, "Could not allocate status report data\n"); + goto cop_pool_free; + } + return 0; cop_pool_free: @@ -322,6 +353,8 @@ testsuite_setup(void) mbuf_pool_free: rte_mempool_free(ts_params->mbuf_pool); ts_params->mbuf_pool = NULL; + rte_free(ts_params->status_report); + rte_free(ts_params->ctrl_pdu_buf); return TEST_FAILED; } @@ -344,6 +377,9 @@ testsuite_teardown(void) rte_mempool_free(ts_params->mbuf_pool); ts_params->mbuf_pool = NULL; + + rte_free(ts_params->status_report); + rte_free(ts_params->ctrl_pdu_buf); } static int @@ -1410,6 +1446,246 @@ test_expiry_with_rte_timer(const struct pdcp_test_conf *ul_conf) return ret; } +static struct rte_pdcp_up_ctrl_pdu_hdr * +pdcp_status_report_init(uint32_t fmc) +{ + struct rte_pdcp_up_ctrl_pdu_hdr *hdr = testsuite_params.status_report; + + hdr->d_c = RTE_PDCP_PDU_TYPE_CTRL; + hdr->pdu_type = RTE_PDCP_CTRL_PDU_TYPE_STATUS_REPORT; + hdr->fmc = rte_cpu_to_be_32(fmc); + hdr->r = 0; + memset(hdr->bitmap, 0, testsuite_params.status_report_bitmask_capacity); + + return hdr; +} + +static uint32_t +pdcp_status_report_len(void) +{ + struct rte_pdcp_up_ctrl_pdu_hdr *hdr = testsuite_params.status_report; + uint32_t i; + + for (i = testsuite_params.status_report_bitmask_capacity; i != 0; i--) { + if (hdr->bitmap[i - 1]) + return i; + } + + return 0; +} + +static int +pdcp_status_report_verify(struct rte_mbuf *status_report, +const struct rte_pdcp_up_ctrl_pdu_hdr *expected_hdr, uint32_t expected_len) +{ + uint32_t received_len = rte_pktmbuf_pkt_len(status_report); + uint8_t *received_buf = testsuite_params.ctrl_pdu_buf; + int ret; + + ret = pktmbuf_read_into(status_report, received_buf, RTE_PDCP_CTRL_PDU_SIZE_MAX); + TEST_ASSERT_SUCCESS(ret, "Failed to copy status report pkt into continuous buffer"); + + debug_hexdump(stdout, "Received:", received_buf, received_len); + debug_hexdump(stdout, "Expected:", expected_hdr, expected_len); + + TEST_ASSERT_EQUAL(expected_len, received_len, + "Mismatch in packet lengths [expected: %d, received: %d]", + expected_len, received_len); + + TEST_ASSERT_BUFFERS_ARE_EQUAL(received_buf, expected_hdr, expected_len, +"Generated packet not as expected"); + + return 0; +} + +static int +test_status_report_gen(const struct pdcp_test_conf *ul_conf, + const struct rte_pdcp_up_ctrl_pdu_hdr *hdr, + uint32_t bitmap_len) +{ + const enum rte_security_pdcp_sn_size sn_size = ul_conf->entity.pdcp_xfrm.sn_size; + struct rte_mbuf *status_report = NULL, **out_mb, *m; + uint16_t nb_success = 0, nb_err = 0; + struct rte_pdcp_entity *pdcp_entity; +