+ return m;
+}
+
+/**
+ * Allocate a bulk of mbufs, initialize refcnt and reset the fields to default
+ * values.
+ *
+ * @param pool
+ * The mempool from which mbufs are allocated.
+ * @param mbufs
+ * Array of pointers to mbufs
+ * @param count
+ * Array size
+ * @return
+ * - 0: Success
+ * - -ENOENT: Not enough entries in the mempool; no mbufs are retrieved.
+ */
+static inline int rte_pktmbuf_alloc_bulk(struct rte_mempool *pool,
+ struct rte_mbuf **mbufs, unsigned count)
+{
+ unsigned idx = 0;
+ int rc;
+
+ rc = rte_mempool_get_bulk(pool, (void **)mbufs, count);
+ if (unlikely(rc))
+ return rc;
+
+ /* To understand duff's device on loop unwinding optimization, see
+ * https://en.wikipedia.org/wiki/Duff's_device.
+ * Here while() loop is used rather than do() while{} to avoid extra
+ * check if count is zero.
+ */
+ switch (count % 4) {
+ case 0:
+ while (idx != count) {
+ MBUF_RAW_ALLOC_CHECK(mbufs[idx]);
+ rte_pktmbuf_reset(mbufs[idx]);
+ idx++;
+ /* fall-through */
+ case 3:
+ MBUF_RAW_ALLOC_CHECK(mbufs[idx]);
+ rte_pktmbuf_reset(mbufs[idx]);
+ idx++;
+ /* fall-through */
+ case 2:
+ MBUF_RAW_ALLOC_CHECK(mbufs[idx]);
+ rte_pktmbuf_reset(mbufs[idx]);
+ idx++;
+ /* fall-through */
+ case 1:
+ MBUF_RAW_ALLOC_CHECK(mbufs[idx]);
+ rte_pktmbuf_reset(mbufs[idx]);
+ idx++;
+ /* fall-through */
+ }
+ }
+ return 0;
+}
+
+/**
+ * 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.
+ *
+ * @warning
+ * @b EXPERIMENTAL: This API may change without prior notice.
+ * Once external buffer is enabled by allowing experimental API,
+ * ``RTE_MBUF_DIRECT()`` and ``RTE_MBUF_INDIRECT()`` are no longer
+ * exclusive. A mbuf can be considered direct if it is neither indirect nor
+ * having external buffer.
+ *
+ * @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_experimental
+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;