+/**
+ * Initialize shared data at the end of an external buffer before attaching
+ * to a mbuf by ``rte_pktmbuf_attach_extbuf()``. This is not a mandatory
+ * initialization but a helper function to simply spare a few bytes at the
+ * end of the buffer for shared data. If shared data is allocated
+ * separately, this should not be called but application has to properly
+ * initialize the shared data according to its need.
+ *
+ * Free callback and its argument is saved and the refcnt is set to 1.
+ *
+ * @warning
+ * The value of buf_len will be reduced to RTE_PTR_DIFF(shinfo, buf_addr)
+ * after this initialization. This shall be used for
+ * ``rte_pktmbuf_attach_extbuf()``
+ *
+ * @param buf_addr
+ * The pointer to the external buffer.
+ * @param [in,out] buf_len
+ * The pointer to length of the external buffer. Input value must be
+ * larger than the size of ``struct rte_mbuf_ext_shared_info`` and
+ * padding for alignment. If not enough, this function will return NULL.
+ * Adjusted buffer length will be returned through this pointer.
+ * @param free_cb
+ * Free callback function to call when the external buffer needs to be
+ * freed.
+ * @param fcb_opaque
+ * Argument for the free callback function.
+ *
+ * @return
+ * A pointer to the initialized shared data on success, return NULL
+ * otherwise.
+ */
+static inline struct rte_mbuf_ext_shared_info *
+rte_pktmbuf_ext_shinfo_init_helper(void *buf_addr, uint16_t *buf_len,
+ rte_mbuf_extbuf_free_callback_t free_cb, void *fcb_opaque)
+{
+ struct rte_mbuf_ext_shared_info *shinfo;
+ void *buf_end = RTE_PTR_ADD(buf_addr, *buf_len);
+ void *addr;
+
+ addr = RTE_PTR_ALIGN_FLOOR(RTE_PTR_SUB(buf_end, sizeof(*shinfo)),
+ sizeof(uintptr_t));
+ if (addr <= buf_addr)
+ return NULL;
+
+ shinfo = (struct rte_mbuf_ext_shared_info *)addr;
+ shinfo->free_cb = free_cb;
+ shinfo->fcb_opaque = fcb_opaque;
+ rte_mbuf_ext_refcnt_set(shinfo, 1);
+
+ *buf_len = (uint16_t)RTE_PTR_DIFF(shinfo, buf_addr);
+ return shinfo;
+}
+
+/**
+ * Attach an external buffer to a mbuf.
+ *
+ * User-managed anonymous buffer can be attached to an mbuf. When attaching
+ * it, corresponding free callback function and its argument should be
+ * provided via shinfo. This callback function will be called once all the
+ * mbufs are detached from the buffer (refcnt becomes zero).
+ *
+ * The headroom for the attaching mbuf will be set to zero and this can be
+ * properly adjusted after attachment. For example, ``rte_pktmbuf_adj()``
+ * or ``rte_pktmbuf_reset_headroom()`` might be used.
+ *
+ * More mbufs can be attached to the same external buffer by
+ * ``rte_pktmbuf_attach()`` once the external buffer has been attached by
+ * this API.
+ *
+ * Detachment can be done by either ``rte_pktmbuf_detach_extbuf()`` or
+ * ``rte_pktmbuf_detach()``.
+ *
+ * Memory for shared data must be provided and user must initialize all of
+ * the content properly, escpecially free callback and refcnt. The pointer
+ * of shared data will be stored in m->shinfo.
+ * ``rte_pktmbuf_ext_shinfo_init_helper`` can help to simply spare a few
+ * bytes at the end of buffer for the shared data, store free callback and
+ * its argument and set the refcnt to 1. The following is an example:
+ *
+ * struct rte_mbuf_ext_shared_info *shinfo =
+ * rte_pktmbuf_ext_shinfo_init_helper(buf_addr, &buf_len,
+ * free_cb, fcb_arg);
+ * rte_pktmbuf_attach_extbuf(m, buf_addr, buf_iova, buf_len, shinfo);
+ * rte_pktmbuf_reset_headroom(m);
+ * rte_pktmbuf_adj(m, data_len);
+ *
+ * Attaching an external buffer is quite similar to mbuf indirection in
+ * replacing buffer addresses and length of a mbuf, but a few differences:
+ * - When an indirect mbuf is attached, refcnt of the direct mbuf would be
+ * 2 as long as the direct mbuf itself isn't freed after the attachment.
+ * In such cases, the buffer area of a direct mbuf must be read-only. But
+ * external buffer has its own refcnt and it starts from 1. Unless
+ * multiple mbufs are attached to a mbuf having an external buffer, the
+ * external buffer is writable.
+ * - There's no need to allocate buffer from a mempool. Any buffer can be
+ * attached with appropriate free callback and its IO address.
+ * - Smaller metadata is required to maintain shared data such as refcnt.
+ *
+ * @param m
+ * The pointer to the mbuf.
+ * @param buf_addr
+ * The pointer to the external buffer.
+ * @param buf_iova
+ * IO address of the external buffer.
+ * @param buf_len
+ * The size of the external buffer.
+ * @param shinfo
+ * User-provided memory for shared data of the external buffer.
+ */
+static inline void
+rte_pktmbuf_attach_extbuf(struct rte_mbuf *m, void *buf_addr,
+ rte_iova_t buf_iova, uint16_t buf_len,
+ struct rte_mbuf_ext_shared_info *shinfo)
+{
+ /* mbuf should not be read-only */
+ RTE_ASSERT(RTE_MBUF_DIRECT(m) && rte_mbuf_refcnt_read(m) == 1);
+ RTE_ASSERT(shinfo->free_cb != NULL);
+
+ m->buf_addr = buf_addr;
+ m->buf_iova = buf_iova;
+ m->buf_len = buf_len;
+
+ m->data_len = 0;
+ m->data_off = 0;
+
+ m->ol_flags |= EXT_ATTACHED_MBUF;
+ m->shinfo = shinfo;
+}
+
+/**
+ * Detach the external buffer attached to a mbuf, same as
+ * ``rte_pktmbuf_detach()``
+ *
+ * @param m
+ * The mbuf having external buffer.
+ */
+#define rte_pktmbuf_detach_extbuf(m) rte_pktmbuf_detach(m)
+