+/*
+ * @internal The callback routine called when reference counter in shinfo
+ * for mbufs with pinned external buffer reaches zero. It means there is
+ * no more reference to buffer backing mbuf and this one should be freed.
+ * This routine is called for the regular (not with pinned external or
+ * indirect buffer) mbufs on detaching from the mbuf with pinned external
+ * buffer.
+ */
+static void
+rte_pktmbuf_free_pinned_extmem(void *addr, void *opaque)
+{
+ struct rte_mbuf *m = opaque;
+
+ RTE_SET_USED(addr);
+ RTE_ASSERT(RTE_MBUF_HAS_EXTBUF(m));
+ RTE_ASSERT(RTE_MBUF_HAS_PINNED_EXTBUF(m));
+ RTE_ASSERT(m->shinfo->fcb_opaque == m);
+
+ rte_mbuf_ext_refcnt_set(m->shinfo, 1);
+ m->ol_flags = EXT_ATTACHED_MBUF;
+ if (m->next != NULL)
+ m->next = NULL;
+ if (m->nb_segs != 1)
+ m->nb_segs = 1;
+ rte_mbuf_raw_free(m);
+}
+
+/** The context to initialize the mbufs with pinned external buffers. */
+struct rte_pktmbuf_extmem_init_ctx {
+ const struct rte_pktmbuf_extmem *ext_mem; /* descriptor array. */
+ unsigned int ext_num; /* number of descriptors in array. */
+ unsigned int ext; /* loop descriptor index. */
+ size_t off; /* loop buffer offset. */
+};
+
+/**
+ * @internal Packet mbuf constructor for pools with pinned external memory.
+ *
+ * This function initializes some fields in the mbuf structure that are
+ * not modified by the user once created (origin pool, buffer start
+ * address, and so on). This function is given as a callback function to
+ * rte_mempool_obj_iter() called from rte_mempool_create_extmem().
+ *
+ * @param mp
+ * The mempool from which mbufs originate.
+ * @param opaque_arg
+ * A pointer to the rte_pktmbuf_extmem_init_ctx - initialization
+ * context structure
+ * @param m
+ * The mbuf to initialize.
+ * @param i
+ * The index of the mbuf in the pool table.
+ */
+static void
+__rte_pktmbuf_init_extmem(struct rte_mempool *mp,
+ void *opaque_arg,
+ void *_m,
+ __rte_unused unsigned int i)
+{
+ struct rte_mbuf *m = _m;
+ struct rte_pktmbuf_extmem_init_ctx *ctx = opaque_arg;
+ const struct rte_pktmbuf_extmem *ext_mem;
+ uint32_t mbuf_size, buf_len, priv_size;
+ struct rte_mbuf_ext_shared_info *shinfo;
+
+ priv_size = rte_pktmbuf_priv_size(mp);
+ mbuf_size = sizeof(struct rte_mbuf) + priv_size;
+ buf_len = rte_pktmbuf_data_room_size(mp);
+
+ RTE_ASSERT(RTE_ALIGN(priv_size, RTE_MBUF_PRIV_ALIGN) == priv_size);
+ RTE_ASSERT(mp->elt_size >= mbuf_size);
+ RTE_ASSERT(buf_len <= UINT16_MAX);
+
+ memset(m, 0, mbuf_size);
+ m->priv_size = priv_size;
+ m->buf_len = (uint16_t)buf_len;
+
+ /* set the data buffer pointers to external memory */
+ ext_mem = ctx->ext_mem + ctx->ext;
+
+ RTE_ASSERT(ctx->ext < ctx->ext_num);
+ RTE_ASSERT(ctx->off + ext_mem->elt_size <= ext_mem->buf_len);
+
+ m->buf_addr = RTE_PTR_ADD(ext_mem->buf_ptr, ctx->off);
+ m->buf_iova = ext_mem->buf_iova == RTE_BAD_IOVA ?
+ RTE_BAD_IOVA : (ext_mem->buf_iova + ctx->off);
+
+ ctx->off += ext_mem->elt_size;
+ if (ctx->off + ext_mem->elt_size > ext_mem->buf_len) {
+ ctx->off = 0;
+ ++ctx->ext;
+ }
+ /* keep some headroom between start of buffer and data */
+ m->data_off = RTE_MIN(RTE_PKTMBUF_HEADROOM, (uint16_t)m->buf_len);
+
+ /* init some constant fields */
+ m->pool = mp;
+ m->nb_segs = 1;
+ m->port = RTE_MBUF_PORT_INVALID;
+ m->ol_flags = EXT_ATTACHED_MBUF;
+ rte_mbuf_refcnt_set(m, 1);
+ m->next = NULL;
+
+ /* init external buffer shared info items */
+ shinfo = RTE_PTR_ADD(m, mbuf_size);
+ m->shinfo = shinfo;
+ shinfo->free_cb = rte_pktmbuf_free_pinned_extmem;
+ shinfo->fcb_opaque = m;
+ rte_mbuf_ext_refcnt_set(shinfo, 1);
+}
+
+/* Helper to create a mbuf pool with given mempool ops name*/