[v2,2/4] raw/aesni_mb: add aesni_mb raw device

Message ID 20200403163656.60545-3-david.coyle@intel.com (mailing list archive)
State Superseded, archived
Headers
Series introduce multi-function processing support |

Checks

Context Check Description
ci/checkpatch success coding style OK
ci/Intel-compilation fail Compilation issues

Commit Message

Coyle, David April 3, 2020, 4:36 p.m. UTC
  Adding an AESNI-MB raw device, thereby exposing AESNI-MB to the
rawdev API. The AESNI-MB raw device will use the multi-function
interface to allow combined operations be sent to the AESNI-MB
software library.

Signed-off-by: David Coyle <david.coyle@intel.com>
Signed-off-by: Mairtin o Loingsigh <mairtin.oloingsigh@intel.com>
---
 config/common_base                            |    6 +
 drivers/raw/Makefile                          |    2 +
 drivers/raw/aesni_mb/Makefile                 |   47 +
 drivers/raw/aesni_mb/aesni_mb_rawdev.c        | 1536 +++++++++++++++++
 drivers/raw/aesni_mb/aesni_mb_rawdev.h        |  112 ++
 drivers/raw/aesni_mb/aesni_mb_rawdev_test.c   | 1102 ++++++++++++
 .../aesni_mb/aesni_mb_rawdev_test_vectors.h   | 1183 +++++++++++++
 drivers/raw/aesni_mb/meson.build              |   26 +
 .../aesni_mb/rte_rawdev_aesni_mb_version.map  |    3 +
 drivers/raw/meson.build                       |    3 +-
 mk/rte.app.mk                                 |    2 +
 11 files changed, 4021 insertions(+), 1 deletion(-)
 create mode 100644 drivers/raw/aesni_mb/Makefile
 create mode 100644 drivers/raw/aesni_mb/aesni_mb_rawdev.c
 create mode 100644 drivers/raw/aesni_mb/aesni_mb_rawdev.h
 create mode 100644 drivers/raw/aesni_mb/aesni_mb_rawdev_test.c
 create mode 100644 drivers/raw/aesni_mb/aesni_mb_rawdev_test_vectors.h
 create mode 100644 drivers/raw/aesni_mb/meson.build
 create mode 100644 drivers/raw/aesni_mb/rte_rawdev_aesni_mb_version.map
  

Comments

De Lara Guarch, Pablo April 7, 2020, 6:51 p.m. UTC | #1
Hi David,

> -----Original Message-----
> From: Coyle, David <david.coyle@intel.com>
> Sent: Friday, April 3, 2020 5:37 PM
> To: dev@dpdk.org
> Cc: Doherty, Declan <declan.doherty@intel.com>; Trahe, Fiona
> <fiona.trahe@intel.com>; De Lara Guarch, Pablo
> <pablo.de.lara.guarch@intel.com>; Ryan, Brendan <brendan.ryan@intel.com>;
> shreyansh.jain@nxp.com; hemant.agrawal@nxp.com; Coyle, David
> <david.coyle@intel.com>; O'loingsigh, Mairtin <mairtin.oloingsigh@intel.com>
> Subject: [PATCH v2 2/4] raw/aesni_mb: add aesni_mb raw device
> 
> Adding an AESNI-MB raw device, thereby exposing AESNI-MB to the
> rawdev API. The AESNI-MB raw device will use the multi-function
> interface to allow combined operations be sent to the AESNI-MB
> software library.
> 
> Signed-off-by: David Coyle <david.coyle@intel.com>
> Signed-off-by: Mairtin o Loingsigh <mairtin.oloingsigh@intel.com>
> ---
>  config/common_base                            |    6 +
>  drivers/raw/Makefile                          |    2 +
>  drivers/raw/aesni_mb/Makefile                 |   47 +
>  drivers/raw/aesni_mb/aesni_mb_rawdev.c        | 1536 +++++++++++++++++
>  drivers/raw/aesni_mb/aesni_mb_rawdev.h        |  112 ++
>  drivers/raw/aesni_mb/aesni_mb_rawdev_test.c   | 1102 ++++++++++++
>  .../aesni_mb/aesni_mb_rawdev_test_vectors.h   | 1183 +++++++++++++
>  drivers/raw/aesni_mb/meson.build              |   26 +
>  .../aesni_mb/rte_rawdev_aesni_mb_version.map  |    3 +
>  drivers/raw/meson.build                       |    3 +-
>  mk/rte.app.mk                                 |    2 +

You missed adding the PMD to the MAINTAINERS file.

>  11 files changed, 4021 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/raw/aesni_mb/Makefile
>  create mode 100644 drivers/raw/aesni_mb/aesni_mb_rawdev.c
>  create mode 100644 drivers/raw/aesni_mb/aesni_mb_rawdev.h
>  create mode 100644 drivers/raw/aesni_mb/aesni_mb_rawdev_test.c
>  create mode 100644 drivers/raw/aesni_mb/aesni_mb_rawdev_test_vectors.h
>  create mode 100644 drivers/raw/aesni_mb/meson.build
>  create mode 100644
> drivers/raw/aesni_mb/rte_rawdev_aesni_mb_version.map
> 
> diff --git a/config/common_base b/config/common_base
> index 4f004968b..7ac6a3428 100644
> --- a/config/common_base
> +++ b/config/common_base
> @@ -818,6 +818,12 @@
> CONFIG_RTE_LIBRTE_PMD_OCTEONTX2_EP_RAWDEV=y
>  #
>  CONFIG_RTE_LIBRTE_PMD_NTB_RAWDEV=y
> 
> +#
> +# Compile PMD for AESNI raw device
> +#
> +CONFIG_RTE_LIBRTE_PMD_AESNI_MB_RAWDEV=n
> +CONFIG_RTE_LIBRTE_PMD_AESNI_MB_RAWDEV_DEBUG=n
> +
>  #
>  # Compile multi-fn raw device interface
>  #
> diff --git a/drivers/raw/Makefile b/drivers/raw/Makefile
> index e16da8d95..5aa608e1e 100644
> --- a/drivers/raw/Makefile
> +++ b/drivers/raw/Makefile
> @@ -15,5 +15,7 @@ DIRS-$(CONFIG_RTE_LIBRTE_PMD_NTB_RAWDEV) += ntb
>  DIRS-$(CONFIG_RTE_LIBRTE_PMD_OCTEONTX2_DMA_RAWDEV) +=
> octeontx2_dma
>  DIRS-$(CONFIG_RTE_LIBRTE_PMD_OCTEONTX2_EP_RAWDEV) +=
> octeontx2_ep
>  DIRS-y += common
> +DIRS-$(CONFIG_RTE_LIBRTE_PMD_AESNI_MB_RAWDEV) += aesni_mb
> +DEPDIRS-aesni_mb := common
> 
>  include $(RTE_SDK)/mk/rte.subdir.mk
> diff --git a/drivers/raw/aesni_mb/Makefile b/drivers/raw/aesni_mb/Makefile
> new file mode 100644
> index 000000000..0a40b75b4
> --- /dev/null
> +++ b/drivers/raw/aesni_mb/Makefile
> @@ -0,0 +1,47 @@
> +# SPDX-License-Identifier: BSD-3-Clause
> +# Copyright(c) 2020 Intel Corporation.
> +
> +include $(RTE_SDK)/mk/rte.vars.mk
> +
> +# library name
> +LIB = librte_pmd_aesni_mb_rawdev.a
> +
> +# build flags
> +CFLAGS += -O3
> +CFLAGS += $(WERROR_FLAGS)
> +CFLAGS += -DALLOW_EXPERIMENTAL_API
> +
> +# versioning export map
> +EXPORT_MAP := rte_rawdev_aesni_mb_version.map
> +
> +# external library dependencies
> +LDLIBS += -lIPSec_MB
> +LDLIBS += -lrte_eal -lrte_mbuf -lrte_mempool -lrte_ring
> +LDLIBS += -lrte_rawdev
> +LDLIBS += -lrte_bus_vdev
> +LDLIBS += -lrte_multi_fn
> +
> +ifneq ($(CONFIG_RTE_LIBRTE_MULTI_FN_COMMON),y)
> +$(error "RTE_LIBRTE_MULTI_FN_COMMON is required to build aesni_mb raw
> device")
> +endif
> +
> +IMB_HDR = $(shell echo '\#include <intel-ipsec-mb.h>' | \
> +	$(CC) -E $(EXTRA_CFLAGS) - | grep 'intel-ipsec-mb.h' | \
> +	head -n1 | cut -d'"' -f2)
> +
> +# Detect library version
> +IMB_VERSION = $(shell grep -e "IMB_VERSION_STR" $(IMB_HDR) | cut -d'"' -
> f2)
> +IMB_VERSION_NUM = $(shell grep -e "IMB_VERSION_NUM" $(IMB_HDR) | cut
> -d' ' -f3)
> +
> +ifeq ($(IMB_VERSION),)
> +$(error "IPSec_MB version >= 0.53.3 is required to build aesni_mb raw device")
> +endif
> +
> +ifeq ($(shell expr $(IMB_VERSION_NUM) \< 0x3503), 1)
> +$(error "IPSec_MB version >= 0.53.3 is required to build aesni_mb raw device")
> +endif
> +
> +SRCS-$(CONFIG_RTE_LIBRTE_PMD_AESNI_MB_RAWDEV) +=
> aesni_mb_rawdev.c
> +SRCS-$(CONFIG_RTE_LIBRTE_PMD_AESNI_MB_RAWDEV) +=
> aesni_mb_rawdev_test.c
> +
> +include $(RTE_SDK)/mk/rte.lib.mk
> diff --git a/drivers/raw/aesni_mb/aesni_mb_rawdev.c
> b/drivers/raw/aesni_mb/aesni_mb_rawdev.c
> new file mode 100644
> index 000000000..946bdd871
> --- /dev/null
> +++ b/drivers/raw/aesni_mb/aesni_mb_rawdev.c
> @@ -0,0 +1,1536 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * Copyright(c) 2020 Intel Corporation.
> + */
> +
> +#include <stdbool.h>
> +
> +#include <intel-ipsec-mb.h>
> +
> +#include <rte_common.h>
> +#include <rte_hexdump.h>
> +#include <rte_cryptodev.h>
> +#include <rte_dev.h>
> +#include <rte_eal.h>
> +#include <rte_bus_vdev.h>
> +#include <rte_malloc.h>
> +#include <rte_cpuflags.h>
> +#include <rte_rawdev.h>
> +#include <rte_rawdev_pmd.h>
> +#include <rte_string_fns.h>
> +#include <rte_multi_fn.h>
> +#include <rte_ether.h>

No need for <rte_hexdump.h>, <rte_cryptodev.h>, <rte_dev.h>, <rte_cpuflags.h> and <rte_multi_fn.h>.
I think <rte_crypto.h> is missing, though, for "rte_crypto_sym_xform".

...

> +static bool
> +docsis_crc_crypto_encrypt_check(struct rte_multi_fn_xform *xform)
> +{
> +	struct rte_crypto_sym_xform *crypto_sym;
> +	struct rte_multi_fn_err_detect_xform *err_detect;
> +	struct rte_multi_fn_xform *next;
> +
> +	if (xform->type == RTE_MULTI_FN_XFORM_TYPE_ERR_DETECT) {
> +
> +		err_detect = &xform->err_detect;
> +		next = xform->next;
> +
> +		if (err_detect->algo ==
> +				RTE_MULTI_FN_ERR_DETECT_CRC32_ETH &&
> +		    err_detect->op ==
> +				RTE_MULTI_FN_ERR_DETECT_OP_GENERATE
> &&

I don't think leading spaces are allowed. Generally, double tab is used
in multi-line if's. Same applies in other parts of the code.

> +		    next != NULL &&
> +		    next->type == RTE_MULTI_FN_XFORM_TYPE_CRYPTO_SYM) {
> +

...

> +static bool
> +docsis_crypto_decrypt_crc_check(struct rte_multi_fn_xform *xform)
> +{
> +	struct rte_crypto_sym_xform *crypto_sym;
> +	struct rte_multi_fn_err_detect_xform *err_detect;
> +	struct rte_multi_fn_xform *next;
> +
> +	if (xform->type == RTE_MULTI_FN_XFORM_TYPE_CRYPTO_SYM) {

I think in order to reduce this many indentation levels, you can check for the opposite here and return false.

If (xform->type != RTE...)
	return false
...

> +
> +static bool
> +pon_crc_crypto_encrypt_bip_check(struct rte_multi_fn_xform *xform)
> +{
> +	struct rte_crypto_sym_xform *crypto_sym;
> +	struct rte_multi_fn_err_detect_xform *err_detect;
> +	struct rte_multi_fn_xform *next;
> +
> +	if (xform->type == RTE_MULTI_FN_XFORM_TYPE_ERR_DETECT) {
> +
> +		err_detect = &xform->err_detect;
> +		next = xform->next;

Same as above here.

> +
> +		if (err_detect->algo ==
> +				RTE_MULTI_FN_ERR_DETECT_CRC32_ETH &&
> +		    err_detect->op ==

> +static bool
> +pon_bip_crypto_decrypt_crc_check(struct rte_multi_fn_xform *xform)
> +{
> +	struct rte_crypto_sym_xform *crypto_sym;
> +	struct rte_multi_fn_err_detect_xform *err_detect;
> +	struct rte_multi_fn_xform *next;
> +
> +	if (xform->type == RTE_MULTI_FN_XFORM_TYPE_ERR_DETECT) {
> +
> +		err_detect = &xform->err_detect;
> +		next = xform->next;

Same as above.

> +}
> +
> +static enum aesni_mb_rawdev_op
> +session_support_check(struct rte_multi_fn_xform *xform)
> +{
> +	enum aesni_mb_rawdev_op op =
> AESNI_MB_RAWDEV_OP_NOT_SUPPORTED;
> +

...

> +static inline int
> +docsis_crypto_crc_check(struct rte_multi_fn_op *first_op,
> +			struct rte_multi_fn_op *cipher_op,
> +			struct rte_multi_fn_op *crc_op)
> +{
> +	struct rte_multi_fn_op *err_op = NULL;
> +	uint8_t err_op_status;
> +	const uint32_t offset_diff = DOCSIS_CIPHER_CRC_OFFSET_DIFF;
> +
> +	if (cipher_op->crypto_sym.cipher.data.length &&
> +	    crc_op->err_detect.data.length) {
> +		/* Cipher offset must be at least 12 greater than CRC offset */
> +		if (cipher_op->crypto_sym.cipher.data.offset <
> +		    ((uint32_t)crc_op->err_detect.data.offset + offset_diff)) {
> +			err_op = crc_op;
> +			err_op_status =
> RTE_MULTI_FN_ERR_DETECT_OP_STATUS_ERROR;
> +		/*
> +		 * Cipher length must be at least 8 less than CRC length, taking
> +		 * known differences of what is ciphered and what is crc'ed into
> +		 * account
> +		 */
> +		} else if ((cipher_op->crypto_sym.cipher.data.length +
> +				DOCSIS_CIPHER_CRC_LENGTH_DIFF) >

For consistency, you can use offset_diff here too, instead of the macro.

> +			    crc_op->err_detect.data.length) {
> +			err_op = crc_op;
> +			err_op_status =
> RTE_MULTI_FN_ERR_DETECT_OP_STATUS_ERROR;
> +		}
> +	}

...

> +static inline int
> +mb_job_params_set(JOB_AES_HMAC *job,
> +		  struct aesni_mb_rawdev_qp *qp,
> +		  struct rte_multi_fn_op *op,
> +		  uint8_t *output_idx)
> +{
> +	struct rte_mbuf *m_src, *m_dst;
> +	struct rte_multi_fn_op *cipher_op;
> +	struct rte_multi_fn_op *crc_op;
> +	struct rte_multi_fn_op *bip_op;
> +	uint32_t cipher_offset;
> +	struct aesni_mb_rawdev_session *session;
> +

...

> +				cipher_op->crypto_sym.cipher.data.length;

I would declare a variable like sym_op to reduce the length of these.
Maybe also for err_dectect.

> +
> +	switch (session->op) {
> +	case AESNI_MB_RAWDEV_OP_DOCSIS_CRC_CRYPTO:
> +	case AESNI_MB_RAWDEV_OP_DOCSIS_CRYPTO_CRC:
> +		job->hash_start_src_offset_in_bytes =
> +						crc_op-

...


> +++ b/drivers/raw/aesni_mb/aesni_mb_rawdev_test.c
> @@ -0,0 +1,1102 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * Copyright(c) 2020 Intel Corporation.

Could this be moved under the test app? Looks odd here.

Thanks,
Pablo
  
De Lara Guarch, Pablo April 8, 2020, 10:44 a.m. UTC | #2
Hi David,

> -----Original Message-----
> From: dev <dev-bounces@dpdk.org> On Behalf Of De Lara Guarch, Pablo
> Sent: Tuesday, April 7, 2020 7:51 PM
> To: Coyle, David <david.coyle@intel.com>; dev@dpdk.org
> Cc: Doherty, Declan <declan.doherty@intel.com>; Trahe, Fiona
> <fiona.trahe@intel.com>; Ryan, Brendan <brendan.ryan@intel.com>;
> shreyansh.jain@nxp.com; hemant.agrawal@nxp.com; O'loingsigh, Mairtin
> <mairtin.oloingsigh@intel.com>
> Subject: Re: [dpdk-dev] [PATCH v2 2/4] raw/aesni_mb: add aesni_mb raw device
> 
> Hi David,
> 
> > -----Original Message-----
> > From: Coyle, David <david.coyle@intel.com>
> > Sent: Friday, April 3, 2020 5:37 PM
> > To: dev@dpdk.org
> > Cc: Doherty, Declan <declan.doherty@intel.com>; Trahe, Fiona
> > <fiona.trahe@intel.com>; De Lara Guarch, Pablo
> > <pablo.de.lara.guarch@intel.com>; Ryan, Brendan
> > <brendan.ryan@intel.com>; shreyansh.jain@nxp.com;
> > hemant.agrawal@nxp.com; Coyle, David <david.coyle@intel.com>;
> > O'loingsigh, Mairtin <mairtin.oloingsigh@intel.com>
> > Subject: [PATCH v2 2/4] raw/aesni_mb: add aesni_mb raw device
> >
> > Adding an AESNI-MB raw device, thereby exposing AESNI-MB to the rawdev
> > API. The AESNI-MB raw device will use the multi-function interface to
> > allow combined operations be sent to the AESNI-MB software library.
> >
> > Signed-off-by: David Coyle <david.coyle@intel.com>
> > Signed-off-by: Mairtin o Loingsigh <mairtin.oloingsigh@intel.com>
> > ---
> >  config/common_base                            |    6 +
> >  drivers/raw/Makefile                          |    2 +
> >  drivers/raw/aesni_mb/Makefile                 |   47 +
> >  drivers/raw/aesni_mb/aesni_mb_rawdev.c        | 1536 +++++++++++++++++
> >  drivers/raw/aesni_mb/aesni_mb_rawdev.h        |  112 ++
> >  drivers/raw/aesni_mb/aesni_mb_rawdev_test.c   | 1102 ++++++++++++
> >  .../aesni_mb/aesni_mb_rawdev_test_vectors.h   | 1183 +++++++++++++
> >  drivers/raw/aesni_mb/meson.build              |   26 +
> >  .../aesni_mb/rte_rawdev_aesni_mb_version.map  |    3 +
> >  drivers/raw/meson.build                       |    3 +-
> >  mk/rte.app.mk                                 |    2 +
> 
> You missed adding the PMD to the MAINTAINERS file.
> 
> >  11 files changed, 4021 insertions(+), 1 deletion(-)  create mode
> > 100644 drivers/raw/aesni_mb/Makefile  create mode 100644
> > drivers/raw/aesni_mb/aesni_mb_rawdev.c
> >  create mode 100644 drivers/raw/aesni_mb/aesni_mb_rawdev.h
> >  create mode 100644 drivers/raw/aesni_mb/aesni_mb_rawdev_test.c
> >  create mode 100644
> > drivers/raw/aesni_mb/aesni_mb_rawdev_test_vectors.h
> >  create mode 100644 drivers/raw/aesni_mb/meson.build  create mode
> > 100644 drivers/raw/aesni_mb/rte_rawdev_aesni_mb_version.map
> >
> > diff --git a/config/common_base b/config/common_base index
> > 4f004968b..7ac6a3428 100644
> > --- a/config/common_base
> > +++ b/config/common_base
> > @@ -818,6 +818,12 @@
> > CONFIG_RTE_LIBRTE_PMD_OCTEONTX2_EP_RAWDEV=y
> >  #
> >  CONFIG_RTE_LIBRTE_PMD_NTB_RAWDEV=y
> >
> > +#
> > +# Compile PMD for AESNI raw device
> > +#
> > +CONFIG_RTE_LIBRTE_PMD_AESNI_MB_RAWDEV=n
> > +CONFIG_RTE_LIBRTE_PMD_AESNI_MB_RAWDEV_DEBUG=n
> > +
> >  #
> >  # Compile multi-fn raw device interface  # diff --git
> > a/drivers/raw/Makefile b/drivers/raw/Makefile index
> > e16da8d95..5aa608e1e 100644
> > --- a/drivers/raw/Makefile
> > +++ b/drivers/raw/Makefile
> > @@ -15,5 +15,7 @@ DIRS-$(CONFIG_RTE_LIBRTE_PMD_NTB_RAWDEV) +=
> ntb
> >  DIRS-$(CONFIG_RTE_LIBRTE_PMD_OCTEONTX2_DMA_RAWDEV) +=
> octeontx2_dma
> >  DIRS-$(CONFIG_RTE_LIBRTE_PMD_OCTEONTX2_EP_RAWDEV) +=
> octeontx2_ep
> > DIRS-y += common
> > +DIRS-$(CONFIG_RTE_LIBRTE_PMD_AESNI_MB_RAWDEV) += aesni_mb
> > +DEPDIRS-aesni_mb := common
> >
> >  include $(RTE_SDK)/mk/rte.subdir.mk
> > diff --git a/drivers/raw/aesni_mb/Makefile
> > b/drivers/raw/aesni_mb/Makefile new file mode 100644 index
> > 000000000..0a40b75b4
> > --- /dev/null
> > +++ b/drivers/raw/aesni_mb/Makefile
> > @@ -0,0 +1,47 @@
> > +# SPDX-License-Identifier: BSD-3-Clause # Copyright(c) 2020 Intel
> > +Corporation.
> > +
> > +include $(RTE_SDK)/mk/rte.vars.mk
> > +
> > +# library name
> > +LIB = librte_pmd_aesni_mb_rawdev.a
> > +
> > +# build flags
> > +CFLAGS += -O3
> > +CFLAGS += $(WERROR_FLAGS)
> > +CFLAGS += -DALLOW_EXPERIMENTAL_API
> > +
> > +# versioning export map
> > +EXPORT_MAP := rte_rawdev_aesni_mb_version.map
> > +
> > +# external library dependencies
> > +LDLIBS += -lIPSec_MB
> > +LDLIBS += -lrte_eal -lrte_mbuf -lrte_mempool -lrte_ring LDLIBS +=
> > +-lrte_rawdev LDLIBS += -lrte_bus_vdev LDLIBS += -lrte_multi_fn
> > +
> > +ifneq ($(CONFIG_RTE_LIBRTE_MULTI_FN_COMMON),y)
> > +$(error "RTE_LIBRTE_MULTI_FN_COMMON is required to build aesni_mb
> raw
> > device")
> > +endif
> > +
> > +IMB_HDR = $(shell echo '\#include <intel-ipsec-mb.h>' | \
> > +	$(CC) -E $(EXTRA_CFLAGS) - | grep 'intel-ipsec-mb.h' | \
> > +	head -n1 | cut -d'"' -f2)
> > +
> > +# Detect library version
> > +IMB_VERSION = $(shell grep -e "IMB_VERSION_STR" $(IMB_HDR) | cut
> > +-d'"' -
> > f2)
> > +IMB_VERSION_NUM = $(shell grep -e "IMB_VERSION_NUM" $(IMB_HDR) |
> cut
> > -d' ' -f3)
> > +
> > +ifeq ($(IMB_VERSION),)
> > +$(error "IPSec_MB version >= 0.53.3 is required to build aesni_mb raw
> > +device") endif
> > +
> > +ifeq ($(shell expr $(IMB_VERSION_NUM) \< 0x3503), 1) $(error
> > +"IPSec_MB version >= 0.53.3 is required to build aesni_mb raw
> > +device") endif
> > +
> > +SRCS-$(CONFIG_RTE_LIBRTE_PMD_AESNI_MB_RAWDEV) +=
> > aesni_mb_rawdev.c
> > +SRCS-$(CONFIG_RTE_LIBRTE_PMD_AESNI_MB_RAWDEV) +=
> > aesni_mb_rawdev_test.c
> > +
> > +include $(RTE_SDK)/mk/rte.lib.mk
> > diff --git a/drivers/raw/aesni_mb/aesni_mb_rawdev.c
> > b/drivers/raw/aesni_mb/aesni_mb_rawdev.c
> > new file mode 100644
> > index 000000000..946bdd871
> > --- /dev/null
> > +++ b/drivers/raw/aesni_mb/aesni_mb_rawdev.c
> > @@ -0,0 +1,1536 @@
> > +/* SPDX-License-Identifier: BSD-3-Clause
> > + * Copyright(c) 2020 Intel Corporation.
> > + */
> > +
> > +#include <stdbool.h>
> > +
> > +#include <intel-ipsec-mb.h>
> > +
> > +#include <rte_common.h>
> > +#include <rte_hexdump.h>
> > +#include <rte_cryptodev.h>
> > +#include <rte_dev.h>
> > +#include <rte_eal.h>
> > +#include <rte_bus_vdev.h>
> > +#include <rte_malloc.h>
> > +#include <rte_cpuflags.h>
> > +#include <rte_rawdev.h>
> > +#include <rte_rawdev_pmd.h>
> > +#include <rte_string_fns.h>
> > +#include <rte_multi_fn.h>
> > +#include <rte_ether.h>
> 
> No need for <rte_hexdump.h>, <rte_cryptodev.h>, <rte_dev.h>,
> <rte_cpuflags.h> and <rte_multi_fn.h>.
> I think <rte_crypto.h> is missing, though, for "rte_crypto_sym_xform".
> 
> ...
> 
> > +static bool
> > +docsis_crc_crypto_encrypt_check(struct rte_multi_fn_xform *xform) {
> > +	struct rte_crypto_sym_xform *crypto_sym;
> > +	struct rte_multi_fn_err_detect_xform *err_detect;
> > +	struct rte_multi_fn_xform *next;
> > +
> > +	if (xform->type == RTE_MULTI_FN_XFORM_TYPE_ERR_DETECT) {
> > +
> > +		err_detect = &xform->err_detect;
> > +		next = xform->next;
> > +
> > +		if (err_detect->algo ==
> > +				RTE_MULTI_FN_ERR_DETECT_CRC32_ETH &&
> > +		    err_detect->op ==
> > +				RTE_MULTI_FN_ERR_DETECT_OP_GENERATE
> > &&
> 
> I don't think leading spaces are allowed. Generally, double tab is used in multi-
> line if's. Same applies in other parts of the code.
> 
> > +		    next != NULL &&
> > +		    next->type == RTE_MULTI_FN_XFORM_TYPE_CRYPTO_SYM) {
> > +
> 
> ...
> 
> > +static bool
> > +docsis_crypto_decrypt_crc_check(struct rte_multi_fn_xform *xform) {
> > +	struct rte_crypto_sym_xform *crypto_sym;
> > +	struct rte_multi_fn_err_detect_xform *err_detect;
> > +	struct rte_multi_fn_xform *next;
> > +
> > +	if (xform->type == RTE_MULTI_FN_XFORM_TYPE_CRYPTO_SYM) {
> 
> I think in order to reduce this many indentation levels, you can check for the
> opposite here and return false.
> 
> If (xform->type != RTE...)
> 	return false
> ...
> 
> > +
> > +static bool
> > +pon_crc_crypto_encrypt_bip_check(struct rte_multi_fn_xform *xform) {
> > +	struct rte_crypto_sym_xform *crypto_sym;
> > +	struct rte_multi_fn_err_detect_xform *err_detect;
> > +	struct rte_multi_fn_xform *next;
> > +
> > +	if (xform->type == RTE_MULTI_FN_XFORM_TYPE_ERR_DETECT) {
> > +
> > +		err_detect = &xform->err_detect;
> > +		next = xform->next;
> 
> Same as above here.
> 
> > +
> > +		if (err_detect->algo ==
> > +				RTE_MULTI_FN_ERR_DETECT_CRC32_ETH &&
> > +		    err_detect->op ==
> 
> > +static bool
> > +pon_bip_crypto_decrypt_crc_check(struct rte_multi_fn_xform *xform) {
> > +	struct rte_crypto_sym_xform *crypto_sym;
> > +	struct rte_multi_fn_err_detect_xform *err_detect;
> > +	struct rte_multi_fn_xform *next;
> > +
> > +	if (xform->type == RTE_MULTI_FN_XFORM_TYPE_ERR_DETECT) {
> > +
> > +		err_detect = &xform->err_detect;
> > +		next = xform->next;
> 
> Same as above.
> 
> > +}
> > +
> > +static enum aesni_mb_rawdev_op
> > +session_support_check(struct rte_multi_fn_xform *xform) {
> > +	enum aesni_mb_rawdev_op op =
> > AESNI_MB_RAWDEV_OP_NOT_SUPPORTED;
> > +
> 
> ...
> 
> > +static inline int
> > +docsis_crypto_crc_check(struct rte_multi_fn_op *first_op,
> > +			struct rte_multi_fn_op *cipher_op,
> > +			struct rte_multi_fn_op *crc_op)
> > +{
> > +	struct rte_multi_fn_op *err_op = NULL;
> > +	uint8_t err_op_status;
> > +	const uint32_t offset_diff = DOCSIS_CIPHER_CRC_OFFSET_DIFF;
> > +
> > +	if (cipher_op->crypto_sym.cipher.data.length &&
> > +	    crc_op->err_detect.data.length) {
> > +		/* Cipher offset must be at least 12 greater than CRC offset */
> > +		if (cipher_op->crypto_sym.cipher.data.offset <
> > +		    ((uint32_t)crc_op->err_detect.data.offset + offset_diff)) {
> > +			err_op = crc_op;
> > +			err_op_status =
> > RTE_MULTI_FN_ERR_DETECT_OP_STATUS_ERROR;
> > +		/*
> > +		 * Cipher length must be at least 8 less than CRC length, taking
> > +		 * known differences of what is ciphered and what is crc'ed into
> > +		 * account
> > +		 */
> > +		} else if ((cipher_op->crypto_sym.cipher.data.length +
> > +				DOCSIS_CIPHER_CRC_LENGTH_DIFF) >
> 
> For consistency, you can use offset_diff here too, instead of the macro.
> 
> > +			    crc_op->err_detect.data.length) {
> > +			err_op = crc_op;
> > +			err_op_status =
> > RTE_MULTI_FN_ERR_DETECT_OP_STATUS_ERROR;
> > +		}
> > +	}
> 
> ...
> 
> > +static inline int
> > +mb_job_params_set(JOB_AES_HMAC *job,
> > +		  struct aesni_mb_rawdev_qp *qp,
> > +		  struct rte_multi_fn_op *op,
> > +		  uint8_t *output_idx)
> > +{
> > +	struct rte_mbuf *m_src, *m_dst;
> > +	struct rte_multi_fn_op *cipher_op;
> > +	struct rte_multi_fn_op *crc_op;
> > +	struct rte_multi_fn_op *bip_op;
> > +	uint32_t cipher_offset;
> > +	struct aesni_mb_rawdev_session *session;
> > +
> 
> ...
> 
> > +				cipher_op->crypto_sym.cipher.data.length;
> 
> I would declare a variable like sym_op to reduce the length of these.
> Maybe also for err_dectect.
> 
> > +
> > +	switch (session->op) {
> > +	case AESNI_MB_RAWDEV_OP_DOCSIS_CRC_CRYPTO:
> > +	case AESNI_MB_RAWDEV_OP_DOCSIS_CRYPTO_CRC:
> > +		job->hash_start_src_offset_in_bytes =
> > +						crc_op-
> 
> ...
> 
> 
> > +++ b/drivers/raw/aesni_mb/aesni_mb_rawdev_test.c
> > @@ -0,0 +1,1102 @@
> > +/* SPDX-License-Identifier: BSD-3-Clause
> > + * Copyright(c) 2020 Intel Corporation.
> 
> Could this be moved under the test app? Looks odd here.

Just realized that other drivers are also including tests in their folders,
so I guess this is consistent too (sorry, I wasn't used to this new method).

Thanks,
Pablo
> 
> Thanks,
> Pablo
  
Coyle, David April 10, 2020, 2:34 p.m. UTC | #3
Hi Pablo
Thank you for reviewing and the comments - see below for resolutions.
The changes will be available in v3 shortly

David

> -----Original Message-----
> From: De Lara Guarch, Pablo <pablo.de.lara.guarch@intel.com>
> Sent: Tuesday, April 7, 2020 7:51 PM
> 
> Hi David,
> 
> > -----Original Message-----
> > From: Coyle, David <david.coyle@intel.com>
> > Sent: Friday, April 3, 2020 5:37 PM
> >
> > Adding an AESNI-MB raw device, thereby exposing AESNI-MB to the
> rawdev
> > API. The AESNI-MB raw device will use the multi-function interface to
> > allow combined operations be sent to the AESNI-MB software library.
> >
> > Signed-off-by: David Coyle <david.coyle@intel.com>
> > Signed-off-by: Mairtin o Loingsigh <mairtin.oloingsigh@intel.com>
> > ---
> >  config/common_base                            |    6 +
> >  drivers/raw/Makefile                          |    2 +
> >  drivers/raw/aesni_mb/Makefile                 |   47 +
> >  drivers/raw/aesni_mb/aesni_mb_rawdev.c        | 1536
> +++++++++++++++++
> >  drivers/raw/aesni_mb/aesni_mb_rawdev.h        |  112 ++
> >  drivers/raw/aesni_mb/aesni_mb_rawdev_test.c   | 1102 ++++++++++++
> >  .../aesni_mb/aesni_mb_rawdev_test_vectors.h   | 1183 +++++++++++++
> >  drivers/raw/aesni_mb/meson.build              |   26 +
> >  .../aesni_mb/rte_rawdev_aesni_mb_version.map  |    3 +
> >  drivers/raw/meson.build                       |    3 +-
> >  mk/rte.app.mk                                 |    2 +
> 
> You missed adding the PMD to the MAINTAINERS file.

[DC] Added the new directories to MAINTAINERS file
> 
> >  11 files changed, 4021 insertions(+), 1 deletion(-)  create mode
> > 100644 drivers/raw/aesni_mb/Makefile  create mode 100644
> > drivers/raw/aesni_mb/aesni_mb_rawdev.c
> >  create mode 100644 drivers/raw/aesni_mb/aesni_mb_rawdev.h
> >  create mode 100644 drivers/raw/aesni_mb/aesni_mb_rawdev_test.c
> >  create mode 100644
> > drivers/raw/aesni_mb/aesni_mb_rawdev_test_vectors.h
> >  create mode 100644 drivers/raw/aesni_mb/meson.build  create mode
> > 100644 drivers/raw/aesni_mb/rte_rawdev_aesni_mb_version.map
> >
...
> > diff --git a/drivers/raw/aesni_mb/aesni_mb_rawdev.c
> > b/drivers/raw/aesni_mb/aesni_mb_rawdev.c
> > new file mode 100644
> > index 000000000..946bdd871
> > --- /dev/null
> > +++ b/drivers/raw/aesni_mb/aesni_mb_rawdev.c
> > @@ -0,0 +1,1536 @@
> > +/* SPDX-License-Identifier: BSD-3-Clause
> > + * Copyright(c) 2020 Intel Corporation.
> > + */
> > +
> > +#include <stdbool.h>
> > +
> > +#include <intel-ipsec-mb.h>
> > +
> > +#include <rte_common.h>
> > +#include <rte_hexdump.h>
> > +#include <rte_cryptodev.h>
> > +#include <rte_dev.h>
> > +#include <rte_eal.h>
> > +#include <rte_bus_vdev.h>
> > +#include <rte_malloc.h>
> > +#include <rte_cpuflags.h>
> > +#include <rte_rawdev.h>
> > +#include <rte_rawdev_pmd.h>
> > +#include <rte_string_fns.h>
> > +#include <rte_multi_fn.h>
> > +#include <rte_ether.h>
> 
> No need for <rte_hexdump.h>, <rte_cryptodev.h>, <rte_dev.h>,
> <rte_cpuflags.h> and <rte_multi_fn.h>.
> I think <rte_crypto.h> is missing, though, for "rte_crypto_sym_xform".

[DC] Removed only hexdump and dev. All the others would seem to be needed.
rte_cpu_get_flag_enabled() is called from cpuflags
And lots of stuff from multi_fn is used throughout this file
> 
> ...
> 
> > +static bool
> > +docsis_crc_crypto_encrypt_check(struct rte_multi_fn_xform *xform) {
> > +	struct rte_crypto_sym_xform *crypto_sym;
> > +	struct rte_multi_fn_err_detect_xform *err_detect;
> > +	struct rte_multi_fn_xform *next;
> > +
> > +	if (xform->type == RTE_MULTI_FN_XFORM_TYPE_ERR_DETECT) {
> > +
> > +		err_detect = &xform->err_detect;
> > +		next = xform->next;
> > +
> > +		if (err_detect->algo ==
> > +				RTE_MULTI_FN_ERR_DETECT_CRC32_ETH
> &&
> > +		    err_detect->op ==
> > +				RTE_MULTI_FN_ERR_DETECT_OP_GENERATE
> > &&
> 
> I don't think leading spaces are allowed. Generally, double tab is used in
> multi-line if's. Same applies in other parts of the code.

[DC] Indentation of multi-line if statements have been fixed here and in other patches
> 
> > +		    next != NULL &&
> > +		    next->type ==
> RTE_MULTI_FN_XFORM_TYPE_CRYPTO_SYM) {
> > +
> 
> ...
> 
> > +static bool
> > +docsis_crypto_decrypt_crc_check(struct rte_multi_fn_xform *xform) {
> > +	struct rte_crypto_sym_xform *crypto_sym;
> > +	struct rte_multi_fn_err_detect_xform *err_detect;
> > +	struct rte_multi_fn_xform *next;
> > +
> > +	if (xform->type == RTE_MULTI_FN_XFORM_TYPE_CRYPTO_SYM) {
> 
> I think in order to reduce this many indentation levels, you can check for the
> opposite here and return false.

[DC] This was a good suggestion. Have use opposite logic throughout all these "check" functions which greatly reduces indentation

> 
> If (xform->type != RTE...)
> 	return false
> ...
> 
> > +
> > +static bool
> > +pon_crc_crypto_encrypt_bip_check(struct rte_multi_fn_xform *xform) {
> > +	struct rte_crypto_sym_xform *crypto_sym;
> > +	struct rte_multi_fn_err_detect_xform *err_detect;
> > +	struct rte_multi_fn_xform *next;
> > +
> > +	if (xform->type == RTE_MULTI_FN_XFORM_TYPE_ERR_DETECT) {
> > +
> > +		err_detect = &xform->err_detect;
> > +		next = xform->next;
> 
> Same as above here.

[DC] Same fix as above
> 
> > +
> > +		if (err_detect->algo ==
> > +				RTE_MULTI_FN_ERR_DETECT_CRC32_ETH
> &&
> > +		    err_detect->op ==
> 
> > +static bool
> > +pon_bip_crypto_decrypt_crc_check(struct rte_multi_fn_xform *xform) {
> > +	struct rte_crypto_sym_xform *crypto_sym;
> > +	struct rte_multi_fn_err_detect_xform *err_detect;
> > +	struct rte_multi_fn_xform *next;
> > +
> > +	if (xform->type == RTE_MULTI_FN_XFORM_TYPE_ERR_DETECT) {
> > +
> > +		err_detect = &xform->err_detect;
> > +		next = xform->next;
> 
> Same as above.

[DC] And same here too
> 
> > +}
> > +
> > +static enum aesni_mb_rawdev_op
> > +session_support_check(struct rte_multi_fn_xform *xform) {
> > +	enum aesni_mb_rawdev_op op =
> > AESNI_MB_RAWDEV_OP_NOT_SUPPORTED;
> > +
> 
> ...
> 
> > +static inline int
> > +docsis_crypto_crc_check(struct rte_multi_fn_op *first_op,
> > +			struct rte_multi_fn_op *cipher_op,
> > +			struct rte_multi_fn_op *crc_op)
> > +{
> > +	struct rte_multi_fn_op *err_op = NULL;
> > +	uint8_t err_op_status;
> > +	const uint32_t offset_diff = DOCSIS_CIPHER_CRC_OFFSET_DIFF;
> > +
> > +	if (cipher_op->crypto_sym.cipher.data.length &&
> > +	    crc_op->err_detect.data.length) {
> > +		/* Cipher offset must be at least 12 greater than CRC offset
> */
> > +		if (cipher_op->crypto_sym.cipher.data.offset <
> > +		    ((uint32_t)crc_op->err_detect.data.offset + offset_diff)) {
> > +			err_op = crc_op;
> > +			err_op_status =
> > RTE_MULTI_FN_ERR_DETECT_OP_STATUS_ERROR;
> > +		/*
> > +		 * Cipher length must be at least 8 less than CRC length,
> taking
> > +		 * known differences of what is ciphered and what is crc'ed
> into
> > +		 * account
> > +		 */
> > +		} else if ((cipher_op->crypto_sym.cipher.data.length +
> > +				DOCSIS_CIPHER_CRC_LENGTH_DIFF) >
> 
> For consistency, you can use offset_diff here too, instead of the macro.

[DC] This check here is a LENGTH_DIFF so re-using offset_diff would work so well
I only added the offset_diff variable to get around a compiler error, so I would have used the macro there too if I could have.
Decided not to make any change here

> 
> > +			    crc_op->err_detect.data.length) {
> > +			err_op = crc_op;
> > +			err_op_status =
> > RTE_MULTI_FN_ERR_DETECT_OP_STATUS_ERROR;
> > +		}
> > +	}
> 
> ...
> 
> > +static inline int
> > +mb_job_params_set(JOB_AES_HMAC *job,
> > +		  struct aesni_mb_rawdev_qp *qp,
> > +		  struct rte_multi_fn_op *op,
> > +		  uint8_t *output_idx)
> > +{
> > +	struct rte_mbuf *m_src, *m_dst;
> > +	struct rte_multi_fn_op *cipher_op;
> > +	struct rte_multi_fn_op *crc_op;
> > +	struct rte_multi_fn_op *bip_op;
> > +	uint32_t cipher_offset;
> > +	struct aesni_mb_rawdev_session *session;
> > +
> 
> ...
> 
> > +				cipher_op->crypto_sym.cipher.data.length;
> 
> I would declare a variable like sym_op to reduce the length of these.
> Maybe also for err_dectect.

[DC] Have added variables like you suggested which does improve things alright in terms of line length
> 
> > +
> > +	switch (session->op) {
> > +	case AESNI_MB_RAWDEV_OP_DOCSIS_CRC_CRYPTO:
> > +	case AESNI_MB_RAWDEV_OP_DOCSIS_CRYPTO_CRC:
> > +		job->hash_start_src_offset_in_bytes =
> > +						crc_op-
> 
> ...
> 
> 
> > +++ b/drivers/raw/aesni_mb/aesni_mb_rawdev_test.c
> > @@ -0,0 +1,1102 @@
> > +/* SPDX-License-Identifier: BSD-3-Clause
> > + * Copyright(c) 2020 Intel Corporation.
> 
> Could this be moved under the test app? Looks odd here.

[DC] As you already spotted yourself, this is how rawdev define there tests.
When we add the QAT multi-function PMD, we will have to look at this though as both will need the same set of tests.
May have to add the tests directly under the test app then

> 
> Thanks,
> Pablo
  

Patch

diff --git a/config/common_base b/config/common_base
index 4f004968b..7ac6a3428 100644
--- a/config/common_base
+++ b/config/common_base
@@ -818,6 +818,12 @@  CONFIG_RTE_LIBRTE_PMD_OCTEONTX2_EP_RAWDEV=y
 #
 CONFIG_RTE_LIBRTE_PMD_NTB_RAWDEV=y
 
+#
+# Compile PMD for AESNI raw device
+#
+CONFIG_RTE_LIBRTE_PMD_AESNI_MB_RAWDEV=n
+CONFIG_RTE_LIBRTE_PMD_AESNI_MB_RAWDEV_DEBUG=n
+
 #
 # Compile multi-fn raw device interface
 #
diff --git a/drivers/raw/Makefile b/drivers/raw/Makefile
index e16da8d95..5aa608e1e 100644
--- a/drivers/raw/Makefile
+++ b/drivers/raw/Makefile
@@ -15,5 +15,7 @@  DIRS-$(CONFIG_RTE_LIBRTE_PMD_NTB_RAWDEV) += ntb
 DIRS-$(CONFIG_RTE_LIBRTE_PMD_OCTEONTX2_DMA_RAWDEV) += octeontx2_dma
 DIRS-$(CONFIG_RTE_LIBRTE_PMD_OCTEONTX2_EP_RAWDEV) += octeontx2_ep
 DIRS-y += common
+DIRS-$(CONFIG_RTE_LIBRTE_PMD_AESNI_MB_RAWDEV) += aesni_mb
+DEPDIRS-aesni_mb := common
 
 include $(RTE_SDK)/mk/rte.subdir.mk
diff --git a/drivers/raw/aesni_mb/Makefile b/drivers/raw/aesni_mb/Makefile
new file mode 100644
index 000000000..0a40b75b4
--- /dev/null
+++ b/drivers/raw/aesni_mb/Makefile
@@ -0,0 +1,47 @@ 
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2020 Intel Corporation.
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+# library name
+LIB = librte_pmd_aesni_mb_rawdev.a
+
+# build flags
+CFLAGS += -O3
+CFLAGS += $(WERROR_FLAGS)
+CFLAGS += -DALLOW_EXPERIMENTAL_API
+
+# versioning export map
+EXPORT_MAP := rte_rawdev_aesni_mb_version.map
+
+# external library dependencies
+LDLIBS += -lIPSec_MB
+LDLIBS += -lrte_eal -lrte_mbuf -lrte_mempool -lrte_ring
+LDLIBS += -lrte_rawdev
+LDLIBS += -lrte_bus_vdev
+LDLIBS += -lrte_multi_fn
+
+ifneq ($(CONFIG_RTE_LIBRTE_MULTI_FN_COMMON),y)
+$(error "RTE_LIBRTE_MULTI_FN_COMMON is required to build aesni_mb raw device")
+endif
+
+IMB_HDR = $(shell echo '\#include <intel-ipsec-mb.h>' | \
+	$(CC) -E $(EXTRA_CFLAGS) - | grep 'intel-ipsec-mb.h' | \
+	head -n1 | cut -d'"' -f2)
+
+# Detect library version
+IMB_VERSION = $(shell grep -e "IMB_VERSION_STR" $(IMB_HDR) | cut -d'"' -f2)
+IMB_VERSION_NUM = $(shell grep -e "IMB_VERSION_NUM" $(IMB_HDR) | cut -d' ' -f3)
+
+ifeq ($(IMB_VERSION),)
+$(error "IPSec_MB version >= 0.53.3 is required to build aesni_mb raw device")
+endif
+
+ifeq ($(shell expr $(IMB_VERSION_NUM) \< 0x3503), 1)
+$(error "IPSec_MB version >= 0.53.3 is required to build aesni_mb raw device")
+endif
+
+SRCS-$(CONFIG_RTE_LIBRTE_PMD_AESNI_MB_RAWDEV) += aesni_mb_rawdev.c
+SRCS-$(CONFIG_RTE_LIBRTE_PMD_AESNI_MB_RAWDEV) += aesni_mb_rawdev_test.c
+
+include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/raw/aesni_mb/aesni_mb_rawdev.c b/drivers/raw/aesni_mb/aesni_mb_rawdev.c
new file mode 100644
index 000000000..946bdd871
--- /dev/null
+++ b/drivers/raw/aesni_mb/aesni_mb_rawdev.c
@@ -0,0 +1,1536 @@ 
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Intel Corporation.
+ */
+
+#include <stdbool.h>
+
+#include <intel-ipsec-mb.h>
+
+#include <rte_common.h>
+#include <rte_hexdump.h>
+#include <rte_cryptodev.h>
+#include <rte_dev.h>
+#include <rte_eal.h>
+#include <rte_bus_vdev.h>
+#include <rte_malloc.h>
+#include <rte_cpuflags.h>
+#include <rte_rawdev.h>
+#include <rte_rawdev_pmd.h>
+#include <rte_string_fns.h>
+#include <rte_multi_fn.h>
+#include <rte_ether.h>
+
+#include "aesni_mb_rawdev.h"
+
+#define MAX_QUEUES        (64)
+#define RING_NAME_MAX_LEN (64)
+
+#define PON_BIP_LEN             (4)
+#define PON_AUTH_TAG_CRC_OFFSET (4)
+
+static const uint16_t err_detect_output_byte_lengths[] = {
+	[IMB_AUTH_DOCSIS_CRC32] = RTE_ETHER_CRC_LEN,
+	[IMB_AUTH_PON_CRC_BIP] = (PON_BIP_LEN + RTE_ETHER_CRC_LEN),
+};
+
+static const char * const xstat_names[] = {
+		"successful_enqueues", "successful_dequeues",
+		"failed_enqueues", "failed_dequeues",
+};
+
+static const char *driver_name = "rawdev_aesni_mb";
+
+static int
+qp_unique_name_set(struct rte_rawdev *rawdev, struct aesni_mb_rawdev_qp *qp)
+{
+	unsigned int n = snprintf(qp->name,
+				  sizeof(qp->name),
+				  "aesni_mb_rawdev_pmd_%u_qp_%u",
+				  rawdev->dev_id,
+				  qp->id);
+
+	if (n >= sizeof(qp->name))
+		return -1;
+
+	return 0;
+}
+
+static struct rte_ring *
+qp_processed_ops_ring_create(struct aesni_mb_rawdev_qp *qp,
+			     unsigned int ring_size,
+			     int socket_id)
+{
+	struct rte_ring *r;
+	char ring_name[RING_NAME_MAX_LEN];
+
+	unsigned int n = strlcpy(ring_name, qp->name, sizeof(ring_name));
+
+	if (n >= sizeof(ring_name))
+		return NULL;
+
+	r = rte_ring_lookup(ring_name);
+	if (r) {
+		if (rte_ring_get_size(r) >= ring_size) {
+			AESNI_MB_RAWDEV_DEBUG(
+				"Reusing existing ring %s for processed ops",
+				ring_name);
+			return r;
+		}
+
+		AESNI_MB_RAWDEV_ERR(
+			"Unable to reuse existing ring %s for processed ops",
+			ring_name);
+		return NULL;
+	}
+
+	return rte_ring_create(ring_name,
+			       ring_size,
+			       socket_id,
+			       RING_F_SP_ENQ | RING_F_SC_DEQ);
+}
+
+static uint16_t
+err_detect_output_byte_length_get(JOB_HASH_ALG algo)
+{
+	return err_detect_output_byte_lengths[algo];
+}
+
+static bool
+docsis_crc_crypto_encrypt_check(struct rte_multi_fn_xform *xform)
+{
+	struct rte_crypto_sym_xform *crypto_sym;
+	struct rte_multi_fn_err_detect_xform *err_detect;
+	struct rte_multi_fn_xform *next;
+
+	if (xform->type == RTE_MULTI_FN_XFORM_TYPE_ERR_DETECT) {
+
+		err_detect = &xform->err_detect;
+		next = xform->next;
+
+		if (err_detect->algo ==
+				RTE_MULTI_FN_ERR_DETECT_CRC32_ETH &&
+		    err_detect->op ==
+				RTE_MULTI_FN_ERR_DETECT_OP_GENERATE &&
+		    next != NULL &&
+		    next->type == RTE_MULTI_FN_XFORM_TYPE_CRYPTO_SYM) {
+
+			crypto_sym = &next->crypto_sym;
+			next = next->next;
+
+			if (crypto_sym->type ==
+					RTE_CRYPTO_SYM_XFORM_CIPHER &&
+			    crypto_sym->cipher.op ==
+					RTE_CRYPTO_CIPHER_OP_ENCRYPT &&
+			    crypto_sym->cipher.algo ==
+					RTE_CRYPTO_CIPHER_AES_DOCSISBPI &&
+			    crypto_sym->cipher.key.length ==
+					IMB_KEY_AES_128_BYTES &&
+			    crypto_sym->cipher.iv.length ==
+					AES_BLOCK_SIZE &&
+			    next == NULL)
+				return true;
+		}
+	}
+
+	return false;
+}
+
+static bool
+docsis_crypto_decrypt_crc_check(struct rte_multi_fn_xform *xform)
+{
+	struct rte_crypto_sym_xform *crypto_sym;
+	struct rte_multi_fn_err_detect_xform *err_detect;
+	struct rte_multi_fn_xform *next;
+
+	if (xform->type == RTE_MULTI_FN_XFORM_TYPE_CRYPTO_SYM) {
+
+		crypto_sym = &xform->crypto_sym;
+		next = xform->next;
+
+		if (crypto_sym->type ==
+				RTE_CRYPTO_SYM_XFORM_CIPHER &&
+		    crypto_sym->cipher.op ==
+				RTE_CRYPTO_CIPHER_OP_DECRYPT &&
+		    crypto_sym->cipher.algo ==
+				RTE_CRYPTO_CIPHER_AES_DOCSISBPI &&
+		    crypto_sym->cipher.key.length ==
+				IMB_KEY_AES_128_BYTES &&
+		    crypto_sym->cipher.iv.length ==
+				AES_BLOCK_SIZE &&
+		    next != NULL &&
+		    next->type == RTE_MULTI_FN_XFORM_TYPE_ERR_DETECT) {
+
+			err_detect = &next->err_detect;
+			next = next->next;
+
+			if (err_detect->algo ==
+					RTE_MULTI_FN_ERR_DETECT_CRC32_ETH &&
+			    err_detect->op ==
+					RTE_MULTI_FN_ERR_DETECT_OP_VERIFY &&
+			    next == NULL)
+				return true;
+		}
+	}
+
+	return false;
+}
+
+static bool
+pon_crc_crypto_encrypt_bip_check(struct rte_multi_fn_xform *xform)
+{
+	struct rte_crypto_sym_xform *crypto_sym;
+	struct rte_multi_fn_err_detect_xform *err_detect;
+	struct rte_multi_fn_xform *next;
+
+	if (xform->type == RTE_MULTI_FN_XFORM_TYPE_ERR_DETECT) {
+
+		err_detect = &xform->err_detect;
+		next = xform->next;
+
+		if (err_detect->algo ==
+				RTE_MULTI_FN_ERR_DETECT_CRC32_ETH &&
+		    err_detect->op ==
+				RTE_MULTI_FN_ERR_DETECT_OP_GENERATE &&
+		    next != NULL &&
+		    next->type == RTE_MULTI_FN_XFORM_TYPE_CRYPTO_SYM) {
+
+			crypto_sym = &next->crypto_sym;
+			next = next->next;
+
+			if (crypto_sym->type ==
+					RTE_CRYPTO_SYM_XFORM_CIPHER &&
+			    crypto_sym->cipher.op ==
+					RTE_CRYPTO_CIPHER_OP_ENCRYPT &&
+			    crypto_sym->cipher.algo ==
+					RTE_CRYPTO_CIPHER_AES_CTR &&
+			    crypto_sym->cipher.key.length ==
+					IMB_KEY_AES_128_BYTES &&
+			    crypto_sym->cipher.iv.length ==
+					AES_BLOCK_SIZE &&
+			    next != NULL &&
+			    next->type ==
+				RTE_MULTI_FN_XFORM_TYPE_ERR_DETECT) {
+
+				err_detect = &next->err_detect;
+				next = next->next;
+
+				if (err_detect->algo ==
+					RTE_MULTI_FN_ERR_DETECT_BIP32 &&
+				    err_detect->op ==
+					RTE_MULTI_FN_ERR_DETECT_OP_GENERATE &&
+				    next == NULL)
+					return true;
+			}
+		}
+	}
+
+	return false;
+}
+
+static bool
+pon_bip_crypto_decrypt_crc_check(struct rte_multi_fn_xform *xform)
+{
+	struct rte_crypto_sym_xform *crypto_sym;
+	struct rte_multi_fn_err_detect_xform *err_detect;
+	struct rte_multi_fn_xform *next;
+
+	if (xform->type == RTE_MULTI_FN_XFORM_TYPE_ERR_DETECT) {
+
+		err_detect = &xform->err_detect;
+		next = xform->next;
+
+		if (err_detect->algo ==
+				RTE_MULTI_FN_ERR_DETECT_BIP32 &&
+		    err_detect->op ==
+				RTE_MULTI_FN_ERR_DETECT_OP_GENERATE &&
+		    next != NULL &&
+		    next->type == RTE_MULTI_FN_XFORM_TYPE_CRYPTO_SYM) {
+
+			crypto_sym = &next->crypto_sym;
+			next = next->next;
+
+			if (crypto_sym->type ==
+					RTE_CRYPTO_SYM_XFORM_CIPHER &&
+			    crypto_sym->cipher.op ==
+					RTE_CRYPTO_CIPHER_OP_DECRYPT &&
+			    crypto_sym->cipher.algo ==
+					RTE_CRYPTO_CIPHER_AES_CTR &&
+			    crypto_sym->cipher.key.length ==
+					IMB_KEY_AES_128_BYTES &&
+			    crypto_sym->cipher.iv.length ==
+					AES_BLOCK_SIZE &&
+			    next != NULL &&
+			    next->type ==
+				RTE_MULTI_FN_XFORM_TYPE_ERR_DETECT) {
+
+				err_detect = &next->err_detect;
+				next = next->next;
+
+				if (err_detect->algo ==
+					RTE_MULTI_FN_ERR_DETECT_CRC32_ETH &&
+				    err_detect->op ==
+					RTE_MULTI_FN_ERR_DETECT_OP_VERIFY &&
+				    next == NULL)
+					return true;
+			}
+		}
+	}
+
+	return false;
+}
+
+static enum aesni_mb_rawdev_op
+session_support_check(struct rte_multi_fn_xform *xform)
+{
+	enum aesni_mb_rawdev_op op = AESNI_MB_RAWDEV_OP_NOT_SUPPORTED;
+
+	if (docsis_crc_crypto_encrypt_check(xform))
+		op = AESNI_MB_RAWDEV_OP_DOCSIS_CRC_CRYPTO;
+	else if (docsis_crypto_decrypt_crc_check(xform))
+		op = AESNI_MB_RAWDEV_OP_DOCSIS_CRYPTO_CRC;
+	else if (pon_crc_crypto_encrypt_bip_check(xform))
+		op = AESNI_MB_RAWDEV_OP_PON_CRC_CRYPTO_BIP;
+	else if (pon_bip_crypto_decrypt_crc_check(xform))
+		op = AESNI_MB_RAWDEV_OP_PON_BIP_CRYPTO_CRC;
+
+	return op;
+}
+
+static int
+session_err_detect_parameters_set(struct aesni_mb_rawdev_session *sess)
+{
+	switch (sess->op) {
+	case AESNI_MB_RAWDEV_OP_DOCSIS_CRC_CRYPTO:
+		sess->err_detect.operation =
+					RTE_MULTI_FN_ERR_DETECT_OP_GENERATE;
+		sess->err_detect.algo = IMB_AUTH_DOCSIS_CRC32;
+		break;
+	case AESNI_MB_RAWDEV_OP_DOCSIS_CRYPTO_CRC:
+		sess->err_detect.operation = RTE_MULTI_FN_ERR_DETECT_OP_VERIFY;
+		sess->err_detect.algo = IMB_AUTH_DOCSIS_CRC32;
+		break;
+	case AESNI_MB_RAWDEV_OP_PON_CRC_CRYPTO_BIP:
+		sess->err_detect.operation =
+					RTE_MULTI_FN_ERR_DETECT_OP_GENERATE;
+		sess->err_detect.algo = IMB_AUTH_PON_CRC_BIP;
+		break;
+	case AESNI_MB_RAWDEV_OP_PON_BIP_CRYPTO_CRC:
+		sess->err_detect.operation = RTE_MULTI_FN_ERR_DETECT_OP_VERIFY;
+		sess->err_detect.algo = IMB_AUTH_PON_CRC_BIP;
+		break;
+	default:
+		AESNI_MB_RAWDEV_ERR(
+				"Unsupported operation for error detection");
+		return -ENOTSUP;
+	}
+
+	sess->err_detect.gen_output_len =
+		err_detect_output_byte_length_get(sess->err_detect.algo);
+
+	return 0;
+}
+
+static int
+session_cipher_parameters_set(const MB_MGR *mb_mgr,
+			      struct aesni_mb_rawdev_session *sess,
+			      const struct rte_crypto_sym_xform *xform)
+{
+
+	if (xform == NULL) {
+		sess->cipher.mode = IMB_CIPHER_NULL;
+		return -EINVAL;
+	}
+
+	if (xform->type != RTE_CRYPTO_SYM_XFORM_CIPHER) {
+		AESNI_MB_RAWDEV_ERR("Crypto xform not of type cipher");
+		return -EINVAL;
+	}
+
+	/* Select cipher direction */
+	switch (sess->op) {
+	case AESNI_MB_RAWDEV_OP_DOCSIS_CRC_CRYPTO:
+		sess->cipher.direction = IMB_DIR_ENCRYPT;
+		sess->cipher.mode = IMB_CIPHER_DOCSIS_SEC_BPI;
+		break;
+	case AESNI_MB_RAWDEV_OP_DOCSIS_CRYPTO_CRC:
+		sess->cipher.direction = IMB_DIR_DECRYPT;
+		sess->cipher.mode = IMB_CIPHER_DOCSIS_SEC_BPI;
+		break;
+	case AESNI_MB_RAWDEV_OP_PON_CRC_CRYPTO_BIP:
+		sess->cipher.direction = IMB_DIR_ENCRYPT;
+		sess->cipher.mode = IMB_CIPHER_PON_AES_CNTR;
+		break;
+	case AESNI_MB_RAWDEV_OP_PON_BIP_CRYPTO_CRC:
+		sess->cipher.direction = IMB_DIR_DECRYPT;
+		sess->cipher.mode = IMB_CIPHER_PON_AES_CNTR;
+		break;
+	default:
+		AESNI_MB_RAWDEV_ERR("Unsupported operation for cipher");
+		return -ENOTSUP;
+	}
+
+	/* Set IV parameters */
+	sess->iv.offset = xform->cipher.iv.offset;
+	sess->iv.length = xform->cipher.iv.length;
+
+	/* Check key length and choose key expansion function for AES */
+	switch (xform->cipher.key.length) {
+	case IMB_KEY_AES_128_BYTES:
+		sess->cipher.key_length_in_bytes = IMB_KEY_AES_128_BYTES;
+		IMB_AES_KEYEXP_128(mb_mgr,
+				   xform->cipher.key.data,
+				   sess->cipher.expanded_aes_keys.encode,
+				   sess->cipher.expanded_aes_keys.decode);
+		break;
+	case IMB_KEY_AES_256_BYTES:
+		sess->cipher.key_length_in_bytes = IMB_KEY_AES_256_BYTES;
+		IMB_AES_KEYEXP_256(mb_mgr,
+				   xform->cipher.key.data,
+				   sess->cipher.expanded_aes_keys.encode,
+				   sess->cipher.expanded_aes_keys.decode);
+		break;
+	default:
+		AESNI_MB_RAWDEV_ERR("Invalid cipher key length");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static inline struct aesni_mb_rawdev_session *
+session_get(struct rte_multi_fn_op *op)
+{
+	struct aesni_mb_rawdev_session *sess = NULL;
+
+	if (likely(op->sess != NULL))
+		sess = op->sess->sess_private_data;
+	else
+		op->overall_status = RTE_MULTI_FN_STATUS_INVALID_SESSION;
+
+	return sess;
+}
+
+static inline int
+op_chain_parse(struct aesni_mb_rawdev_session *sess,
+	       struct rte_multi_fn_op *op_chain,
+	       struct rte_multi_fn_op **cipher_op,
+	       struct rte_multi_fn_op **crc_op,
+	       struct rte_multi_fn_op **bip_op)
+{
+	*cipher_op = NULL;
+	*crc_op = NULL;
+	*bip_op = NULL;
+
+	switch (sess->op) {
+	case AESNI_MB_RAWDEV_OP_DOCSIS_CRC_CRYPTO:
+	case AESNI_MB_RAWDEV_OP_DOCSIS_CRYPTO_CRC:
+		if (unlikely(op_chain == NULL || op_chain->next == NULL)) {
+			return -EINVAL;
+		} else if (sess->op == AESNI_MB_RAWDEV_OP_DOCSIS_CRC_CRYPTO) {
+			*crc_op = op_chain;
+			*cipher_op = op_chain->next;
+		} else {
+			*cipher_op = op_chain;
+			*crc_op = op_chain->next;
+		}
+		break;
+	case AESNI_MB_RAWDEV_OP_PON_CRC_CRYPTO_BIP:
+	case AESNI_MB_RAWDEV_OP_PON_BIP_CRYPTO_CRC:
+		if (unlikely(op_chain == NULL ||
+			     op_chain->next == NULL ||
+			     op_chain->next->next == NULL)) {
+			return -EINVAL;
+		} else if (sess->op == AESNI_MB_RAWDEV_OP_PON_CRC_CRYPTO_BIP) {
+			*crc_op = op_chain;
+			*cipher_op = op_chain->next;
+			*bip_op = op_chain->next->next;
+		} else {
+			*bip_op = op_chain;
+			*cipher_op = op_chain->next;
+			*crc_op = op_chain->next->next;
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static inline void
+op_statuses_set(struct rte_multi_fn_op *first_op,
+		struct rte_multi_fn_op *cipher_op,
+		struct rte_multi_fn_op *crc_op,
+		struct rte_multi_fn_op *bip_op,
+		enum rte_multi_fn_op_status overall_status,
+		uint8_t crypto_status,
+		uint8_t err_detect_status)
+{
+	first_op->overall_status = overall_status;
+
+	if (cipher_op != NULL)
+		cipher_op->op_status = crypto_status;
+	if (crc_op != NULL)
+		crc_op->op_status = err_detect_status;
+	if (bip_op != NULL)
+		bip_op->op_status = err_detect_status;
+}
+
+#ifdef RTE_LIBRTE_PMD_AESNI_MB_RAWDEV_DEBUG
+#define DOCSIS_CIPHER_CRC_OFFSET_DIFF (RTE_ETHER_HDR_LEN - RTE_ETHER_TYPE_LEN)
+#define DOCSIS_CIPHER_CRC_LENGTH_DIFF (RTE_ETHER_HDR_LEN - \
+					RTE_ETHER_TYPE_LEN - \
+					RTE_ETHER_CRC_LEN)
+
+static inline int
+docsis_crypto_crc_check(struct rte_multi_fn_op *first_op,
+			struct rte_multi_fn_op *cipher_op,
+			struct rte_multi_fn_op *crc_op)
+{
+	struct rte_multi_fn_op *err_op = NULL;
+	uint8_t err_op_status;
+	const uint32_t offset_diff = DOCSIS_CIPHER_CRC_OFFSET_DIFF;
+
+	if (cipher_op->crypto_sym.cipher.data.length &&
+	    crc_op->err_detect.data.length) {
+		/* Cipher offset must be at least 12 greater than CRC offset */
+		if (cipher_op->crypto_sym.cipher.data.offset <
+		    ((uint32_t)crc_op->err_detect.data.offset + offset_diff)) {
+			err_op = crc_op;
+			err_op_status = RTE_MULTI_FN_ERR_DETECT_OP_STATUS_ERROR;
+		/*
+		 * Cipher length must be at least 8 less than CRC length, taking
+		 * known differences of what is ciphered and what is crc'ed into
+		 * account
+		 */
+		} else if ((cipher_op->crypto_sym.cipher.data.length +
+				DOCSIS_CIPHER_CRC_LENGTH_DIFF) >
+			    crc_op->err_detect.data.length) {
+			err_op = crc_op;
+			err_op_status = RTE_MULTI_FN_ERR_DETECT_OP_STATUS_ERROR;
+		}
+	}
+
+	if (err_op != NULL) {
+		err_op->op_status = err_op_status;
+		first_op->overall_status = RTE_MULTI_FN_OP_STATUS_FAILURE;
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+#define PON_FRAME_HDR_SIZE      (8U)
+#define PON_FRAME_MULTIPLE_SIZE (4)
+#define PON_PLI_SHIFT_BITS      (2)
+
+static inline int
+pon_crypto_crc_bip_check(struct rte_multi_fn_op *first_op,
+			 struct rte_multi_fn_op *crc_op,
+			 struct rte_multi_fn_op *bip_op,
+			 struct rte_mbuf *m_src)
+{
+	struct rte_multi_fn_op *err_op = NULL;
+	uint8_t err_op_status;
+
+	/*
+	 * BIP length must be multiple of 4 and be at least a full PON header
+	 * in size
+	 */
+	if (bip_op->err_detect.data.length % PON_FRAME_MULTIPLE_SIZE != 0 ||
+	    bip_op->err_detect.data.length < PON_FRAME_HDR_SIZE) {
+		err_op = bip_op;
+		err_op_status = RTE_MULTI_FN_ERR_DETECT_OP_STATUS_ERROR;
+	}
+
+	/*
+	 * Check the PLI field in the PON frame header matches the
+	 * CRC length
+	 */
+	uint16_t *pli_key_idx = rte_pktmbuf_mtod(m_src, uint16_t *);
+	uint16_t pli = rte_bswap16(*pli_key_idx) >> PON_PLI_SHIFT_BITS;
+	if (crc_op->err_detect.data.length != 0 &&
+	    crc_op->err_detect.data.length != (pli - RTE_ETHER_CRC_LEN)) {
+		err_op = crc_op;
+		err_op_status = RTE_MULTI_FN_ERR_DETECT_OP_STATUS_ERROR;
+	}
+
+	if (err_op != NULL) {
+		err_op->op_status = err_op_status;
+		first_op->overall_status = RTE_MULTI_FN_OP_STATUS_FAILURE;
+		return -EINVAL;
+	}
+
+	return 0;
+}
+#endif /* RTE_LIBRTE_PMD_AESNI_MB_RAWDEV_DEBUG */
+
+static inline int
+mb_job_params_set(JOB_AES_HMAC *job,
+		  struct aesni_mb_rawdev_qp *qp,
+		  struct rte_multi_fn_op *op,
+		  uint8_t *output_idx)
+{
+	struct rte_mbuf *m_src, *m_dst;
+	struct rte_multi_fn_op *cipher_op;
+	struct rte_multi_fn_op *crc_op;
+	struct rte_multi_fn_op *bip_op;
+	uint32_t cipher_offset;
+	struct aesni_mb_rawdev_session *session;
+
+	session = session_get(op);
+	if (unlikely(session == NULL)) {
+		op->overall_status = RTE_MULTI_FN_STATUS_INVALID_SESSION;
+		return -EINVAL;
+	}
+
+	if (unlikely(op_chain_parse(session,
+				    op,
+				    &cipher_op,
+				    &crc_op,
+				    &bip_op) < 0)) {
+		op_statuses_set(
+			op,
+			cipher_op,
+			crc_op,
+			bip_op,
+			RTE_MULTI_FN_OP_STATUS_FAILURE,
+			RTE_CRYPTO_OP_STATUS_NOT_PROCESSED,
+			RTE_MULTI_FN_ERR_DETECT_OP_STATUS_NOT_PROCESSED);
+		return -EINVAL;
+	}
+
+	op_statuses_set(op,
+			cipher_op,
+			crc_op,
+			bip_op,
+			RTE_MULTI_FN_OP_STATUS_NOT_PROCESSED,
+			RTE_CRYPTO_OP_STATUS_NOT_PROCESSED,
+			RTE_MULTI_FN_ERR_DETECT_OP_STATUS_NOT_PROCESSED);
+
+	m_src = op->m_src;
+
+	if (op->m_dst == NULL || op->m_dst == op->m_src) {
+		/* in-place operation */
+		m_dst = m_src;
+	} else {
+		/* out-of-place operation not supported */
+		op->overall_status = RTE_MULTI_FN_OP_STATUS_FAILURE;
+		return -EINVAL;
+	}
+
+#ifdef RTE_LIBRTE_PMD_AESNI_MB_RAWDEV_DEBUG
+	switch (session->op) {
+	case AESNI_MB_RAWDEV_OP_DOCSIS_CRC_CRYPTO:
+	case AESNI_MB_RAWDEV_OP_DOCSIS_CRYPTO_CRC:
+		if (docsis_crypto_crc_check(op, cipher_op, crc_op) < 0)
+			return -EINVAL;
+		break;
+	case AESNI_MB_RAWDEV_OP_PON_CRC_CRYPTO_BIP:
+	case AESNI_MB_RAWDEV_OP_PON_BIP_CRYPTO_CRC:
+	/*
+	 * session->op is known to be ok at this point so ok to include
+	 * default case here
+	 */
+	default:
+		if (pon_crypto_crc_bip_check(op, crc_op, bip_op, m_src) < 0)
+			return -EINVAL;
+		break;
+	}
+#endif
+
+	/* Set order */
+	job->chain_order = session->chain_order;
+
+	/* Set cipher parameters */
+	job->cipher_direction = session->cipher.direction;
+	job->cipher_mode = session->cipher.mode;
+
+	job->key_len_in_bytes = session->cipher.key_length_in_bytes;
+	job->enc_keys = session->cipher.expanded_aes_keys.encode;
+	job->dec_keys = session->cipher.expanded_aes_keys.decode;
+
+	/*
+	 * Set error detection parameters
+	 * In intel-ipsec-mb, error detection is treated as a hash algorithm
+	 */
+	job->hash_alg = session->err_detect.algo;
+
+	job->auth_tag_output = qp->temp_outputs[*output_idx];
+	*output_idx = (*output_idx + 1) % MAX_JOBS;
+
+	job->auth_tag_output_len_in_bytes = session->err_detect.gen_output_len;
+
+	/* Set data parameters */
+	cipher_offset = cipher_op->crypto_sym.cipher.data.offset;
+
+	job->src = rte_pktmbuf_mtod(m_src, uint8_t *);
+	job->dst = rte_pktmbuf_mtod_offset(m_dst, uint8_t *, cipher_offset);
+
+	job->cipher_start_src_offset_in_bytes =	cipher_offset;
+	job->msg_len_to_cipher_in_bytes =
+				cipher_op->crypto_sym.cipher.data.length;
+
+	switch (session->op) {
+	case AESNI_MB_RAWDEV_OP_DOCSIS_CRC_CRYPTO:
+	case AESNI_MB_RAWDEV_OP_DOCSIS_CRYPTO_CRC:
+		job->hash_start_src_offset_in_bytes =
+						crc_op->err_detect.data.offset;
+		job->msg_len_to_hash_in_bytes = crc_op->err_detect.data.length;
+
+		break;
+	case AESNI_MB_RAWDEV_OP_PON_CRC_CRYPTO_BIP:
+	case AESNI_MB_RAWDEV_OP_PON_BIP_CRYPTO_CRC:
+	/*
+	 * session->op is known to be ok at this point so ok to include
+	 * default case here
+	 */
+	default:
+		job->hash_start_src_offset_in_bytes =
+						bip_op->err_detect.data.offset;
+		job->msg_len_to_hash_in_bytes = bip_op->err_detect.data.length;
+
+#ifdef RTE_LIBRTE_PMD_AESNI_MB_RAWDEV_DEBUG
+#endif
+		break;
+	}
+
+	/* Set IV parameters */
+	job->iv_len_in_bytes = session->iv.length;
+	job->iv = (uint8_t *)cipher_op + session->iv.offset;
+
+	job->user_data = op;
+
+	return 0;
+}
+
+static inline void
+bip_copy(JOB_AES_HMAC *job, struct rte_multi_fn_op *bip_op)
+{
+	if (bip_op->err_detect.data.length == 0)
+		return;
+
+	/* Copy BIP to output location */
+	memcpy(bip_op->err_detect.output.data,
+	       job->auth_tag_output,
+	       PON_BIP_LEN);
+}
+
+static inline void
+crc_verify(JOB_AES_HMAC *job,
+	   struct rte_multi_fn_op *crc_op,
+	   uint8_t auth_tag_crc_offset)
+{
+	if (crc_op->err_detect.data.length == 0)
+		return;
+
+	/* Verify CRC */
+	if (memcmp(job->auth_tag_output + auth_tag_crc_offset,
+		   crc_op->err_detect.output.data,
+		   RTE_ETHER_CRC_LEN) != 0)
+		crc_op->op_status =
+			RTE_MULTI_FN_ERR_DETECT_OP_STATUS_VERIFY_FAILED;
+}
+
+static inline struct rte_multi_fn_op *
+mb_job_post_process(JOB_AES_HMAC *job)
+{
+	struct rte_multi_fn_op *op = (struct rte_multi_fn_op *)job->user_data;
+	struct aesni_mb_rawdev_session *sess = op->sess->sess_private_data;
+	struct rte_multi_fn_op *cipher_op;
+	struct rte_multi_fn_op *crc_op;
+	struct rte_multi_fn_op *bip_op;
+
+	if (unlikely(op_chain_parse(sess,
+				    op,
+				    &cipher_op,
+				    &crc_op,
+				    &bip_op) < 0)) {
+		op_statuses_set(
+			op,
+			cipher_op,
+			crc_op,
+			bip_op,
+			RTE_MULTI_FN_OP_STATUS_FAILURE,
+			RTE_CRYPTO_OP_STATUS_ERROR,
+			RTE_MULTI_FN_ERR_DETECT_OP_STATUS_ERROR);
+
+	} else if (op->overall_status ==
+				RTE_MULTI_FN_OP_STATUS_NOT_PROCESSED) {
+		switch (job->status) {
+		case STS_COMPLETED:
+			if (unlikely(job->hash_alg == IMB_AUTH_NULL))
+				break;
+
+			op_statuses_set(
+				op,
+				cipher_op,
+				crc_op,
+				bip_op,
+				RTE_MULTI_FN_OP_STATUS_SUCCESS,
+				RTE_CRYPTO_OP_STATUS_SUCCESS,
+				RTE_MULTI_FN_ERR_DETECT_OP_STATUS_SUCCESS);
+
+			if (job->hash_alg == IMB_AUTH_PON_CRC_BIP)
+				bip_copy(job, bip_op);
+
+			if (sess->err_detect.operation ==
+					RTE_MULTI_FN_ERR_DETECT_OP_VERIFY)
+				crc_verify(
+					job,
+					crc_op,
+					job->hash_alg == IMB_AUTH_PON_CRC_BIP ?
+						PON_AUTH_TAG_CRC_OFFSET : 0);
+
+			if (crc_op->op_status !=
+				RTE_MULTI_FN_ERR_DETECT_OP_STATUS_SUCCESS)
+				op->overall_status =
+					RTE_MULTI_FN_OP_STATUS_FAILURE;
+			break;
+		default:
+			op_statuses_set(
+				op,
+				cipher_op,
+				crc_op,
+				bip_op,
+				RTE_MULTI_FN_OP_STATUS_FAILURE,
+				RTE_CRYPTO_OP_STATUS_ERROR,
+				RTE_MULTI_FN_ERR_DETECT_OP_STATUS_ERROR);
+			break;
+		}
+	}
+
+	return op;
+}
+
+static unsigned
+completed_jobs_handle(struct aesni_mb_rawdev_qp *qp,
+		      JOB_AES_HMAC *job,
+		      struct rte_multi_fn_op **ops,
+		      uint16_t nb_ops)
+{
+	struct rte_multi_fn_op *op = NULL;
+	unsigned int processed_jobs = 0;
+
+	while (job != NULL) {
+		op = mb_job_post_process(job);
+
+		if (op) {
+			ops[processed_jobs++] = op;
+			qp->stats.dequeued_count++;
+		} else {
+			qp->stats.dequeue_err_count++;
+			break;
+		}
+		if (processed_jobs == nb_ops)
+			break;
+
+		job = IMB_GET_COMPLETED_JOB(qp->mb_mgr);
+	}
+
+	return processed_jobs;
+}
+
+static inline uint16_t
+mb_mgr_flush(struct aesni_mb_rawdev_qp *qp,
+	     struct rte_multi_fn_op **ops,
+	     uint16_t nb_ops)
+{
+	int processed_ops = 0;
+
+	/* Flush the remaining jobs */
+	JOB_AES_HMAC *job = IMB_FLUSH_JOB(qp->mb_mgr);
+
+	if (job)
+		processed_ops += completed_jobs_handle(qp,
+						       job,
+						       &ops[processed_ops],
+						       nb_ops - processed_ops);
+
+	return processed_ops;
+}
+
+static inline JOB_AES_HMAC *
+mb_job_params_null_set(JOB_AES_HMAC *job, struct rte_multi_fn_op *op)
+{
+	job->chain_order = IMB_ORDER_HASH_CIPHER;
+	job->cipher_mode = IMB_CIPHER_NULL;
+	job->hash_alg = IMB_AUTH_NULL;
+	job->cipher_direction = IMB_DIR_DECRYPT;
+
+	/* Set user data to be crypto operation data struct */
+	job->user_data = op;
+
+	return job;
+}
+
+static int
+aesni_mb_rawdev_pmd_config(const struct rte_rawdev *rawdev,
+			   rte_rawdev_obj_t config)
+{
+	struct aesni_mb_rawdev *aesni_mb_dev = rawdev->dev_private;
+	struct rte_multi_fn_dev_config *conf = config;
+
+	aesni_mb_dev->nb_queue_pairs = conf->nb_queues;
+
+	aesni_mb_dev->queue_pairs =
+			rte_zmalloc_socket(
+				"aesni_mb_rawdev_qps",
+				aesni_mb_dev->nb_queue_pairs *
+					sizeof(struct aesni_mb_rawdev_qp *),
+				RTE_CACHE_LINE_SIZE,
+				rawdev->socket_id);
+
+	if (aesni_mb_dev->queue_pairs == NULL) {
+		AESNI_MB_RAWDEV_ERR("Unable to allocate queue pairs");
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static void
+aesni_mb_rawdev_pmd_info_get(struct rte_rawdev *rawdev,
+			     rte_rawdev_obj_t dev_info)
+{
+	struct aesni_mb_rawdev *aesni_mb_dev = rawdev->dev_private;
+	struct rte_multi_fn_dev_info *info = dev_info;
+
+	if (info != NULL)
+		info->max_nb_queues = aesni_mb_dev->max_nb_queue_pairs;
+}
+
+static int
+aesni_mb_rawdev_pmd_start(__rte_unused struct rte_rawdev *rawdev)
+{
+	return 0;
+}
+
+static void
+aesni_mb_rawdev_pmd_stop(__rte_unused struct rte_rawdev *rawdev)
+{
+}
+
+static int
+aesni_mb_rawdev_pmd_close(struct rte_rawdev *rawdev)
+{
+	struct aesni_mb_rawdev *aesni_mb_dev = rawdev->dev_private;
+
+	if (aesni_mb_dev->queue_pairs != NULL)
+		rte_free(aesni_mb_dev->queue_pairs);
+
+	return 0;
+}
+
+static int
+aesni_mb_rawdev_pmd_qp_release(struct rte_rawdev *rawdev, uint16_t qp_id)
+{
+	struct aesni_mb_rawdev *aesni_mb_dev = rawdev->dev_private;
+	struct aesni_mb_rawdev_qp *qp = aesni_mb_dev->queue_pairs[qp_id];
+	struct rte_ring *r = NULL;
+
+	if (qp != NULL) {
+		r = rte_ring_lookup(qp->name);
+		if (r)
+			rte_ring_free(r);
+		if (qp->mb_mgr)
+			free_mb_mgr(qp->mb_mgr);
+		rte_free(qp);
+		aesni_mb_dev->queue_pairs[qp_id] = NULL;
+	}
+
+	return 0;
+}
+
+static int
+aesni_mb_rawdev_pmd_qp_setup(struct rte_rawdev *rawdev,
+			     uint16_t qp_id,
+			     rte_rawdev_obj_t qp_c)
+{
+	struct aesni_mb_rawdev *aesni_mb_dev = rawdev->dev_private;
+	struct aesni_mb_rawdev_qp *qp = NULL;
+	const struct rte_multi_fn_qp_config *qp_conf =
+			(const struct rte_multi_fn_qp_config *)qp_c;
+	int ret = -1;
+
+	if (qp_id >= aesni_mb_dev->max_nb_queue_pairs) {
+		AESNI_MB_RAWDEV_ERR("Invalid queue pair id=%d", qp_id);
+		return -EINVAL;
+	}
+
+	/* Free memory prior to re-allocation if needed */
+	if (aesni_mb_dev->queue_pairs[qp_id] != NULL)
+		aesni_mb_rawdev_pmd_qp_release(rawdev, qp_id);
+
+	/* Allocate the queue pair data structure */
+	qp = rte_zmalloc_socket("aesni_mb_rawdev_qp",
+				sizeof(struct aesni_mb_rawdev_qp),
+				RTE_CACHE_LINE_SIZE,
+				rawdev->socket_id);
+	if (qp == NULL)
+		return -ENOMEM;
+
+	qp->id = qp_id;
+	aesni_mb_dev->queue_pairs[qp_id] = qp;
+
+	if (qp_unique_name_set(rawdev, qp))
+		goto qp_setup_cleanup;
+
+	qp->mb_mgr = alloc_mb_mgr(0);
+	if (qp->mb_mgr == NULL) {
+		ret = -ENOMEM;
+		goto qp_setup_cleanup;
+	}
+
+	switch (aesni_mb_dev->vector_mode) {
+	case AESNI_MB_RAWDEV_SSE:
+		init_mb_mgr_sse(qp->mb_mgr);
+		break;
+	case AESNI_MB_RAWDEV_AVX:
+		init_mb_mgr_avx(qp->mb_mgr);
+		break;
+	case AESNI_MB_RAWDEV_AVX2:
+		init_mb_mgr_avx2(qp->mb_mgr);
+		break;
+	case AESNI_MB_RAWDEV_AVX512:
+		init_mb_mgr_avx512(qp->mb_mgr);
+		break;
+	default:
+		AESNI_MB_RAWDEV_ERR("Unsupported vector mode %u",
+				    aesni_mb_dev->vector_mode);
+		goto qp_setup_cleanup;
+	}
+
+	qp->ingress_queue = qp_processed_ops_ring_create(
+						qp,
+						qp_conf->nb_descriptors,
+						rawdev->socket_id);
+	if (qp->ingress_queue == NULL) {
+		ret = -1;
+		goto qp_setup_cleanup;
+	}
+
+	memset(&qp->stats, 0, sizeof(qp->stats));
+
+	return 0;
+
+qp_setup_cleanup:
+	if (qp) {
+		if (qp->mb_mgr)
+			free_mb_mgr(qp->mb_mgr);
+		rte_free(qp);
+	}
+
+	return ret;
+}
+
+static uint16_t
+aesni_mb_rawdev_pmd_qp_count(struct rte_rawdev *rawdev)
+{
+	struct aesni_mb_rawdev *aesni_mb_dev = rawdev->dev_private;
+
+	return aesni_mb_dev->nb_queue_pairs;
+}
+
+static int
+aesni_mb_rawdev_pmd_enq(struct rte_rawdev *rawdev,
+			struct rte_rawdev_buf **ops,
+			unsigned int nb_ops,
+			rte_rawdev_obj_t q_id)
+{
+	struct aesni_mb_rawdev *aesni_mb_dev = rawdev->dev_private;
+	struct aesni_mb_rawdev_qp *qp;
+	unsigned int nb_enqueued;
+
+	qp = aesni_mb_dev->queue_pairs[*(uint16_t *)q_id];
+
+	nb_enqueued = rte_ring_enqueue_burst(qp->ingress_queue,
+					     (void **)ops,
+					     nb_ops,
+					     NULL);
+
+	qp->stats.enqueued_count += nb_enqueued;
+
+	return nb_enqueued;
+}
+
+static int
+aesni_mb_rawdev_pmd_deq(struct rte_rawdev *rawdev,
+			struct rte_rawdev_buf **ops,
+			unsigned int nb_ops,
+			rte_rawdev_obj_t q_id)
+{
+	struct aesni_mb_rawdev *aesni_mb_dev = rawdev->dev_private;
+	struct aesni_mb_rawdev_qp *qp;
+	struct rte_multi_fn_op *op;
+	JOB_AES_HMAC *job;
+	uint8_t output_idx;
+	unsigned int processed_jobs = 0;
+	int ret;
+
+	qp = aesni_mb_dev->queue_pairs[*(uint16_t *)q_id];
+
+	if (unlikely(nb_ops == 0))
+		return 0;
+
+	output_idx = qp->output_idx;
+
+	do {
+		/* Get next free mb job struct from mb manager */
+		job = IMB_GET_NEXT_JOB(qp->mb_mgr);
+		if (unlikely(job == NULL)) {
+			/* if no free mb job structs we need to flush mb_mgr */
+			processed_jobs += mb_mgr_flush(
+						qp,
+						(struct rte_multi_fn_op **)
+							&ops[processed_jobs],
+						nb_ops - processed_jobs);
+
+			if (nb_ops == processed_jobs)
+				break;
+
+			job = IMB_GET_NEXT_JOB(qp->mb_mgr);
+		}
+
+		/*
+		 * Get next operation to process from ingress queue.
+		 * There is no need to return the job to the MB_MGR if there
+		 * are no more operations to process, since the MB_MGR can use
+		 * that pointer again in next get_next calls.
+		 */
+		ret = rte_ring_dequeue(qp->ingress_queue, (void **)&op);
+		if (ret < 0)
+			break;
+
+		ret = mb_job_params_set(job, qp, op, &output_idx);
+		if (unlikely(ret != 0)) {
+			qp->stats.dequeue_err_count++;
+			mb_job_params_null_set(job, op);
+		}
+
+		/* Submit job to multi-buffer for processing */
+#ifdef RTE_LIBRTE_PMD_AESNI_MB_RAWDEV_DEBUG
+		job = IMB_SUBMIT_JOB(qp->mb_mgr);
+#else
+		job = IMB_SUBMIT_JOB_NOCHECK(qp->mb_mgr);
+#endif
+		/*
+		 * If submit returns a processed job then handle it,
+		 * before submitting subsequent jobs
+		 */
+		if (job)
+			processed_jobs += completed_jobs_handle(
+						qp,
+						job,
+						(struct rte_multi_fn_op **)
+							&ops[processed_jobs],
+						nb_ops - processed_jobs);
+
+	} while (processed_jobs < nb_ops);
+
+	qp->output_idx = output_idx;
+
+	if (processed_jobs < 1)
+		processed_jobs += mb_mgr_flush(qp,
+					       (struct rte_multi_fn_op **)
+							&ops[processed_jobs],
+					       nb_ops - processed_jobs);
+
+	return processed_jobs;
+}
+
+static int
+aesni_mb_rawdev_pmd_xstats_get(const struct rte_rawdev *rawdev,
+			       const unsigned int ids[],
+			       uint64_t values[],
+			       unsigned int n)
+{
+	struct aesni_mb_rawdev *aesni_mb_dev = rawdev->dev_private;
+	struct aesni_mb_rawdev_qp *qp;
+	struct aesni_mb_rawdev_stats stats = {0};
+	int qp_id;
+	unsigned int i;
+
+	for (qp_id = 0; qp_id < aesni_mb_dev->nb_queue_pairs; qp_id++) {
+		qp = aesni_mb_dev->queue_pairs[qp_id];
+
+		stats.enqueued_count += qp->stats.enqueued_count;
+		stats.dequeued_count += qp->stats.dequeued_count;
+
+		stats.enqueue_err_count += qp->stats.enqueue_err_count;
+		stats.dequeue_err_count += qp->stats.dequeue_err_count;
+	}
+
+	for (i = 0; i < n; i++) {
+		switch (ids[i]) {
+		case 0:
+			values[i] = stats.enqueued_count;
+			break;
+		case 1:
+			values[i] = stats.dequeued_count;
+			break;
+		case 2:
+			values[i] = stats.enqueue_err_count;
+			break;
+		case 3:
+			values[i] = stats.dequeue_err_count;
+			break;
+		default:
+			values[i] = 0;
+			break;
+		}
+	}
+
+	return n;
+}
+
+static int
+aesni_mb_rawdev_pmd_xstats_get_names(
+				__rte_unused const struct rte_rawdev *rawdev,
+				struct rte_rawdev_xstats_name *names,
+				unsigned int size)
+{
+	unsigned int i;
+
+	if (size < RTE_DIM(xstat_names))
+		return RTE_DIM(xstat_names);
+
+	for (i = 0; i < RTE_DIM(xstat_names); i++)
+		strlcpy(names[i].name, xstat_names[i], sizeof(names[i]));
+
+	return RTE_DIM(xstat_names);
+}
+
+static int
+aesni_mb_rawdev_pmd_xstats_reset(struct rte_rawdev *rawdev,
+				 const uint32_t *ids,
+				 uint32_t nb_ids)
+{
+	struct aesni_mb_rawdev *aesni_mb_dev = rawdev->dev_private;
+	struct aesni_mb_rawdev_qp *qp;
+	unsigned int i;
+	int qp_id;
+
+	if (!ids) {
+		for (qp_id = 0; qp_id < aesni_mb_dev->nb_queue_pairs; qp_id++) {
+			qp = aesni_mb_dev->queue_pairs[qp_id];
+			qp->stats.enqueued_count = 0;
+			qp->stats.dequeued_count = 0;
+			qp->stats.enqueue_err_count = 0;
+			qp->stats.dequeue_err_count = 0;
+		}
+
+		return 0;
+	}
+
+	for (i = 0; i < nb_ids; i++) {
+		switch (ids[i]) {
+		case 0:
+			for (qp_id = 0;
+			     qp_id < aesni_mb_dev->nb_queue_pairs;
+			     qp_id++) {
+				qp = aesni_mb_dev->queue_pairs[qp_id];
+				qp->stats.enqueued_count = 0;
+			}
+			break;
+		case 1:
+			for (qp_id = 0;
+			     qp_id < aesni_mb_dev->nb_queue_pairs;
+			     qp_id++) {
+				qp = aesni_mb_dev->queue_pairs[qp_id];
+				qp->stats.dequeued_count = 0;
+			}
+			break;
+		case 2:
+			for (qp_id = 0;
+			     qp_id < aesni_mb_dev->nb_queue_pairs;
+			     qp_id++) {
+				qp = aesni_mb_dev->queue_pairs[qp_id];
+				qp->stats.enqueue_err_count = 0;
+			}
+			break;
+		case 3:
+			for (qp_id = 0;
+			     qp_id < aesni_mb_dev->nb_queue_pairs;
+			     qp_id++) {
+				qp = aesni_mb_dev->queue_pairs[qp_id];
+				qp->stats.dequeue_err_count = 0;
+			}
+			break;
+		default:
+			AESNI_MB_RAWDEV_ERR("Invalid xstat id - cannot reset");
+			break;
+		}
+	}
+
+	return 0;
+}
+
+static int
+aesni_mb_rawdev_pmd_selftest(uint16_t dev_id)
+{
+	return aesni_mb_rawdev_test(dev_id);
+}
+
+static struct rte_multi_fn_session *
+aesni_mb_rawdev_pmd_session_create(struct rte_rawdev *rawdev,
+				   struct rte_multi_fn_xform *xform,
+				   int socket_id)
+{
+	struct aesni_mb_rawdev *aesni_mb_dev = rawdev->dev_private;
+	struct aesni_mb_rawdev_session *aesni_sess = NULL;
+	struct rte_multi_fn_session *session;
+	struct rte_crypto_sym_xform *cipher_xform;
+	enum aesni_mb_rawdev_op op;
+	int ret;
+
+	op = session_support_check(xform);
+
+	/* Allocate multi-function session */
+	session = rte_zmalloc_socket("multi_fn_session",
+				     sizeof(struct rte_multi_fn_session),
+				     RTE_CACHE_LINE_MIN_SIZE,
+				     socket_id);
+
+	if (session == NULL) {
+		AESNI_MB_RAWDEV_ERR("Multi-function session allocation failed");
+		return NULL;
+	}
+
+	/* Allocate AESNI-MB_rawdev session */
+	aesni_sess = rte_zmalloc_socket("aesni_mb_rawdev_session",
+					sizeof(struct aesni_mb_rawdev_session),
+					RTE_CACHE_LINE_MIN_SIZE,
+					socket_id);
+
+	if (aesni_sess == NULL) {
+		AESNI_MB_RAWDEV_ERR(
+				"AESNI-MB rawdev session allocation failed");
+		return NULL;
+	}
+
+	session->sess_private_data = aesni_sess;
+	aesni_sess->op = op;
+
+	switch (op) {
+	case AESNI_MB_RAWDEV_OP_DOCSIS_CRC_CRYPTO:
+	case AESNI_MB_RAWDEV_OP_PON_CRC_CRYPTO_BIP:
+		aesni_sess->chain_order = IMB_ORDER_HASH_CIPHER;
+		cipher_xform = &xform->next->crypto_sym;
+		break;
+	case AESNI_MB_RAWDEV_OP_DOCSIS_CRYPTO_CRC:
+		aesni_sess->chain_order = IMB_ORDER_CIPHER_HASH;
+		cipher_xform = &xform->crypto_sym;
+		break;
+	case AESNI_MB_RAWDEV_OP_PON_BIP_CRYPTO_CRC:
+		aesni_sess->chain_order = IMB_ORDER_CIPHER_HASH;
+		cipher_xform = &xform->next->crypto_sym;
+		break;
+	default:
+		AESNI_MB_RAWDEV_ERR("Unsupported multi-function xform chain");
+		return NULL;
+	}
+
+	ret = session_err_detect_parameters_set(aesni_sess);
+
+	if (ret != 0) {
+		AESNI_MB_RAWDEV_ERR(
+				"Invalid/unsupported error detect parameters");
+		return NULL;
+	}
+
+	ret = session_cipher_parameters_set(aesni_mb_dev->mb_mgr,
+					    aesni_sess,
+					    cipher_xform);
+
+	if (ret != 0) {
+		AESNI_MB_RAWDEV_ERR("Invalid/unsupported cipher parameters");
+		return NULL;
+	}
+
+	return session;
+}
+
+static int
+aesni_mb_rawdev_pmd_session_destroy(__rte_unused struct rte_rawdev *rawdev,
+				    struct rte_multi_fn_session *sess)
+{
+
+	if (sess) {
+		if (sess->sess_private_data)
+			rte_free(sess->sess_private_data);
+		rte_free(sess);
+	}
+
+	return 0;
+}
+
+static const struct rte_rawdev_ops aesni_mb_rawdev_ops = {
+	.dev_configure = aesni_mb_rawdev_pmd_config,
+	.dev_info_get = aesni_mb_rawdev_pmd_info_get,
+	.dev_start = aesni_mb_rawdev_pmd_start,
+	.dev_stop = aesni_mb_rawdev_pmd_stop,
+	.dev_close = aesni_mb_rawdev_pmd_close,
+	.queue_setup = aesni_mb_rawdev_pmd_qp_setup,
+	.queue_release = aesni_mb_rawdev_pmd_qp_release,
+	.queue_count = aesni_mb_rawdev_pmd_qp_count,
+	.enqueue_bufs = aesni_mb_rawdev_pmd_enq,
+	.dequeue_bufs = aesni_mb_rawdev_pmd_deq,
+	.xstats_get = aesni_mb_rawdev_pmd_xstats_get,
+	.xstats_get_names = aesni_mb_rawdev_pmd_xstats_get_names,
+	.xstats_reset = aesni_mb_rawdev_pmd_xstats_reset,
+	.dev_selftest = aesni_mb_rawdev_pmd_selftest,
+};
+
+static const struct rte_multi_fn_ops mf_ops = {
+	.session_create = aesni_mb_rawdev_pmd_session_create,
+	.session_destroy = aesni_mb_rawdev_pmd_session_destroy,
+};
+
+static int
+aesni_mb_rawdev_create(const char *name,
+		       struct rte_vdev_device *vdev,
+		       unsigned int socket_id)
+{
+	struct rte_rawdev *rawdev;
+	struct aesni_mb_rawdev *aesni_mb_dev;
+	enum aesni_mb_rawdev_vector_mode vector_mode;
+	MB_MGR *mb_mgr;
+
+	/* Allocate device structure */
+	rawdev = rte_rawdev_pmd_allocate(name,
+					 sizeof(struct aesni_mb_rawdev),
+					 socket_id);
+	if (!rawdev) {
+		AESNI_MB_RAWDEV_ERR("Unable to allocate raw device");
+		return -EINVAL;
+	}
+
+	rawdev->dev_ops = &aesni_mb_rawdev_ops;
+	rawdev->device = &vdev->device;
+	rawdev->driver_name = driver_name;
+
+	/* Check CPU for supported vector instruction set */
+	if (rte_cpu_get_flag_enabled(RTE_CPUFLAG_AVX512F))
+		vector_mode = AESNI_MB_RAWDEV_AVX512;
+	else if (rte_cpu_get_flag_enabled(RTE_CPUFLAG_AVX2))
+		vector_mode = AESNI_MB_RAWDEV_AVX2;
+	else if (rte_cpu_get_flag_enabled(RTE_CPUFLAG_AVX))
+		vector_mode = AESNI_MB_RAWDEV_AVX;
+	else
+		vector_mode = AESNI_MB_RAWDEV_SSE;
+
+	/* Check CPU for support for AES instruction set */
+	if (!rte_cpu_get_flag_enabled(RTE_CPUFLAG_AES))
+		AESNI_MB_RAWDEV_WARN("AES instructions not supported by CPU");
+
+	mb_mgr = alloc_mb_mgr(0);
+
+	if (mb_mgr == NULL)
+		return -ENOMEM;
+
+	switch (vector_mode) {
+	case AESNI_MB_RAWDEV_SSE:
+		init_mb_mgr_sse(mb_mgr);
+		break;
+	case AESNI_MB_RAWDEV_AVX:
+		init_mb_mgr_avx(mb_mgr);
+		break;
+	case AESNI_MB_RAWDEV_AVX2:
+		init_mb_mgr_avx2(mb_mgr);
+		break;
+	case AESNI_MB_RAWDEV_AVX512:
+		init_mb_mgr_avx512(mb_mgr);
+		break;
+	default:
+		AESNI_MB_RAWDEV_ERR("Unsupported vector mode %u",
+				    vector_mode);
+		free_mb_mgr(mb_mgr);
+		mb_mgr = NULL;
+		break;
+	}
+
+	if (mb_mgr == NULL) {
+		rte_rawdev_pmd_release(rawdev);
+		return -1;
+	}
+
+	/* Set the device's private data */
+	aesni_mb_dev = rawdev->dev_private;
+	aesni_mb_dev->mf_ops = &mf_ops;
+	aesni_mb_dev->vector_mode = vector_mode;
+	aesni_mb_dev->max_nb_queue_pairs = MAX_QUEUES;
+	aesni_mb_dev->mb_mgr = mb_mgr;
+
+	AESNI_MB_RAWDEV_INFO("IPSec Multi-buffer library version used: %s",
+			     imb_get_version_str());
+
+	return 0;
+}
+
+static int
+aesni_mb_rawdev_destroy(const char *name)
+{
+	struct rte_rawdev *rawdev;
+	struct aesni_mb_rawdev *aesni_mb_dev;
+	int ret;
+
+	rawdev = rte_rawdev_pmd_get_named_dev(name);
+	if (rawdev == NULL) {
+		AESNI_MB_RAWDEV_ERR("Invalid device name (%s)", name);
+		return -EINVAL;
+	}
+
+	aesni_mb_dev = rawdev->dev_private;
+	free_mb_mgr(aesni_mb_dev->mb_mgr);
+
+	ret = rte_rawdev_pmd_release(rawdev);
+	if (ret)
+		AESNI_MB_RAWDEV_DEBUG("Device cleanup failed");
+
+	return 0;
+}
+
+static int
+aesni_mb_rawdev_probe(struct rte_vdev_device *vdev)
+{
+	const char *name;
+
+	name = rte_vdev_device_name(vdev);
+	if (name == NULL)
+		return -EINVAL;
+
+	AESNI_MB_RAWDEV_INFO("Init %s on NUMA node %d", name, rte_socket_id());
+
+	return aesni_mb_rawdev_create(name, vdev, rte_socket_id());
+}
+
+static int
+aesni_mb_rawdev_remove(struct rte_vdev_device *vdev)
+{
+	const char *name;
+
+	name = rte_vdev_device_name(vdev);
+	if (name == NULL)
+		return -1;
+
+	AESNI_MB_RAWDEV_INFO("Closing %s on NUMA node %d",
+			     name,
+			     rte_socket_id());
+
+	return aesni_mb_rawdev_destroy(name);
+}
+
+static struct rte_vdev_driver rawdev_aesni_mb_pmd_drv = {
+	.probe = aesni_mb_rawdev_probe,
+	.remove = aesni_mb_rawdev_remove
+};
+
+RTE_PMD_REGISTER_VDEV(rawdev_aesni_mb, rawdev_aesni_mb_pmd_drv);
+
+RTE_INIT(aesni_mb_raw_init_log)
+{
+	aesni_mb_rawdev_pmd_logtype = rte_log_register("rawdev.aesni_mb");
+	if (aesni_mb_rawdev_pmd_logtype >= 0)
+		rte_log_set_level(aesni_mb_rawdev_pmd_logtype, RTE_LOG_INFO);
+}
diff --git a/drivers/raw/aesni_mb/aesni_mb_rawdev.h b/drivers/raw/aesni_mb/aesni_mb_rawdev.h
new file mode 100644
index 000000000..59d78b8d8
--- /dev/null
+++ b/drivers/raw/aesni_mb/aesni_mb_rawdev.h
@@ -0,0 +1,112 @@ 
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Intel Corporation.
+ */
+
+#ifndef _AESNI_MB_RAWDEV_H_
+#define _AESNI_MB_RAWDEV_H_
+
+#include <intel-ipsec-mb.h>
+#include <rte_rawdev.h>
+#include <rte_multi_fn.h>
+#include <rte_multi_fn_driver.h>
+
+/* AESNI-MB Rawdev PMD logtype */
+int aesni_mb_rawdev_pmd_logtype;
+
+#define AESNI_MB_RAWDEV_LOG(level, fmt, args...)  \
+	rte_log(RTE_LOG_ ## level, aesni_mb_rawdev_pmd_logtype,  \
+		"%s() line %u: " fmt "\n", \
+		__func__, __LINE__, ##args)
+#define AESNI_MB_RAWDEV_DEBUG(fmt, args...) \
+	AESNI_MB_RAWDEV_LOG(DEBUG, fmt, ## args)
+#define AESNI_MB_RAWDEV_INFO(fmt, args...) \
+	AESNI_MB_RAWDEV_LOG(INFO, fmt, ## args)
+#define AESNI_MB_RAWDEV_ERR(fmt, args...) \
+	AESNI_MB_RAWDEV_LOG(ERR, fmt, ## args)
+#define AESNI_MB_RAWDEV_WARN(fmt, args...) \
+	AESNI_MB_RAWDEV_LOG(WARNING, fmt, ## args)
+
+
+/* Maximum length for output */
+#define OUTPUT_LENGTH_MAX 8
+
+/* AESNI-MB supported operations */
+enum aesni_mb_rawdev_op {
+	AESNI_MB_RAWDEV_OP_DOCSIS_CRC_CRYPTO,  /* DOCSIS encrypt direction */
+	AESNI_MB_RAWDEV_OP_DOCSIS_CRYPTO_CRC,  /* DOCSIS decrypt direction */
+	AESNI_MB_RAWDEV_OP_PON_CRC_CRYPTO_BIP, /* PON encrypt direction */
+	AESNI_MB_RAWDEV_OP_PON_BIP_CRYPTO_CRC, /* PON decrypt direction */
+	AESNI_MB_RAWDEV_OP_NOT_SUPPORTED
+};
+
+/* AESNI-MB device statistics */
+struct aesni_mb_rawdev_stats {
+	uint64_t enqueued_count;
+	uint64_t dequeued_count;
+	uint64_t enqueue_err_count;
+	uint64_t dequeue_err_count;
+};
+
+/* AESNI-MB queue pair */
+struct aesni_mb_rawdev_qp {
+	uint16_t id;
+	char name[RTE_RAWDEV_NAME_MAX_LEN];
+	MB_MGR *mb_mgr;
+	struct rte_ring *ingress_queue;
+	struct aesni_mb_rawdev_stats stats;
+	uint8_t output_idx;
+	uint8_t temp_outputs[MAX_JOBS][OUTPUT_LENGTH_MAX];
+} __rte_cache_aligned;
+
+/* AESNI-MB vector modes */
+enum aesni_mb_rawdev_vector_mode {
+	AESNI_MB_RAWDEV_NOT_SUPPORTED = 0,
+	AESNI_MB_RAWDEV_SSE,
+	AESNI_MB_RAWDEV_AVX,
+	AESNI_MB_RAWDEV_AVX2,
+	AESNI_MB_RAWDEV_AVX512
+};
+
+/* AESNI-MB device data */
+struct aesni_mb_rawdev {
+	const struct rte_multi_fn_ops *mf_ops; /* MUST be first */
+	MB_MGR *mb_mgr;
+	struct aesni_mb_rawdev_qp **queue_pairs;
+	enum aesni_mb_rawdev_vector_mode vector_mode;
+	uint16_t max_nb_queue_pairs;
+	uint16_t nb_queue_pairs;
+};
+
+/* AESNI-MB private session structure */
+struct aesni_mb_rawdev_session {
+	enum aesni_mb_rawdev_op op;
+	JOB_CHAIN_ORDER chain_order;
+	struct {
+		uint16_t length;
+		uint16_t offset;
+	} iv;
+	struct {
+		JOB_CIPHER_DIRECTION direction;
+		JOB_CIPHER_MODE mode;
+
+		uint64_t key_length_in_bytes;
+
+		union {
+			struct {
+				uint32_t encode[60] __rte_aligned(16);
+				uint32_t decode[60] __rte_aligned(16);
+			} expanded_aes_keys;
+		};
+	} cipher;
+	struct {
+		JOB_HASH_ALG algo;
+		enum rte_multi_fn_err_detect_operation operation;
+		uint16_t gen_output_len;
+
+	} err_detect;
+} __rte_cache_aligned;
+
+int
+aesni_mb_rawdev_test(uint16_t dev_id);
+
+#endif /* _AESNI_MB_RAWDEV_H_ */
diff --git a/drivers/raw/aesni_mb/aesni_mb_rawdev_test.c b/drivers/raw/aesni_mb/aesni_mb_rawdev_test.c
new file mode 100644
index 000000000..a8051cc80
--- /dev/null
+++ b/drivers/raw/aesni_mb/aesni_mb_rawdev_test.c
@@ -0,0 +1,1102 @@ 
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Intel Corporation.
+ */
+
+#include <rte_common.h>
+#include <rte_mbuf.h>
+#include <rte_malloc.h>
+#include <rte_memcpy.h>
+#include <rte_dev.h>
+#include <rte_bus_vdev.h>
+#include <rte_rawdev.h>
+#include <rte_multi_fn.h>
+#include <rte_ether.h>
+#include <rte_test.h>
+
+#include "aesni_mb_rawdev.h"
+#include "aesni_mb_rawdev_test_vectors.h"
+
+#define TEST_DEV_NAME "rawdev_aesni_mb"
+
+#define TEST(setup, teardown, run, data, suffix) \
+	test_run(setup, teardown, run, data, RTE_STR(run)"_"suffix)
+
+#define TEST_SUCCESS 0
+#define TEST_FAILED  -1
+
+#define QP_NB_DESC (4096)
+
+#define MBUF_POOL_NAME        "aesni_mb_rawdev_mbuf_pool"
+#define MBUF_POOL_SIZE        (8191)
+#define MBUF_CACHE_SIZE       (256)
+#define MBUF_DATAPAYLOAD_SIZE (2048)
+#define MBUF_SIZE             (sizeof(struct rte_mbuf) + \
+				RTE_PKTMBUF_HEADROOM + MBUF_DATAPAYLOAD_SIZE)
+
+#define OP_POOL_NAME  "aesni_mb_rawdev_op_pool"
+#define OP_POOL_SIZE  (8191)
+#define OP_PRIV_SIZE  (16)
+#define OP_CACHE_SIZE (256)
+
+#define MAX_OPS (3)
+
+static int eal_log_level;
+
+struct testsuite_params {
+	uint16_t dev_id;
+	struct rte_mempool *mbuf_pool;
+	struct rte_mempool *op_pool;
+};
+
+struct unittest_params {
+	struct rte_multi_fn_session *sess;
+	struct rte_multi_fn_op *ops[MAX_OPS];
+	struct rte_mbuf *ibuf;
+	struct rte_mbuf *obuf;
+};
+
+static struct testsuite_params testsuite_params;
+static struct unittest_params unittest_params;
+
+static int total;
+static int passed;
+static int failed;
+static int unsupported;
+
+static int
+testsuite_setup(uint16_t dev_id)
+{
+	struct testsuite_params *ts_params = &testsuite_params;
+	uint8_t count = rte_rawdev_count();
+
+	eal_log_level = rte_log_get_level(RTE_LOGTYPE_EAL);
+	rte_log_set_level(RTE_LOGTYPE_EAL, RTE_LOG_DEBUG);
+
+	memset(ts_params, 0, sizeof(*ts_params));
+
+	if (!count) {
+		AESNI_MB_RAWDEV_INFO("No existing rawdev found - creating %s",
+				     TEST_DEV_NAME);
+		return rte_vdev_init(TEST_DEV_NAME, NULL);
+	}
+
+	ts_params->dev_id = dev_id;
+
+	ts_params->mbuf_pool = rte_mempool_lookup(MBUF_POOL_NAME);
+	if (ts_params->mbuf_pool == NULL) {
+		/* Not already created so create */
+		ts_params->mbuf_pool = rte_pktmbuf_pool_create(
+						MBUF_POOL_NAME,
+						MBUF_POOL_SIZE,
+						MBUF_CACHE_SIZE,
+						0,
+						MBUF_SIZE,
+						rte_socket_id());
+		if (ts_params->mbuf_pool == NULL) {
+			AESNI_MB_RAWDEV_ERR("Cannot create AESNI-MB rawdev "
+					    "mbuf pool");
+			return TEST_FAILED;
+		}
+	}
+
+	ts_params->op_pool = rte_multi_fn_op_pool_create(OP_POOL_NAME,
+							  OP_POOL_SIZE,
+							  OP_CACHE_SIZE,
+							  OP_PRIV_SIZE,
+							  rte_socket_id());
+
+	if (ts_params->op_pool == NULL) {
+		AESNI_MB_RAWDEV_ERR("Cannot create AESNI-MB rawdev operation "
+				     "pool");
+		return TEST_FAILED;
+	}
+
+	return TEST_SUCCESS;
+}
+
+static void
+testsuite_teardown(void)
+{
+	struct testsuite_params *ts_params = &testsuite_params;
+
+	if (ts_params->mbuf_pool != NULL) {
+		rte_mempool_free(ts_params->mbuf_pool);
+		ts_params->mbuf_pool = NULL;
+	}
+
+	if (ts_params->op_pool != NULL) {
+		rte_mempool_free(ts_params->op_pool);
+		ts_params->op_pool = NULL;
+	}
+
+	rte_vdev_uninit(TEST_DEV_NAME);
+
+	rte_log_set_level(RTE_LOGTYPE_EAL, eal_log_level);
+}
+
+static int
+test_setup(void)
+{
+	struct testsuite_params *ts_params = &testsuite_params;
+	struct unittest_params *ut_params = &unittest_params;
+
+	struct rte_rawdev_info info = {0};
+	struct rte_multi_fn_dev_config mf_dev_conf = {0};
+	struct rte_multi_fn_qp_config qp_conf = {0};
+	uint16_t qp_id;
+	int ret;
+
+	/* Clear unit test parameters before running test */
+	memset(ut_params, 0, sizeof(*ut_params));
+
+	/* Configure device and queue pairs */
+	mf_dev_conf.nb_queues = 1;
+	info.dev_private = &mf_dev_conf;
+	qp_conf.nb_descriptors = QP_NB_DESC;
+
+	ret = rte_rawdev_configure(ts_params->dev_id, &info);
+	RTE_TEST_ASSERT_SUCCESS(ret,
+				"Failed to configure rawdev %u",
+				ts_params->dev_id);
+
+	for (qp_id = 0; qp_id < mf_dev_conf.nb_queues; qp_id++) {
+		ret = rte_rawdev_queue_setup(ts_params->dev_id,
+					     qp_id,
+					     &qp_conf);
+		RTE_TEST_ASSERT_SUCCESS(ret,
+					"Failed to setup queue pair %u on "
+					"rawdev %u",
+					qp_id,
+					ts_params->dev_id);
+	}
+
+	ret = rte_rawdev_xstats_reset(ts_params->dev_id, NULL, 0);
+	RTE_TEST_ASSERT_SUCCESS(ret,
+				"Failed to reset stats on rawdev %u",
+				ts_params->dev_id);
+
+	/* Start the device */
+	ret = rte_rawdev_start(ts_params->dev_id);
+	RTE_TEST_ASSERT_SUCCESS(ret,
+				"Failed to start rawdev %u",
+				ts_params->dev_id);
+
+	return 0;
+}
+
+static void
+test_teardown(void)
+{
+	struct testsuite_params *ts_params = &testsuite_params;
+	struct unittest_params *ut_params = &unittest_params;
+
+	int i;
+
+	/* Free multi-function operations */
+	for (i = 0; i < MAX_OPS; i++) {
+		if (ut_params->ops[i] != NULL) {
+			rte_multi_fn_op_free(ut_params->ops[i]);
+			ut_params->ops[i] = NULL;
+		}
+	}
+
+	/* Free multi-function session */
+	if (ut_params->sess != NULL) {
+		rte_multi_fn_session_destroy(ts_params->dev_id,
+					     ut_params->sess);
+		ut_params->sess = NULL;
+	}
+
+	/*
+	 * Free mbuf - both obuf and ibuf are usually the same,
+	 * so check if they point at the same address is necessary,
+	 * to avoid freeing the mbuf twice.
+	 */
+	if (ut_params->obuf != NULL) {
+		rte_pktmbuf_free(ut_params->obuf);
+		if (ut_params->ibuf == ut_params->obuf)
+			ut_params->ibuf = NULL;
+		ut_params->obuf = NULL;
+	}
+	if (ut_params->ibuf != NULL) {
+		rte_pktmbuf_free(ut_params->ibuf);
+		ut_params->ibuf = NULL;
+	}
+
+	/* Stop the device */
+	rte_rawdev_stop(ts_params->dev_id);
+}
+
+static int
+test_docsis_encrypt(void *vtdata)
+{
+	struct docsis_test_data *tdata = (struct docsis_test_data *)vtdata;
+	struct testsuite_params *ts_params = &testsuite_params;
+	struct unittest_params *ut_params = &unittest_params;
+
+	/* Xforms */
+	struct rte_multi_fn_xform xform1 = {0};
+	struct rte_multi_fn_xform xform2 = {0};
+	struct rte_crypto_cipher_xform *xform_cipher;
+
+	/* Operations */
+	struct rte_multi_fn_op *result;
+	struct rte_crypto_sym_op *cipher_op;
+	struct rte_multi_fn_err_detect_op *crc_op;
+
+	/* Cipher params */
+	int cipher_len = 0;
+	uint8_t *iv_ptr;
+
+	/* CRC params */
+	int crc_len = 0, crc_data_len = 0;
+
+	/* Test data */
+	uint8_t *plaintext = NULL, *ciphertext = NULL;
+
+	/* Stats */
+	uint64_t stats[4] = {0};
+	struct rte_rawdev_xstats_name stats_names[4] = {0};
+	const unsigned int stats_id[4] = {0, 1, 2, 3};
+	int num_stats = 0, num_names = 0;
+
+	uint16_t qp_id = 0, nb_enq, nb_deq = 0, nb_ops;
+	int i, ret = TEST_SUCCESS;
+
+	/* Setup source mbuf */
+	ut_params->ibuf = rte_pktmbuf_alloc(ts_params->mbuf_pool);
+	RTE_TEST_ASSERT_NOT_NULL(ut_params->ibuf,
+				 "Failed to allocate source mbuf");
+	memset(rte_pktmbuf_mtod(ut_params->ibuf, uint8_t *),
+	       0,
+	       rte_pktmbuf_tailroom(ut_params->ibuf));
+	plaintext = (uint8_t *)rte_pktmbuf_append(ut_params->ibuf,
+						  tdata->plaintext.len);
+	memcpy(plaintext, tdata->plaintext.data, tdata->plaintext.len);
+
+	/* Create session */
+	xform1.type = RTE_MULTI_FN_XFORM_TYPE_ERR_DETECT;
+	xform1.err_detect.algo = RTE_MULTI_FN_ERR_DETECT_CRC32_ETH;
+	xform1.err_detect.op = RTE_MULTI_FN_ERR_DETECT_OP_GENERATE;
+	xform1.next = &xform2;
+
+	xform2.type = RTE_MULTI_FN_XFORM_TYPE_CRYPTO_SYM;
+	xform2.crypto_sym.type = RTE_CRYPTO_SYM_XFORM_CIPHER;
+	xform_cipher = &xform2.crypto_sym.cipher;
+	xform_cipher->op = RTE_CRYPTO_CIPHER_OP_ENCRYPT;
+	xform_cipher->algo = RTE_CRYPTO_CIPHER_AES_DOCSISBPI;
+	xform_cipher->key.data = tdata->key.data;
+	xform_cipher->key.length = tdata->key.len;
+	xform_cipher->iv.offset = sizeof(struct rte_multi_fn_op);
+	xform_cipher->iv.length = tdata->cipher_iv.len;
+	xform2.next = NULL;
+
+	ut_params->sess = rte_multi_fn_session_create(ts_params->dev_id,
+						      &xform1,
+						      rte_socket_id());
+
+	RTE_TEST_ASSERT((ut_params->sess != NULL &&
+			 ut_params->sess->sess_private_data != NULL),
+			"Failed to create multi-function session");
+
+	/* Create operations */
+	nb_ops = rte_multi_fn_op_bulk_alloc(ts_params->op_pool,
+					    ut_params->ops,
+					    2);
+	RTE_TEST_ASSERT_EQUAL(nb_ops,
+			      2,
+			      "Failed to allocate multi-function operations");
+
+	ut_params->ops[0]->next = ut_params->ops[1];
+	ut_params->ops[0]->m_src = ut_params->ibuf;
+	ut_params->ops[0]->m_dst = NULL;
+	ut_params->ops[1]->next = NULL;
+
+	/* CRC op config */
+	crc_len = tdata->plaintext.no_crc == false ?
+					(tdata->plaintext.len -
+					 tdata->plaintext.crc_offset -
+					 RTE_ETHER_CRC_LEN) :
+					0;
+	crc_len = crc_len > 0 ? crc_len : 0;
+	crc_data_len = crc_len == 0 ? 0 : RTE_ETHER_CRC_LEN;
+	crc_op = &ut_params->ops[0]->err_detect;
+	crc_op->data.offset = tdata->plaintext.crc_offset;
+	crc_op->data.length = crc_len;
+	crc_op->output.data = rte_pktmbuf_mtod_offset(
+						ut_params->ibuf,
+						uint8_t *,
+						ut_params->ibuf->data_len -
+							crc_data_len);
+
+	/* Cipher encrypt op config */
+	cipher_len = tdata->plaintext.no_cipher == false ?
+					(tdata->plaintext.len -
+					 tdata->plaintext.cipher_offset) :
+					0;
+	cipher_len = cipher_len > 0 ? cipher_len : 0;
+	cipher_op = &ut_params->ops[1]->crypto_sym;
+	cipher_op->cipher.data.offset = tdata->plaintext.cipher_offset;
+	cipher_op->cipher.data.length = cipher_len;
+	iv_ptr = (uint8_t *)(ut_params->ops[1]) +
+				sizeof(struct rte_multi_fn_op);
+	rte_memcpy(iv_ptr, tdata->cipher_iv.data, tdata->cipher_iv.len);
+
+	/* Attach session to operation */
+	ut_params->ops[0]->sess = ut_params->sess;
+
+	/* Enqueue to device */
+	nb_enq = rte_rawdev_enqueue_buffers(
+				ts_params->dev_id,
+				(struct rte_rawdev_buf **)ut_params->ops,
+				1,
+				(rte_rawdev_obj_t)&qp_id);
+
+	RTE_TEST_ASSERT_EQUAL(nb_enq,
+			      1,
+			      "Failed to enqueue multi-function operations");
+
+	/* Dequeue from device */
+	do {
+		nb_deq = rte_rawdev_dequeue_buffers(
+					ts_params->dev_id,
+					(struct rte_rawdev_buf **)&result,
+					1,
+					(rte_rawdev_obj_t)&qp_id);
+	} while (nb_deq < 1);
+
+	RTE_TEST_ASSERT_EQUAL(nb_deq,
+			      1,
+			      "Failed to dequeue multi-function operations");
+
+	/* Check results */
+	ciphertext = plaintext;
+
+	/* Validate ciphertext */
+	ret = memcmp(ciphertext, tdata->ciphertext.data, tdata->ciphertext.len);
+	RTE_TEST_ASSERT_SUCCESS(ret, "Ciphertext not as expected");
+
+	RTE_TEST_ASSERT_EQUAL(result->overall_status,
+			      RTE_MULTI_FN_OP_STATUS_SUCCESS,
+			      "Multi-function op processing failed");
+
+	/* Print stats */
+	num_stats = rte_rawdev_xstats_get(ts_params->dev_id,
+					  stats_id,
+					  stats,
+					  4);
+	num_names = rte_rawdev_xstats_names_get(ts_params->dev_id,
+						stats_names,
+						4);
+	RTE_TEST_ASSERT_EQUAL(num_stats, 4, "Failed to get stats");
+	RTE_TEST_ASSERT_EQUAL(num_names, 4, "Failed to get stats names");
+
+	for (i = 0; i < num_stats; i++)
+		AESNI_MB_RAWDEV_DEBUG("%s:  %"PRIu64,
+				      stats_names[i].name,
+				      stats[i]);
+
+	return 0;
+}
+
+static int
+test_docsis_decrypt(void *vtdata)
+{
+	struct docsis_test_data *tdata = (struct docsis_test_data *)vtdata;
+	struct testsuite_params *ts_params = &testsuite_params;
+	struct unittest_params *ut_params = &unittest_params;
+
+	/* Xforms */
+	struct rte_multi_fn_xform xform1 = {0};
+	struct rte_multi_fn_xform xform2 = {0};
+	struct rte_crypto_cipher_xform *xform_cipher;
+
+	/* Operations */
+	struct rte_multi_fn_op *result;
+	struct rte_crypto_sym_op *cipher_op;
+	struct rte_multi_fn_err_detect_op *crc_op;
+
+	/* Cipher params */
+	int cipher_len = 0;
+	uint8_t *iv_ptr;
+
+	/* CRC params */
+	int crc_len = 0, crc_data_len;
+
+	/* Test data */
+	uint8_t *plaintext = NULL, *ciphertext = NULL;
+
+	/* Stats */
+	uint64_t stats[4] = {0};
+	struct rte_rawdev_xstats_name stats_names[4] = {0};
+	const unsigned int stats_id[4] = {0, 1, 2, 3};
+	int num_stats = 0, num_names = 0;
+
+	uint16_t qp_id = 0, nb_enq, nb_deq = 0, nb_ops;
+	int i, ret = TEST_SUCCESS;
+
+	/* Setup source mbuf */
+	ut_params->ibuf = rte_pktmbuf_alloc(ts_params->mbuf_pool);
+	RTE_TEST_ASSERT_NOT_NULL(ut_params->ibuf,
+				 "Failed to allocate source mbuf");
+	memset(rte_pktmbuf_mtod(ut_params->ibuf, uint8_t *),
+	       0,
+	       rte_pktmbuf_tailroom(ut_params->ibuf));
+	ciphertext = (uint8_t *)rte_pktmbuf_append(ut_params->ibuf,
+						   tdata->ciphertext.len);
+	memcpy(ciphertext, tdata->ciphertext.data, tdata->ciphertext.len);
+
+	/* Create session */
+	xform1.type = RTE_MULTI_FN_XFORM_TYPE_CRYPTO_SYM;
+	xform1.crypto_sym.type = RTE_CRYPTO_SYM_XFORM_CIPHER;
+	xform_cipher = &xform1.crypto_sym.cipher;
+	xform_cipher->op = RTE_CRYPTO_CIPHER_OP_DECRYPT;
+	xform_cipher->algo = RTE_CRYPTO_CIPHER_AES_DOCSISBPI;
+	xform_cipher->key.data = tdata->key.data;
+	xform_cipher->key.length = tdata->key.len;
+	xform_cipher->iv.offset = sizeof(struct rte_multi_fn_op);
+	xform_cipher->iv.length = tdata->cipher_iv.len;
+	xform1.next = &xform2;
+
+	xform2.type = RTE_MULTI_FN_XFORM_TYPE_ERR_DETECT;
+	xform2.err_detect.algo = RTE_MULTI_FN_ERR_DETECT_CRC32_ETH;
+	xform2.err_detect.op = RTE_MULTI_FN_ERR_DETECT_OP_VERIFY;
+	xform2.next = NULL;
+
+	ut_params->sess = rte_multi_fn_session_create(ts_params->dev_id,
+						      &xform1,
+						      rte_socket_id());
+
+	RTE_TEST_ASSERT((ut_params->sess != NULL &&
+			 ut_params->sess->sess_private_data != NULL),
+			"Failed to create multi-function session");
+
+	/* Create operations */
+	nb_ops = rte_multi_fn_op_bulk_alloc(ts_params->op_pool,
+					    ut_params->ops,
+					    2);
+	RTE_TEST_ASSERT_EQUAL(nb_ops,
+			      2,
+			      "Failed to allocate multi-function operations");
+
+	ut_params->ops[0]->next = ut_params->ops[1];
+	ut_params->ops[0]->m_src = ut_params->ibuf;
+	ut_params->ops[0]->m_dst = NULL;
+	ut_params->ops[1]->next = NULL;
+
+	/* Cipher decrypt op config */
+	cipher_len = tdata->ciphertext.no_cipher == false ?
+					(tdata->ciphertext.len -
+					 tdata->ciphertext.cipher_offset) :
+					0;
+	cipher_len = cipher_len > 0 ? cipher_len : 0;
+	cipher_op = &ut_params->ops[0]->crypto_sym;
+	cipher_op->cipher.data.offset = tdata->ciphertext.cipher_offset;
+	cipher_op->cipher.data.length = cipher_len;
+	iv_ptr = (uint8_t *)(ut_params->ops[1]) +
+				sizeof(struct rte_multi_fn_op);
+	rte_memcpy(iv_ptr, tdata->cipher_iv.data, tdata->cipher_iv.len);
+
+	/* CRC op config */
+	crc_len = tdata->plaintext.no_crc == false ?
+					(tdata->ciphertext.len -
+					 tdata->ciphertext.crc_offset -
+					 RTE_ETHER_CRC_LEN) :
+					0;
+	crc_len = crc_len > 0 ? crc_len : 0;
+	crc_data_len = crc_len == 0 ? 0 : RTE_ETHER_CRC_LEN;
+	crc_op = &ut_params->ops[1]->err_detect;
+	crc_op->data.offset = tdata->ciphertext.crc_offset;
+	crc_op->data.length = crc_len;
+	crc_op->output.data = rte_pktmbuf_mtod_offset(
+						ut_params->ibuf,
+						uint8_t *,
+						ut_params->ibuf->data_len -
+							crc_data_len);
+
+	/* Attach session to operation */
+	ut_params->ops[0]->sess = ut_params->sess;
+
+	/* Enqueue to device */
+	nb_enq = rte_rawdev_enqueue_buffers(
+				ts_params->dev_id,
+				(struct rte_rawdev_buf **)ut_params->ops,
+				1,
+				(rte_rawdev_obj_t)&qp_id);
+
+	RTE_TEST_ASSERT_EQUAL(nb_enq,
+			      1,
+			      "Failed to enqueue multi-function operations");
+
+	/* Dequeue to device */
+	do {
+		nb_deq = rte_rawdev_dequeue_buffers(
+					ts_params->dev_id,
+					(struct rte_rawdev_buf **)&result,
+					1,
+					(rte_rawdev_obj_t)&qp_id);
+	} while (nb_deq < 1);
+
+	RTE_TEST_ASSERT_EQUAL(nb_deq,
+			      1,
+			      "Failed to dequeue multi-function operations");
+
+	/* Check results */
+	plaintext = ciphertext;
+
+	/* Validate plaintext */
+	ret = memcmp(plaintext,
+		     tdata->plaintext.data,
+		     /* Check only as far as CRC - CRC is checked internally */
+		     tdata->plaintext.len - crc_data_len);
+	RTE_TEST_ASSERT_SUCCESS(ret, "Plaintext not as expected");
+
+	RTE_TEST_ASSERT_EQUAL(result->overall_status,
+			      RTE_MULTI_FN_OP_STATUS_SUCCESS,
+			      "Multi-function op processing failed");
+
+	/* Print stats */
+	num_stats = rte_rawdev_xstats_get(ts_params->dev_id,
+					  stats_id,
+					  stats,
+					  4);
+	num_names = rte_rawdev_xstats_names_get(ts_params->dev_id,
+						stats_names,
+						4);
+	RTE_TEST_ASSERT_EQUAL(num_stats, 4, "Failed to get stats");
+	RTE_TEST_ASSERT_EQUAL(num_names, 4, "Failed to get stats names");
+
+	for (i = 0; i < num_stats; i++)
+		AESNI_MB_RAWDEV_DEBUG("%s:  %"PRIu64,
+				      stats_names[i].name,
+				      stats[i]);
+
+	return 0;
+}
+
+static int
+test_pon_encrypt(void *vtdata)
+{
+	struct pon_test_data *tdata = (struct pon_test_data *)vtdata;
+	struct testsuite_params *ts_params = &testsuite_params;
+	struct unittest_params *ut_params = &unittest_params;
+
+	/* Xforms */
+	struct rte_multi_fn_xform xform1 = {0};
+	struct rte_multi_fn_xform xform2 = {0};
+	struct rte_multi_fn_xform xform3 = {0};
+	struct rte_crypto_cipher_xform *xform_cipher;
+
+	/* Operations */
+	struct rte_multi_fn_op *result;
+	struct rte_crypto_sym_op *cipher_op;
+	struct rte_multi_fn_err_detect_op *crc_op;
+	struct rte_multi_fn_err_detect_op *bip_op;
+
+	/* Cipher params */
+	int cipher_len = 0;
+	uint8_t *iv_ptr;
+
+	/* CRC params */
+	int crc_len = 0, crc_data_len = 0;
+
+	/* BIP params */
+	int bip_len = 0;
+
+	/* Test data */
+	uint8_t *plaintext = NULL, *ciphertext = NULL;
+
+	/* Stats */
+	uint64_t stats[4] = {0};
+	struct rte_rawdev_xstats_name stats_names[4] = {0};
+	const unsigned int stats_id[4] = {0, 1, 2, 3};
+	int num_stats = 0, num_names = 0;
+
+	uint16_t qp_id = 0, nb_enq, nb_deq = 0, nb_ops;
+	int i, ret = TEST_SUCCESS;
+
+	/* Setup source mbuf */
+	ut_params->ibuf = rte_pktmbuf_alloc(ts_params->mbuf_pool);
+	RTE_TEST_ASSERT_NOT_NULL(ut_params->ibuf,
+				 "Failed to allocate source mbuf");
+	memset(rte_pktmbuf_mtod(ut_params->ibuf, uint8_t *),
+	       0,
+	       rte_pktmbuf_tailroom(ut_params->ibuf));
+	plaintext = (uint8_t *)rte_pktmbuf_append(ut_params->ibuf,
+						  tdata->plaintext.len);
+	memcpy(plaintext, tdata->plaintext.data, tdata->plaintext.len);
+
+	/* Create session */
+	xform1.type = RTE_MULTI_FN_XFORM_TYPE_ERR_DETECT;
+	xform1.err_detect.algo = RTE_MULTI_FN_ERR_DETECT_CRC32_ETH;
+	xform1.err_detect.op = RTE_MULTI_FN_ERR_DETECT_OP_GENERATE;
+	xform1.next = &xform2;
+
+	xform2.type = RTE_MULTI_FN_XFORM_TYPE_CRYPTO_SYM;
+	xform2.crypto_sym.type = RTE_CRYPTO_SYM_XFORM_CIPHER;
+	xform_cipher = &xform2.crypto_sym.cipher;
+	xform_cipher->op = RTE_CRYPTO_CIPHER_OP_ENCRYPT;
+	xform_cipher->algo = RTE_CRYPTO_CIPHER_AES_CTR;
+	xform_cipher->key.data = tdata->key.data;
+	xform_cipher->key.length = tdata->key.len;
+	xform_cipher->iv.offset = sizeof(struct rte_multi_fn_op);
+	xform_cipher->iv.length = tdata->cipher_iv.len;
+	xform2.next = &xform3;
+
+	xform3.type = RTE_MULTI_FN_XFORM_TYPE_ERR_DETECT;
+	xform3.err_detect.algo = RTE_MULTI_FN_ERR_DETECT_BIP32;
+	xform3.err_detect.op = RTE_MULTI_FN_ERR_DETECT_OP_GENERATE;
+	xform3.next = NULL;
+
+	ut_params->sess = rte_multi_fn_session_create(ts_params->dev_id,
+						      &xform1,
+						      rte_socket_id());
+
+	RTE_TEST_ASSERT((ut_params->sess != NULL &&
+			 ut_params->sess->sess_private_data != NULL),
+			"Failed to create multi-function session");
+
+	/* Create operations */
+	nb_ops = rte_multi_fn_op_bulk_alloc(ts_params->op_pool,
+					    ut_params->ops,
+					    3);
+	RTE_TEST_ASSERT_EQUAL(nb_ops,
+			      3,
+			      "Failed to allocate multi-function operations");
+
+	ut_params->ops[0]->next = ut_params->ops[1];
+	ut_params->ops[0]->m_src = ut_params->ibuf;
+	ut_params->ops[0]->m_dst = NULL;
+	ut_params->ops[1]->next = ut_params->ops[2];
+	ut_params->ops[2]->next = NULL;
+
+	/* CRC op config */
+	crc_len = tdata->plaintext.len -
+			tdata->plaintext.crc_offset -
+			tdata->plaintext.padding_len -
+			RTE_ETHER_CRC_LEN;
+	crc_len = crc_len > 0 ? crc_len : 0;
+	crc_data_len = crc_len == 0 ? 0 : RTE_ETHER_CRC_LEN;
+	crc_op = &ut_params->ops[0]->err_detect;
+	crc_op->data.offset = tdata->plaintext.crc_offset;
+	crc_op->data.length = crc_len;
+	crc_op->output.data = rte_pktmbuf_mtod_offset(
+					ut_params->ibuf,
+					uint8_t *,
+					ut_params->ibuf->data_len -
+						tdata->plaintext.padding_len -
+						crc_data_len);
+
+	/* Cipher encrypt op config */
+	cipher_len = tdata->plaintext.no_cipher == false ?
+					(tdata->plaintext.len -
+					 tdata->plaintext.cipher_offset) :
+					0;
+	cipher_len = cipher_len > 0 ? cipher_len : 0;
+	cipher_op = &ut_params->ops[1]->crypto_sym;
+	cipher_op->cipher.data.offset = tdata->plaintext.cipher_offset;
+	cipher_op->cipher.data.length = cipher_len;
+	iv_ptr = (uint8_t *)(ut_params->ops[1]) +
+				sizeof(struct rte_multi_fn_op);
+	rte_memcpy(iv_ptr, tdata->cipher_iv.data, tdata->cipher_iv.len);
+
+	/* BIP op config */
+	bip_len = tdata->plaintext.len - tdata->plaintext.bip_offset;
+	bip_len = bip_len > 0 ? bip_len : 0;
+	bip_op = &ut_params->ops[2]->err_detect;
+	bip_op->data.offset = tdata->plaintext.bip_offset;
+	bip_op->data.length = bip_len;
+	bip_op->output.data = (uint8_t *)(ut_params->ops[2]) +
+				sizeof(struct rte_multi_fn_op);
+
+	/* Attach session to op */
+	ut_params->ops[0]->sess = ut_params->sess;
+
+	/* Enqueue to device */
+	nb_enq = rte_rawdev_enqueue_buffers(
+				ts_params->dev_id,
+				(struct rte_rawdev_buf **)ut_params->ops,
+				1,
+				(rte_rawdev_obj_t)&qp_id);
+
+	RTE_TEST_ASSERT_EQUAL(nb_enq,
+			      1,
+			      "Failed to enqueue multi-function operations");
+
+	/* Dequeue from device */
+	do {
+		nb_deq = rte_rawdev_dequeue_buffers(
+					ts_params->dev_id,
+					(struct rte_rawdev_buf **)&result,
+					1,
+					(rte_rawdev_obj_t)&qp_id);
+	} while (nb_deq < 1);
+
+	/* Check results */
+	ciphertext = plaintext;
+
+	/* Validate ciphertext */
+	ret = memcmp(ciphertext, tdata->ciphertext.data, tdata->ciphertext.len);
+	RTE_TEST_ASSERT_SUCCESS(ret, "Ciphertext not as expected");
+
+	ret = memcmp(bip_op->output.data,
+		     tdata->output.data,
+		     tdata->output.len);
+	RTE_TEST_ASSERT_SUCCESS(ret, "BIP not as expected");
+
+	RTE_TEST_ASSERT_EQUAL(result->overall_status,
+			      RTE_MULTI_FN_OP_STATUS_SUCCESS,
+			      "Multi-function op processing failed");
+
+	/* Print stats */
+	num_stats = rte_rawdev_xstats_get(ts_params->dev_id,
+					  stats_id,
+					  stats,
+					  4);
+	num_names = rte_rawdev_xstats_names_get(ts_params->dev_id,
+						stats_names,
+						4);
+	RTE_TEST_ASSERT_EQUAL(num_stats, 4, "Failed to get stats");
+	RTE_TEST_ASSERT_EQUAL(num_names, 4, "Failed to get stats names");
+
+	for (i = 0; i < num_stats; i++)
+		AESNI_MB_RAWDEV_DEBUG("%s:  %"PRIu64,
+				      stats_names[i].name,
+				      stats[i]);
+
+	return 0;
+}
+
+static int
+test_pon_decrypt(void *vtdata)
+{
+	struct pon_test_data *tdata = (struct pon_test_data *)vtdata;
+	struct testsuite_params *ts_params = &testsuite_params;
+	struct unittest_params *ut_params = &unittest_params;
+
+	/* Xforms */
+	struct rte_multi_fn_xform xform1 = {0};
+	struct rte_multi_fn_xform xform2 = {0};
+	struct rte_multi_fn_xform xform3 = {0};
+	struct rte_crypto_cipher_xform *xform_cipher;
+
+	/* Operations */
+	struct rte_multi_fn_op *result;
+	struct rte_crypto_sym_op *cipher_op;
+	struct rte_multi_fn_err_detect_op *crc_op;
+	struct rte_multi_fn_err_detect_op *bip_op;
+
+	/* Cipher params */
+	int cipher_len = 0;
+	uint8_t *iv_ptr;
+
+	/* CRC params */
+	int crc_len = 0, crc_data_len = 0;
+
+	/* BIP params */
+	int bip_len = 0;
+
+	/* Test data */
+	uint8_t *plaintext = NULL, *ciphertext = NULL;
+
+	/* Stats */
+	uint64_t stats[4] = {0};
+	struct rte_rawdev_xstats_name stats_names[4] = {0};
+	const unsigned int stats_id[4] = {0, 1, 2, 3};
+	int num_stats = 0, num_names = 0;
+
+	uint16_t qp_id = 0, nb_enq, nb_deq = 0, nb_ops;
+	int i, ret = TEST_SUCCESS;
+
+	/* Setup source mbuf */
+	ut_params->ibuf = rte_pktmbuf_alloc(ts_params->mbuf_pool);
+	RTE_TEST_ASSERT_NOT_NULL(ut_params->ibuf,
+				 "Failed to allocate source mbuf");
+	memset(rte_pktmbuf_mtod(ut_params->ibuf, uint8_t *),
+	       0,
+	       rte_pktmbuf_tailroom(ut_params->ibuf));
+	ciphertext = (uint8_t *)rte_pktmbuf_append(ut_params->ibuf,
+						   tdata->ciphertext.len);
+	memcpy(ciphertext, tdata->ciphertext.data, tdata->ciphertext.len);
+
+	/* Create session */
+	xform1.type = RTE_MULTI_FN_XFORM_TYPE_ERR_DETECT;
+	xform1.err_detect.algo = RTE_MULTI_FN_ERR_DETECT_BIP32;
+	xform1.err_detect.op = RTE_MULTI_FN_ERR_DETECT_OP_GENERATE;
+	xform1.next = &xform2;
+
+	xform2.type = RTE_MULTI_FN_XFORM_TYPE_CRYPTO_SYM;
+	xform2.crypto_sym.type = RTE_CRYPTO_SYM_XFORM_CIPHER;
+	xform_cipher = &xform2.crypto_sym.cipher;
+	xform_cipher->op = RTE_CRYPTO_CIPHER_OP_DECRYPT;
+	xform_cipher->algo = RTE_CRYPTO_CIPHER_AES_CTR;
+	xform_cipher->key.data = tdata->key.data;
+	xform_cipher->key.length = tdata->key.len;
+	xform_cipher->iv.offset = sizeof(struct rte_multi_fn_op);
+	xform_cipher->iv.length = tdata->cipher_iv.len;
+	xform2.next = &xform3;
+
+	xform3.type = RTE_MULTI_FN_XFORM_TYPE_ERR_DETECT;
+	xform3.err_detect.algo = RTE_MULTI_FN_ERR_DETECT_CRC32_ETH;
+	xform3.err_detect.op = RTE_MULTI_FN_ERR_DETECT_OP_VERIFY;
+	xform3.next = NULL;
+
+	ut_params->sess = rte_multi_fn_session_create(ts_params->dev_id,
+						      &xform1,
+						      rte_socket_id());
+
+	RTE_TEST_ASSERT((ut_params->sess != NULL &&
+			 ut_params->sess->sess_private_data != NULL),
+			"Failed to create multi-function session");
+
+	/* Create operations */
+	nb_ops = rte_multi_fn_op_bulk_alloc(ts_params->op_pool,
+					    ut_params->ops,
+					    3);
+	RTE_TEST_ASSERT_EQUAL(nb_ops,
+			      3,
+			      "Failed to allocate multi-function operations");
+
+	ut_params->ops[0]->next = ut_params->ops[1];
+	ut_params->ops[0]->m_src = ut_params->ibuf;
+	ut_params->ops[0]->m_dst = NULL;
+	ut_params->ops[1]->next = ut_params->ops[2];
+	ut_params->ops[2]->next = NULL;
+
+	/* BIP op config */
+	bip_len = tdata->ciphertext.len - tdata->ciphertext.bip_offset;
+	bip_len = bip_len > 0 ? bip_len : 0;
+	bip_op = &ut_params->ops[0]->err_detect;
+	bip_op->data.offset = tdata->ciphertext.bip_offset;
+	bip_op->data.length = bip_len;
+	bip_op->output.data = (uint8_t *)(ut_params->ops[0]) +
+				sizeof(struct rte_multi_fn_op);
+
+	/* Cipher encrypt op config */
+	cipher_len = tdata->ciphertext.no_cipher == false ?
+					(tdata->ciphertext.len -
+					 tdata->ciphertext.cipher_offset) :
+					0;
+	cipher_len = cipher_len > 0 ? cipher_len : 0;
+	cipher_op = &ut_params->ops[1]->crypto_sym;
+	cipher_op->cipher.data.offset = tdata->ciphertext.cipher_offset;
+	cipher_op->cipher.data.length = cipher_len;
+	iv_ptr = (uint8_t *)(ut_params->ops[1]) +
+				sizeof(struct rte_multi_fn_op);
+	rte_memcpy(iv_ptr, tdata->cipher_iv.data, tdata->cipher_iv.len);
+
+	/* CRC op config */
+	crc_len = tdata->ciphertext.len -
+			tdata->ciphertext.crc_offset -
+			tdata->ciphertext.padding_len -
+			RTE_ETHER_CRC_LEN;
+	crc_len = crc_len > 0 ? crc_len : 0;
+	crc_data_len = crc_len == 0 ? 0 : RTE_ETHER_CRC_LEN;
+	crc_op = &ut_params->ops[2]->err_detect;
+	crc_op->data.offset = tdata->ciphertext.crc_offset;
+	crc_op->data.length = crc_len;
+	crc_op->output.data = rte_pktmbuf_mtod_offset(
+					ut_params->ibuf,
+					uint8_t *,
+					ut_params->ibuf->data_len -
+						tdata->ciphertext.padding_len -
+						crc_data_len);
+
+	/* Attach session to op */
+	ut_params->ops[0]->sess = ut_params->sess;
+
+	/* Enqueue to device */
+	nb_enq = rte_rawdev_enqueue_buffers(
+				ts_params->dev_id,
+				(struct rte_rawdev_buf **)ut_params->ops,
+				1,
+				(rte_rawdev_obj_t)&qp_id);
+
+	RTE_TEST_ASSERT_EQUAL(nb_enq,
+			      1,
+			      "Failed to enqueue multi-function operations");
+
+	/* Dequeue from device */
+	do {
+		nb_deq = rte_rawdev_dequeue_buffers(
+					ts_params->dev_id,
+					(struct rte_rawdev_buf **)&result,
+					1,
+					(rte_rawdev_obj_t)&qp_id);
+	} while (nb_deq < 1);
+
+	/* Check results */
+	plaintext = ciphertext;
+
+	/* Validate plaintext */
+	ret = memcmp(plaintext,
+		     tdata->plaintext.data,
+		     /* Check only as far as CRC - CRC is checked internally */
+		     tdata->plaintext.len -
+			tdata->plaintext.padding_len -
+			crc_data_len);
+	RTE_TEST_ASSERT_SUCCESS(ret, "Plaintext not as expected");
+
+	ret = memcmp(bip_op->output.data,
+		     tdata->output.data,
+		     tdata->output.len);
+	RTE_TEST_ASSERT_SUCCESS(ret, "BIP not as expected");
+
+	RTE_TEST_ASSERT_EQUAL(result->overall_status,
+			      RTE_MULTI_FN_OP_STATUS_SUCCESS,
+			      "Multi-function op processing failed");
+
+	/* Print stats */
+	num_stats = rte_rawdev_xstats_get(ts_params->dev_id,
+					  stats_id,
+					  stats,
+					  4);
+	num_names = rte_rawdev_xstats_names_get(ts_params->dev_id,
+						stats_names,
+						4);
+	RTE_TEST_ASSERT_EQUAL(num_stats, 4, "Failed to get stats");
+	RTE_TEST_ASSERT_EQUAL(num_names, 4, "Failed to get stats names");
+
+	for (i = 0; i < num_stats; i++)
+		AESNI_MB_RAWDEV_DEBUG("%s:  %"PRIu64,
+				      stats_names[i].name,
+				      stats[i]);
+
+	return 0;
+}
+
+static void
+test_run(int (*setup)(void),
+	 void (*teardown)(void),
+	 int (*run)(void *),
+	 void *data,
+	 const char *name)
+{
+	int ret = 0;
+
+	if (setup != NULL) {
+		ret = setup();
+		if (ret < 0) {
+			AESNI_MB_RAWDEV_INFO("Error setting up test %s", name);
+			unsupported++;
+		}
+	}
+
+	if (run != NULL) {
+		ret = run(data);
+		if (ret < 0) {
+			failed++;
+			AESNI_MB_RAWDEV_INFO("%s Failed", name);
+		} else {
+			passed++;
+			AESNI_MB_RAWDEV_INFO("%s Passed", name);
+		}
+	}
+
+	if (teardown != NULL)
+		teardown();
+
+	total++;
+}
+
+int
+aesni_mb_rawdev_test(uint16_t dev_id)
+{
+	if (testsuite_setup(dev_id) != TEST_SUCCESS) {
+		AESNI_MB_RAWDEV_ERR("Setup failed");
+		testsuite_teardown();
+		return TEST_FAILED;
+	}
+
+	/* DOCSIS: Crypto-CRC */
+	TEST(test_setup, test_teardown, test_docsis_encrypt,
+	     &docsis_test_case_1, "1");
+	TEST(test_setup, test_teardown, test_docsis_encrypt,
+	     &docsis_test_case_2, "2");
+	TEST(test_setup, test_teardown, test_docsis_encrypt,
+	     &docsis_test_case_3, "3");
+	TEST(test_setup, test_teardown, test_docsis_encrypt,
+	     &docsis_test_case_4, "4");
+	TEST(test_setup, test_teardown, test_docsis_encrypt,
+	     &docsis_test_case_5, "5");
+	TEST(test_setup, test_teardown, test_docsis_encrypt,
+	     &docsis_test_case_6, "6");
+	TEST(test_setup, test_teardown, test_docsis_encrypt,
+	     &docsis_test_case_7, "7");
+	TEST(test_setup, test_teardown, test_docsis_encrypt,
+	     &docsis_test_case_8, "8");
+	TEST(test_setup, test_teardown, test_docsis_encrypt,
+	     &docsis_test_case_9, "9");
+	TEST(test_setup, test_teardown, test_docsis_encrypt,
+	     &docsis_test_case_10, "10");
+	TEST(test_setup, test_teardown, test_docsis_encrypt,
+	     &docsis_test_case_11, "11");
+	TEST(test_setup, test_teardown, test_docsis_encrypt,
+	     &docsis_test_case_12, "12");
+	TEST(test_setup, test_teardown, test_docsis_encrypt,
+	     &docsis_test_case_13, "13");
+	TEST(test_setup, test_teardown, test_docsis_decrypt,
+	     &docsis_test_case_1, "1");
+	TEST(test_setup, test_teardown, test_docsis_decrypt,
+	     &docsis_test_case_2, "2");
+	TEST(test_setup, test_teardown, test_docsis_decrypt,
+	     &docsis_test_case_3, "3");
+	TEST(test_setup, test_teardown, test_docsis_decrypt,
+	     &docsis_test_case_4, "4");
+	TEST(test_setup, test_teardown, test_docsis_decrypt,
+	     &docsis_test_case_5, "5");
+	TEST(test_setup, test_teardown, test_docsis_decrypt,
+	     &docsis_test_case_6, "6");
+	TEST(test_setup, test_teardown, test_docsis_decrypt,
+	     &docsis_test_case_7, "7");
+	TEST(test_setup, test_teardown, test_docsis_decrypt,
+	     &docsis_test_case_8, "8");
+	TEST(test_setup, test_teardown, test_docsis_decrypt,
+	     &docsis_test_case_9, "9");
+	TEST(test_setup, test_teardown, test_docsis_decrypt,
+	     &docsis_test_case_10, "10");
+	TEST(test_setup, test_teardown, test_docsis_decrypt,
+	     &docsis_test_case_11, "11");
+	TEST(test_setup, test_teardown, test_docsis_decrypt,
+	     &docsis_test_case_12, "12");
+	TEST(test_setup, test_teardown, test_docsis_decrypt,
+	     &docsis_test_case_13, "13");
+
+	/* PON: Crypto-CRC-BIP */
+	TEST(test_setup, test_teardown, test_pon_encrypt,
+	     &pon_test_case_1, "1");
+	TEST(test_setup, test_teardown, test_pon_encrypt,
+	     &pon_test_case_2, "2");
+	TEST(test_setup, test_teardown, test_pon_encrypt,
+	     &pon_test_case_3, "3");
+	TEST(test_setup, test_teardown, test_pon_encrypt,
+	     &pon_test_case_4, "4");
+	TEST(test_setup, test_teardown, test_pon_encrypt,
+	     &pon_test_case_5, "5");
+	TEST(test_setup, test_teardown, test_pon_encrypt,
+	     &pon_test_case_6, "6");
+	TEST(test_setup, test_teardown, test_pon_decrypt,
+	     &pon_test_case_1, "1");
+	TEST(test_setup, test_teardown, test_pon_decrypt,
+	     &pon_test_case_2, "2");
+	TEST(test_setup, test_teardown, test_pon_decrypt,
+	     &pon_test_case_3, "3");
+	TEST(test_setup, test_teardown, test_pon_decrypt,
+	     &pon_test_case_4, "4");
+	TEST(test_setup, test_teardown, test_pon_decrypt,
+	     &pon_test_case_5, "5");
+	TEST(test_setup, test_teardown, test_pon_decrypt,
+	     &pon_test_case_6, "6");
+
+	testsuite_teardown();
+
+	printf("Total tests   : %d\n", total);
+	printf("Passed        : %d\n", passed);
+	printf("Failed        : %d\n", failed);
+	printf("Not supported : %d\n", unsupported);
+
+	if (failed)
+		return TEST_FAILED;
+
+	return TEST_SUCCESS;
+}
diff --git a/drivers/raw/aesni_mb/aesni_mb_rawdev_test_vectors.h b/drivers/raw/aesni_mb/aesni_mb_rawdev_test_vectors.h
new file mode 100644
index 000000000..46bb220f4
--- /dev/null
+++ b/drivers/raw/aesni_mb/aesni_mb_rawdev_test_vectors.h
@@ -0,0 +1,1183 @@ 
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Intel Corporation.
+ */
+
+#ifndef _AESNI_MB_RAWDEV_TEST_VECTORS_H_
+#define _AESNI_MB_RAWDEV_TEST_VECTORS_H_
+
+#include <stdbool.h>
+
+/*
+ * DOCSIS test data and cases
+ * - encrypt direction: CRC-Crypto
+ * - decrypt direction: Crypto-CRC
+ */
+struct docsis_test_data {
+	struct {
+		uint8_t data[16];
+		unsigned int len;
+	} key;
+
+	struct {
+		uint8_t data[16] __rte_aligned(16);
+		unsigned int len;
+	} cipher_iv;
+
+	struct {
+		uint8_t data[1024];
+		unsigned int len;
+		unsigned int cipher_offset;
+		unsigned int crc_offset;
+		bool no_cipher;
+		bool no_crc;
+	} plaintext;
+
+	struct {
+		uint8_t data[1024];
+		unsigned int len;
+		unsigned int cipher_offset;
+		unsigned int crc_offset;
+		bool no_cipher;
+		bool no_crc;
+	} ciphertext;
+};
+
+struct docsis_test_data docsis_test_case_1 = {
+	.key = {
+		.data = {
+			0x00, 0x00, 0x00, 0x00, 0xAA, 0xBB, 0xCC, 0xDD,
+			0xEE, 0xFF, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55
+		},
+		.len = 16
+	},
+	.cipher_iv = {
+		.data = {
+			0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+			0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
+		},
+		.len = 16
+	},
+	.plaintext = {
+		.data = {
+			/* DOCSIS header */
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			/* Ethernet frame */
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x06, 0x05,
+			0x04, 0x03, 0x02, 0x01, 0x08, 0x00,
+			/* CRC */
+			0xFF, 0xFF, 0xFF, 0xFF
+		},
+		.len = 24,
+		.cipher_offset = 18,
+		.crc_offset = 6,
+		.no_cipher = false,
+		.no_crc = false
+	},
+	.ciphertext = {
+		.data = {
+			/* DOCSIS header */
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			/* Ethernet frame */
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x06, 0x05,
+			0x04, 0x03, 0x02, 0x01, 0x7A, 0xF0,
+			/* CRC */
+			0x61, 0xF8, 0x63, 0x42
+		},
+		.len = 24,
+		.cipher_offset = 18,
+		.crc_offset = 6,
+		.no_cipher = false,
+		.no_crc = false
+	}
+};
+
+struct docsis_test_data docsis_test_case_2 = {
+	.key = {
+		.data = {
+			0x00, 0x00, 0x00, 0x00, 0xAA, 0xBB, 0xCC, 0xDD,
+			0xEE, 0xFF, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55
+		},
+		.len = 16
+	},
+	.cipher_iv = {
+		.data = {
+			0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+			0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
+		},
+		.len = 16
+	},
+	.plaintext = {
+		.data = {
+			/* DOCSIS header */
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			/* Ethernet frame */
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x06, 0x05,
+			0x04, 0x03, 0x02, 0x01, 0x08, 0x00, 0xAA,
+			/* CRC */
+			0xFF, 0xFF, 0xFF, 0xFF
+		},
+		.len = 25,
+		.cipher_offset = 18,
+		.crc_offset = 6,
+		.no_cipher = false,
+		.no_crc = false
+	},
+	.ciphertext = {
+		.data = {
+			/* DOCSIS header */
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			/* Ethernet frame */
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x06, 0x05,
+			0x04, 0x03, 0x02, 0x01, 0x7A, 0xF0, 0xDF,
+			/* CRC */
+			0xFE, 0x12, 0x99, 0xE5
+		},
+		.len = 25,
+		.cipher_offset = 18,
+		.crc_offset = 6,
+		.no_cipher = false,
+		.no_crc = false
+	}
+};
+
+struct docsis_test_data docsis_test_case_3 = {
+	.key = {
+		.data = {
+			0x00, 0x00, 0x00, 0x00, 0xAA, 0xBB, 0xCC, 0xDD,
+			0xEE, 0xFF, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55
+		},
+		.len = 16
+	},
+	.cipher_iv = {
+		.data = {
+			0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+			0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
+		},
+		.len = 16
+	},
+	.plaintext = {
+		.data = {
+			/* DOCSIS header */
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			/* Ethernet frame */
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x06, 0x05,
+			0x04, 0x03, 0x02, 0x01, 0x08, 0x00, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			/* CRC */
+			0xFF, 0xFF, 0xFF, 0xFF
+		},
+		.len = 34,
+		.cipher_offset = 18,
+		.crc_offset = 6,
+		.no_cipher = false,
+		.no_crc = false
+	},
+	.ciphertext = {
+		.data = {
+			/* DOCSIS header */
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			/* Ethernet frame */
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x06, 0x05,
+			0x04, 0x03, 0x02, 0x01, 0xD6, 0xE2, 0x70, 0x5C,
+			0xE6, 0x4D, 0xCC, 0x8C, 0x47, 0xB7, 0x09, 0xD6,
+			/* CRC */
+			0x54, 0x85, 0xF8, 0x32
+		},
+		.len = 34,
+		.cipher_offset = 18,
+		.crc_offset = 6,
+		.no_cipher = false,
+		.no_crc = false
+	}
+};
+
+struct docsis_test_data docsis_test_case_4 = {
+	.key = {
+		.data = {
+			0x00, 0x00, 0x00, 0x00, 0xAA, 0xBB, 0xCC, 0xDD,
+			0xEE, 0xFF, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55
+		},
+		.len = 16
+	},
+	.cipher_iv = {
+		.data = {
+			0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+			0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
+		},
+		.len = 16
+	},
+	.plaintext = {
+		.data = {
+			/* DOCSIS header */
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			/* Ethernet frame */
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x06, 0x05,
+			0x04, 0x03, 0x02, 0x01, 0x08, 0x00, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA,
+			/* CRC */
+			0xFF, 0xFF, 0xFF, 0xFF
+		},
+		.len = 35,
+		.cipher_offset = 18,
+		.crc_offset = 6,
+		.no_cipher = false,
+		.no_crc = false
+	},
+	.ciphertext = {
+		.data = {
+			/* DOCSIS header */
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			/* Ethernet frame */
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x06, 0x05,
+			0x04, 0x03, 0x02, 0x01, 0x92, 0x6A, 0xC2, 0xDC,
+			0xEE, 0x3B, 0x31, 0xEC, 0x03, 0xDE, 0x95, 0x33,
+			0x5E,
+			/* CRC */
+			0xFE, 0x47, 0x3E, 0x22
+		},
+		.len = 35,
+		.cipher_offset = 18,
+		.crc_offset = 6,
+		.no_cipher = false,
+		.no_crc = false
+	}
+};
+
+struct docsis_test_data docsis_test_case_5 = {
+	.key = {
+		.data = {
+			0x00, 0x00, 0x00, 0x00, 0xAA, 0xBB, 0xCC, 0xDD,
+			0xEE, 0xFF, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55
+		},
+		.len = 16
+	},
+	.cipher_iv = {
+		.data = {
+			0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+			0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
+		},
+		.len = 16
+	},
+	.plaintext = {
+		.data = {
+			/* DOCSIS header */
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			/* Ethernet frame */
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x06, 0x05,
+			0x04, 0x03, 0x02, 0x01, 0x08, 0x00, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			/* CRC */
+			0xFF, 0xFF, 0xFF, 0xFF
+		},
+		.len = 82,
+		.cipher_offset = 18,
+		.crc_offset = 6,
+		.no_cipher = false,
+		.no_crc = false
+	},
+	.ciphertext = {
+		.data = {
+			/* DOCSIS header */
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			/* Ethernet frame */
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x06, 0x05,
+			0x04, 0x03, 0x02, 0x01, 0x77, 0x74, 0x56, 0x05,
+			0xD1, 0x14, 0xA2, 0x8D, 0x2C, 0x9A, 0x11, 0xFC,
+			0x7D, 0xB0, 0xE7, 0x18, 0xCE, 0x75, 0x7C, 0x89,
+			0x14, 0x56, 0xE2, 0xF2, 0xB7, 0x47, 0x08, 0x27,
+			0xF7, 0x08, 0x7A, 0x13, 0x90, 0x81, 0x75, 0xB0,
+			0xC7, 0x91, 0x04, 0x83, 0xAD, 0x11, 0x46, 0x46,
+			0xF8, 0x54, 0x87, 0xA0, 0x42, 0xF3, 0x71, 0xA9,
+			0x8A, 0xCD, 0x59, 0x77, 0x67, 0x11, 0x1A, 0x87,
+			/* CRC */
+			0xAB, 0xED, 0x2C, 0x26
+		},
+		.len = 82,
+		.cipher_offset = 18,
+		.crc_offset = 6,
+		.no_cipher = false,
+		.no_crc = false
+	}
+};
+
+struct docsis_test_data docsis_test_case_6 = {
+	.key = {
+		.data = {
+			0x00, 0x00, 0x00, 0x00, 0xAA, 0xBB, 0xCC, 0xDD,
+			0xEE, 0xFF, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55
+		},
+		.len = 16
+	},
+	.cipher_iv = {
+		.data = {
+			0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+			0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
+		},
+		.len = 16
+	},
+	.plaintext = {
+		.data = {
+			/* DOCSIS header */
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			/* Ethernet frame */
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x06, 0x05,
+			0x04, 0x03, 0x02, 0x01, 0x08, 0x00, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA,
+			/* CRC */
+			0xFF, 0xFF, 0xFF, 0xFF
+		},
+		.len = 83,
+		.cipher_offset = 18,
+		.crc_offset = 6,
+		.no_cipher = false,
+		.no_crc = false
+	},
+	.ciphertext = {
+		.data = {
+			/* DOCSIS header */
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			/* Ethernet frame */
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x06, 0x05,
+			0x04, 0x03, 0x02, 0x01, 0x77, 0x74, 0x56, 0x05,
+			0xD1, 0x14, 0xA2, 0x8D, 0x2C, 0x9A, 0x11, 0xFC,
+			0x7D, 0xB0, 0xE7, 0x18, 0xCE, 0x75, 0x7C, 0x89,
+			0x14, 0x56, 0xE2, 0xF2, 0xB7, 0x47, 0x08, 0x27,
+			0xF7, 0x08, 0x7A, 0x13, 0x90, 0x81, 0x75, 0xB0,
+			0xC7, 0x91, 0x04, 0x83, 0xAD, 0x11, 0x46, 0x46,
+			0xF8, 0x54, 0x87, 0xA0, 0xA4, 0x0C, 0xC2, 0xF0,
+			0x81, 0x49, 0xA8, 0xA6, 0x6C, 0x48, 0xEB, 0x1F,
+			0x4B,
+			/* CRC */
+			0x2F, 0xD4, 0x48, 0x18
+		},
+		.len = 83,
+		.cipher_offset = 18,
+		.crc_offset = 6,
+		.no_cipher = false,
+		.no_crc = false
+	}
+};
+
+struct docsis_test_data docsis_test_case_7 = {
+	.key = {
+		.data = {
+			0x00, 0x00, 0x00, 0x00, 0xAA, 0xBB, 0xCC, 0xDD,
+			0xEE, 0xFF, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55
+		},
+		.len = 16
+	},
+	.cipher_iv = {
+		.data = {
+			0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+			0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
+		},
+		.len = 16
+	},
+	.plaintext = {
+		.data = {
+			/* DOCSIS header */
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			/* Ethernet frame */
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x06, 0x05,
+			0x04, 0x03, 0x02, 0x01, 0x08, 0x00, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA,
+			/* CRC */
+			0xFF, 0xFF, 0xFF, 0xFF
+		},
+		.len = 83,
+		.cipher_offset = 40,
+		.crc_offset = 6,
+		.no_cipher = false,
+		.no_crc = false
+	},
+	.ciphertext = {
+		.data = {
+			/* DOCSIS header */
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			/* Ethernet frame */
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x06, 0x05,
+			0x04, 0x03, 0x02, 0x01, 0x08, 0x00, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA, 0xAA, 0x3B, 0x9F, 0x72, 0x4C, 0xB5, 0x72,
+			0x3E, 0x56, 0x54, 0x49, 0x13, 0x53, 0xC4, 0xAA,
+			0xCD, 0xEA, 0x6A, 0x88, 0x99, 0x07, 0x86, 0xF4,
+			0xCF, 0x03, 0x4E, 0xDF, 0x65, 0x61, 0x47, 0x5B,
+			0x2F, 0x81, 0x09, 0x12, 0x9A, 0xC2, 0x24, 0x8C,
+			0x09,
+			/* CRC */
+			0x11, 0xB4, 0x06, 0x33
+		},
+		.len = 83,
+		.cipher_offset = 40,
+		.crc_offset = 6,
+		.no_cipher = false,
+		.no_crc = false
+	}
+};
+
+struct docsis_test_data docsis_test_case_8 = {
+	.key = {
+		.data = {
+			0x00, 0x00, 0x00, 0x00, 0xAA, 0xBB, 0xCC, 0xDD,
+			0xEE, 0xFF, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55
+		},
+		.len = 16
+	},
+	.cipher_iv = {
+		.data = {
+			0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+			0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
+		},
+		.len = 16
+	},
+	.plaintext = {
+		.data = {
+			/* DOCSIS header */
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			/* Ethernet frame */
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x06, 0x05,
+			0x04, 0x03, 0x02, 0x01, 0x08, 0x00,
+			/* CRC */
+			0xFF, 0xFF, 0xFF, 0xFF
+		},
+		.len = 24,
+		.cipher_offset = 18,
+		.crc_offset = 6,
+		.no_cipher = false,
+		.no_crc = true
+	},
+	.ciphertext = {
+		.data = {
+			/* DOCSIS header */
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			/* Ethernet frame */
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x06, 0x05,
+			0x04, 0x03, 0x02, 0x01, 0x7A, 0xF0,
+			/* CRC */
+			0x8A, 0x0F, 0x74, 0xE8
+		},
+		.len = 24,
+		.cipher_offset = 18,
+		.crc_offset = 6,
+		.no_cipher = false,
+		.no_crc = true
+	}
+};
+
+struct docsis_test_data docsis_test_case_9 = {
+	.key = {
+		.data = {
+			0x00, 0x00, 0x00, 0x00, 0xAA, 0xBB, 0xCC, 0xDD,
+			0xEE, 0xFF, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55
+		},
+		.len = 16
+	},
+	.cipher_iv = {
+		.data = {
+			0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+			0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
+		},
+		.len = 16
+	},
+	.plaintext = {
+		.data = {
+			/* DOCSIS header */
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			/* Ethernet frame */
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x06, 0x05,
+			0x04, 0x03, 0x02, 0x01, 0x08, 0x00, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA,
+			/* CRC */
+			0xFF, 0xFF, 0xFF, 0xFF
+		},
+		.len = 83,
+		.cipher_offset = 40,
+		.crc_offset = 6,
+		.no_cipher = false,
+		.no_crc = true
+	},
+	.ciphertext = {
+		.data = {
+			/* DOCSIS header */
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			/* Ethernet frame */
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x06, 0x05,
+			0x04, 0x03, 0x02, 0x01, 0x08, 0x00, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA, 0xAA, 0x3B, 0x9F, 0x72, 0x4C, 0xB5, 0x72,
+			0x3E, 0x56, 0x54, 0x49, 0x13, 0x53, 0xC4, 0xAA,
+			0xCD, 0xEA, 0x6A, 0x88, 0x99, 0x07, 0x86, 0xF4,
+			0xCF, 0x03, 0x4E, 0xDF, 0x65, 0x61, 0x47, 0x5B,
+			0x2F, 0x81, 0x09, 0x12, 0x9A, 0xC2, 0x24, 0x8C,
+			0x09,
+			/* CRC */
+			0x5D, 0x2B, 0x12, 0xF4
+		},
+		.len = 83,
+		.cipher_offset = 40,
+		.crc_offset = 6,
+		.no_cipher = false,
+		.no_crc = true
+	}
+};
+
+struct docsis_test_data docsis_test_case_10 = {
+	.key = {
+		.data = {
+			0x00, 0x00, 0x00, 0x00, 0xAA, 0xBB, 0xCC, 0xDD,
+			0xEE, 0xFF, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55
+		},
+		.len = 16
+	},
+	.cipher_iv = {
+		.data = {
+			0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+			0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
+		},
+		.len = 16
+	},
+	.plaintext = {
+		.data = {
+			/* DOCSIS header */
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			/* Ethernet frame */
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x06, 0x05,
+			0x04, 0x03, 0x02, 0x01, 0x08, 0x00,
+			/* CRC */
+			0xFF, 0xFF, 0xFF, 0xFF
+		},
+		.len = 24,
+		.cipher_offset = 18,
+		.crc_offset = 6,
+		.no_cipher = true,
+		.no_crc = false
+	},
+	.ciphertext = {
+		.data = {
+			/* DOCSIS header */
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			/* Ethernet frame */
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x06, 0x05,
+			0x04, 0x03, 0x02, 0x01, 0x08, 0x00,
+			/* CRC */
+			0x14, 0x08, 0xE8, 0x55
+		},
+		.len = 24,
+		.cipher_offset = 18,
+		.crc_offset = 6,
+		.no_cipher = true,
+		.no_crc = false
+	}
+};
+
+struct docsis_test_data docsis_test_case_11 = {
+	.key = {
+		.data = {
+			0x00, 0x00, 0x00, 0x00, 0xAA, 0xBB, 0xCC, 0xDD,
+			0xEE, 0xFF, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55
+		},
+		.len = 16
+	},
+	.cipher_iv = {
+		.data = {
+			0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+			0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
+		},
+		.len = 16
+	},
+	.plaintext = {
+		.data = {
+			/* DOCSIS header */
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			/* Ethernet frame */
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x06, 0x05,
+			0x04, 0x03, 0x02, 0x01, 0x08, 0x00, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA,
+			/* CRC */
+			0xFF, 0xFF, 0xFF, 0xFF
+		},
+		.len = 83,
+		.cipher_offset = 40,
+		.crc_offset = 6,
+		.no_cipher = true,
+		.no_crc = false
+	},
+	.ciphertext = {
+		.data = {
+			/* DOCSIS header */
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			/* Ethernet frame */
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x06, 0x05,
+			0x04, 0x03, 0x02, 0x01, 0x08, 0x00, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA,
+			/* CRC */
+			0xB3, 0x60, 0xEB, 0x38
+		},
+		.len = 83,
+		.cipher_offset = 40,
+		.crc_offset = 6,
+		.no_cipher = true,
+		.no_crc = false
+	}
+};
+
+struct docsis_test_data docsis_test_case_12 = {
+	.key = {
+		.data = {
+			0x00, 0x00, 0x00, 0x00, 0xAA, 0xBB, 0xCC, 0xDD,
+			0xEE, 0xFF, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55
+		},
+		.len = 16
+	},
+	.cipher_iv = {
+		.data = {
+			0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+			0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
+		},
+		.len = 16
+	},
+	.plaintext = {
+		.data = {
+			/* DOCSIS header */
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			/* Ethernet frame */
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x06, 0x05,
+			0x04, 0x03, 0x02, 0x01, 0x08, 0x00,
+			/* CRC */
+			0xFF, 0xFF, 0xFF, 0xFF
+		},
+		.len = 24,
+		.cipher_offset = 18,
+		.crc_offset = 6,
+		.no_cipher = true,
+		.no_crc = true
+	},
+	.ciphertext = {
+		.data = {
+			/* DOCSIS header */
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			/* Ethernet frame */
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x06, 0x05,
+			0x04, 0x03, 0x02, 0x01, 0x08, 0x00,
+			/* CRC */
+			0xFF, 0xFF, 0xFF, 0xFF
+		},
+		.len = 24,
+		.cipher_offset = 18,
+		.crc_offset = 6,
+		.no_cipher = true,
+		.no_crc = true
+	}
+};
+
+struct docsis_test_data docsis_test_case_13 = {
+	.key = {
+		.data = {
+			0x00, 0x00, 0x00, 0x00, 0xAA, 0xBB, 0xCC, 0xDD,
+			0xEE, 0xFF, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55
+		},
+		.len = 16
+	},
+	.cipher_iv = {
+		.data = {
+			0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+			0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
+		},
+		.len = 16
+	},
+	.plaintext = {
+		.data = {
+			/* DOCSIS header */
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			/* Ethernet frame */
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x06, 0x05,
+			0x04, 0x03, 0x02, 0x01, 0x08, 0x00, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA,
+			/* CRC */
+			0xFF, 0xFF, 0xFF, 0xFF
+		},
+		.len = 83,
+		.cipher_offset = 40,
+		.crc_offset = 6,
+		.no_cipher = true,
+		.no_crc = true
+	},
+	.ciphertext = {
+		.data = {
+			/* DOCSIS header */
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			/* Ethernet frame */
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x06, 0x05,
+			0x04, 0x03, 0x02, 0x01, 0x08, 0x00, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+			0xAA,
+			/* CRC */
+			0xFF, 0xFF, 0xFF, 0xFF
+		},
+		.len = 83,
+		.cipher_offset = 40,
+		.crc_offset = 6,
+		.no_cipher = true,
+		.no_crc = true
+	}
+};
+
+/*
+ * PON test data and cases
+ * - encrypt direction: CRC-Crypto-BIP
+ * - decrypt direction: BIP-Crypto-CRC
+ */
+struct pon_test_data {
+	struct {
+		uint8_t data[16];
+		unsigned int len;
+	} key;
+
+	struct {
+		uint8_t data[16] __rte_aligned(16);
+		unsigned int len;
+	} cipher_iv;
+
+	struct {
+		uint8_t data[1024];
+		unsigned int len;
+		unsigned int cipher_offset;
+		unsigned int crc_offset;
+		unsigned int bip_offset;
+		unsigned int padding_len;
+		bool no_cipher;
+	} plaintext;
+
+	struct {
+		uint8_t data[1024];
+		unsigned int len;
+		unsigned int cipher_offset;
+		unsigned int crc_offset;
+		unsigned int bip_offset;
+		unsigned int padding_len;
+		bool no_cipher;
+	} ciphertext;
+
+	struct {
+	uint8_t data[8];
+		unsigned int len;
+	} output;
+};
+
+struct pon_test_data pon_test_case_1 = {
+	.key = {
+		.data = {
+			0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
+			0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00
+		},
+		.len = 16
+	},
+	.cipher_iv = {
+		.data = {
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04
+		},
+		.len = 16
+	},
+	.plaintext = {
+		.data = {
+			/* XGEM header */
+			0x00, 0x20, 0x27, 0x11, 0x00, 0x00, 0x21, 0x23,
+			/* Ethernet frame */
+			0x01, 0x02, 0x03, 0x04,
+			/* CRC */
+			0x05, 0x06, 0x01, 0x01
+		},
+		.len = 16,
+		.cipher_offset = 8,
+		.crc_offset = 8,
+		.bip_offset = 0,
+		.padding_len = 0,
+		.no_cipher = false
+	},
+	.ciphertext = {
+		.data = {
+			/* XGEM header */
+			0x00, 0x20, 0x27, 0x11, 0x00, 0x00, 0x21, 0x23,
+			/* Ethernet frame */
+			0xC7, 0x62, 0x82, 0xCA,
+			/* CRC */
+			0x3E, 0x92, 0xC8, 0x5A
+		},
+		.len = 16,
+		.cipher_offset = 8,
+		.crc_offset = 8,
+		.bip_offset = 0,
+		.padding_len = 0,
+		.no_cipher = false
+	},
+	.output = {
+		/* Expected BIP */
+		.data = {0xF9, 0xD0, 0x4C, 0xA2},
+		.len  = 4
+	}
+};
+
+struct pon_test_data pon_test_case_2 = {
+	.key = {
+		.data = {
+			0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
+			0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00
+		},
+		.len = 16
+	},
+	.cipher_iv = {
+		.data = {
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04
+		},
+		.len = 16
+	},
+	.plaintext = {
+		.data = {
+			/* XGEM header */
+			0x00, 0x40, 0x27, 0x11, 0x00, 0x00, 0x29, 0x3C,
+			/* Ethernet frame */
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x01, 0x01,
+			0x01, 0x01, 0x01, 0x01,
+			/* CRC */
+			0x81, 0x00, 0x00, 0x01
+		},
+		.len = 24,
+		.cipher_offset = 8,
+		.crc_offset = 8,
+		.bip_offset = 0,
+		.padding_len = 0,
+		.no_cipher = false
+	},
+	.ciphertext = {
+		.data = {
+			/* XGEM header */
+			0x00, 0x40, 0x27, 0x11, 0x00, 0x00, 0x29, 0x3C,
+			/* Ethernet frame */
+			0xC7, 0x62, 0x82, 0xCA, 0xF6, 0x6F, 0xF5, 0xED,
+			0xB7, 0x90, 0x1E, 0x02,
+			/* CRC */
+			0xEA, 0x38, 0xA1, 0x78
+		},
+		.len = 24,
+		.cipher_offset = 8,
+		.crc_offset = 8,
+		.bip_offset = 0,
+		.padding_len = 0,
+		.no_cipher = false
+	},
+	.output = {
+		/* Expected BIP */
+		.data = {0x6C, 0xE5, 0xC6, 0x70},
+		.len  = 4
+	}
+};
+
+struct pon_test_data pon_test_case_3 = {
+	.key = {
+		.data = {
+			0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
+			0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00
+		},
+		.len = 16
+	},
+	.cipher_iv = {
+		.data = {
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04
+		},
+		.len = 16
+	},
+	.plaintext = {
+		.data = {
+			/* XGEM header */
+			0x01, 0x00, 0x27, 0x11, 0x00, 0x00, 0x33, 0x0B,
+			/* Ethernet frame */
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x01, 0x01,
+			0x01, 0x01, 0x01, 0x01, 0x81, 0x00, 0x00, 0x01,
+			0x08, 0x00, 0x45, 0x00, 0x00, 0x6A, 0xB0, 0x7E,
+			0x00, 0x00, 0x04, 0x06, 0x83, 0xBD, 0xC0, 0xA8,
+			0x00, 0x01, 0xC0, 0xA8, 0x01, 0x01, 0x04, 0xD2,
+			0x16, 0x2E, 0x12, 0x34, 0x56, 0x78, 0x12, 0x34,
+			0x56, 0x90, 0x50, 0x10, 0x20, 0x00, 0xA6, 0x33,
+			0x00, 0x00, 0x30, 0x31,
+			/* CRC */
+			0x32, 0x33, 0x34, 0x35
+		},
+		.len = 72,
+		.cipher_offset = 8,
+		.crc_offset = 8,
+		.bip_offset = 0,
+		.padding_len = 0,
+		.no_cipher = false
+	},
+	.ciphertext = {
+		.data = {
+			/* XGEM header */
+			0x01, 0x00, 0x27, 0x11, 0x00, 0x00, 0x33, 0x0B,
+			/* Ethernet frame */
+			0xC7, 0x62, 0x82, 0xCA, 0xF6, 0x6F, 0xF5, 0xED,
+			0xB7, 0x90, 0x1E, 0x02, 0x6B, 0x2C, 0x08, 0x7D,
+			0x3C, 0x90, 0xE8, 0x2C, 0x44, 0x30, 0x03, 0x29,
+			0x5F, 0x88, 0xA9, 0xD6, 0x1E, 0xF9, 0xD1, 0xF1,
+			0xD6, 0x16, 0x8C, 0x72, 0xA4, 0xCD, 0xD2, 0x8F,
+			0x63, 0x26, 0xC9, 0x66, 0xB0, 0x65, 0x24, 0x9B,
+			0x60, 0x5B, 0x18, 0x60, 0xBD, 0xD5, 0x06, 0x13,
+			0x40, 0xC9, 0x60, 0x64,
+			/* CRC */
+			0x36, 0x5F, 0x86, 0x8C
+		},
+		.len = 72,
+		.cipher_offset = 8,
+		.crc_offset = 8,
+		.bip_offset = 0,
+		.padding_len = 0,
+		.no_cipher = false
+	},
+	.output = {
+		/* Expected BIP */
+		.data = {0xDF, 0xE0, 0xAD, 0xFB},
+		.len  = 4
+	}
+};
+
+struct pon_test_data pon_test_case_4 = {
+	.key = {
+		.data = {
+			0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
+			0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00
+		},
+		.len = 16
+	},
+	.cipher_iv = {
+		.data = {
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+		},
+		.len = 16
+	},
+	.plaintext = {
+		.data = {
+			/* XGEM header */
+			0x00, 0x39, 0x03, 0xFD, 0x00, 0x00, 0xB3, 0x6A,
+			/* Ethernet frame */
+			0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+			0x10, 0x11,
+			/* CRC */
+			0x20, 0x21, 0x22, 0x23,
+			/* Padding */
+			0x55, 0x55
+		},
+		.len = 24,
+		.cipher_offset = 8,
+		.crc_offset = 8,
+		.bip_offset = 0,
+		.padding_len = 2,
+		.no_cipher = false
+	},
+	.ciphertext = {
+		.data = {
+			/* XGEM header */
+			0x00, 0x39, 0x03, 0xFD, 0x00, 0x00, 0xB3, 0x6A,
+			/* Ethernet frame */
+			0x73, 0xE0, 0x5D, 0x5D, 0x32, 0x9C, 0x3B, 0xFA,
+			0x6B, 0x66,
+			/* CRC */
+			0xF6, 0x8E, 0x5B, 0xD5,
+			/* Padding */
+			0xAB, 0xCD
+		},
+		.len = 24,
+		.cipher_offset = 8,
+		.crc_offset = 8,
+		.bip_offset = 0,
+		.padding_len = 2,
+		.no_cipher = false
+	},
+	.output = {
+		/* Expected BIP */
+		.data = {0x71, 0xF6, 0x8B, 0x73},
+		.len  = 4
+	}
+};
+
+struct pon_test_data pon_test_case_5 = {
+	.key = {
+		.data = {
+			0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
+			0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00
+		},
+		.len = 16
+	},
+	.cipher_iv = {
+		.data = {
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+		},
+		.len = 16
+	},
+	.plaintext = {
+		.data = {
+			/* XGEM header */
+			0x00, 0x05, 0x03, 0xFD, 0x00, 0x00, 0xB9, 0xB4,
+			/* Ethernet frame */
+			0x08,
+			/* Padding */
+			0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55
+		},
+		.len = 16,
+		.cipher_offset = 8,
+		.crc_offset = 8,
+		.bip_offset = 0,
+		.padding_len = 7,
+		.no_cipher = false
+	},
+	.ciphertext = {
+		.data = {
+			/* XGEM header */
+			0x00, 0x05, 0x03, 0xFD, 0x00, 0x00, 0xB9, 0xB4,
+			/* Ethernet frame */
+			0x73,
+			/* Padding */
+			0xBC, 0x02, 0x03, 0x6B, 0xC4, 0x60, 0xA0
+		},
+		.len = 16,
+		.cipher_offset = 8,
+		.crc_offset = 8,
+		.bip_offset = 0,
+		.padding_len = 7,
+		.no_cipher = false
+	},
+	.output = {
+		/* Expected BIP */
+		.data = {0x18, 0x7D, 0xD8, 0xEA},
+		.len  = 4
+	}
+};
+
+struct pon_test_data pon_test_case_6 = {
+	.key = {
+		.data = {
+			0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
+			0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00
+		},
+		.len = 16
+	},
+	.cipher_iv = {
+		.data = {
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+		},
+		.len = 16
+	},
+	.plaintext = {
+		.data = {
+			/* XGEM header */
+			0x01, 0x00, 0x27, 0x11, 0x00, 0x00, 0x33, 0x0B,
+			/* Ethernet frame */
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x01, 0x01,
+			0x01, 0x01, 0x01, 0x01, 0x81, 0x00, 0x00, 0x01,
+			0x08, 0x00, 0x45, 0x00, 0x00, 0x6A, 0xB0, 0x7E,
+			0x00, 0x00, 0x04, 0x06, 0x83, 0xBD, 0xC0, 0xA8,
+			0x00, 0x01, 0xC0, 0xA8, 0x01, 0x01, 0x04, 0xD2,
+			0x16, 0x2E, 0x12, 0x34, 0x56, 0x78, 0x12, 0x34,
+			0x56, 0x90, 0x50, 0x10, 0x20, 0x00, 0xA6, 0x33,
+			0x00, 0x00, 0x30, 0x31,
+			/* CRC */
+			0x32, 0x33, 0x34, 0x35
+		},
+		.len = 72,
+		.cipher_offset = 8,
+		.crc_offset = 8,
+		.bip_offset = 0,
+		.padding_len = 0,
+		.no_cipher = true
+	},
+	.ciphertext = {
+		.data = {
+			/* XGEM header */
+			0x01, 0x00, 0x27, 0x11, 0x00, 0x00, 0x33, 0x0B,
+			/* Ethernet frame */
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x01, 0x01,
+			0x01, 0x01, 0x01, 0x01, 0x81, 0x00, 0x00, 0x01,
+			0x08, 0x00, 0x45, 0x00, 0x00, 0x6a, 0xb0, 0x7e,
+			0x00, 0x00, 0x04, 0x06, 0x83, 0xbd, 0xc0, 0xa8,
+			0x00, 0x01, 0xc0, 0xa8, 0x01, 0x01, 0x04, 0xd2,
+			0x16, 0x2e, 0x12, 0x34, 0x56, 0x78, 0x12, 0x34,
+			0x56, 0x90, 0x50, 0x10, 0x20, 0x00, 0xa6, 0x33,
+			0x00, 0x00, 0x30, 0x31,
+			/* CRC */
+			0x53, 0xC1, 0xE6, 0x0C
+		},
+		.len = 72,
+		.cipher_offset = 8,
+		.crc_offset = 8,
+		.bip_offset = 0,
+		.padding_len = 0,
+		.no_cipher = true
+	},
+	.output = {
+		/* Expected BIP */
+		.data = {0x6A, 0xD5, 0xC2, 0xAB},
+		.len  = 4
+	}
+};
+#endif /* _AESNI_MB_RAWDEV_TEST_VECTORS_H_ */
diff --git a/drivers/raw/aesni_mb/meson.build b/drivers/raw/aesni_mb/meson.build
new file mode 100644
index 000000000..085f629be
--- /dev/null
+++ b/drivers/raw/aesni_mb/meson.build
@@ -0,0 +1,26 @@ 
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2020 Intel Corporation.
+
+IMB_required_ver = '0.53.3-dev'
+lib = cc.find_library('IPSec_MB', required: false)
+if not lib.found()
+	build = false
+	reason = 'missing dependency, "libIPSec_MB"'
+else
+	ext_deps += lib
+
+	# version comes with quotes, so we split based on " and take the middle
+	imb_ver = cc.get_define('IMB_VERSION_STR',
+		prefix : '#include<intel-ipsec-mb.h>').split('"')[1]
+
+	if (imb_ver == '') or (imb_ver.version_compare('<' + IMB_required_ver))
+		reason = 'IPSec_MB version >= @0@ is required, found version @1@'.format(
+				IMB_required_ver, imb_ver)
+		build = false
+	endif
+
+endif
+
+sources = files('aesni_mb_rawdev.c', 'aesni_mb_rawdev_test.c')
+allow_experimental_apis = true
+deps += ['bus_vdev', 'net', 'rawdev', 'cryptodev', 'common_multi_fn']
diff --git a/drivers/raw/aesni_mb/rte_rawdev_aesni_mb_version.map b/drivers/raw/aesni_mb/rte_rawdev_aesni_mb_version.map
new file mode 100644
index 000000000..fa9e17c29
--- /dev/null
+++ b/drivers/raw/aesni_mb/rte_rawdev_aesni_mb_version.map
@@ -0,0 +1,3 @@ 
+DPDK_20.0 {
+	local: *;
+};
\ No newline at end of file
diff --git a/drivers/raw/meson.build b/drivers/raw/meson.build
index bb5797760..c0bbc84a6 100644
--- a/drivers/raw/meson.build
+++ b/drivers/raw/meson.build
@@ -5,7 +5,8 @@  drivers = ['dpaa2_cmdif', 'dpaa2_qdma',
 	'ifpga', 'ioat', 'ntb',
 	'octeontx2_dma',
 	'octeontx2_ep',
-	'skeleton']
+	'skeleton',
+	'aesni_mb']
 std_deps = ['rawdev']
 config_flag_fmt = 'RTE_LIBRTE_PMD_@0@_RAWDEV'
 driver_name_fmt = 'rte_rawdev_@0@'
diff --git a/mk/rte.app.mk b/mk/rte.app.mk
index b836d220d..a6e1e925f 100644
--- a/mk/rte.app.mk
+++ b/mk/rte.app.mk
@@ -347,6 +347,8 @@  _LDLIBS-$(CONFIG_RTE_LIBRTE_PMD_IOAT_RAWDEV)   += -lrte_rawdev_ioat
 _LDLIBS-$(CONFIG_RTE_LIBRTE_PMD_NTB_RAWDEV) += -lrte_rawdev_ntb
 _LDLIBS-$(CONFIG_RTE_LIBRTE_PMD_OCTEONTX2_DMA_RAWDEV) += -lrte_rawdev_octeontx2_dma
 _LDLIBS-$(CONFIG_RTE_LIBRTE_PMD_OCTEONTX2_EP_RAWDEV) += -lrte_rawdev_octeontx2_ep
+_LDLIBS-$(CONFIG_RTE_LIBRTE_PMD_AESNI_MB_RAWDEV) += -lrte_pmd_aesni_mb_rawdev
+_LDLIBS-$(CONFIG_RTE_LIBRTE_PMD_AESNI_MB_RAWDEV) += -lIPSec_MB
 _LDLIBS-$(CONFIG_RTE_LIBRTE_MULTI_FN_COMMON) += -lrte_multi_fn
 endif # CONFIG_RTE_LIBRTE_RAWDEV