From mboxrd@z Thu Jan  1 00:00:00 1970
Return-Path: <yskoh@mellanox.com>
Received: from EUR01-VE1-obe.outbound.protection.outlook.com
 (mail-ve1eur01on0061.outbound.protection.outlook.com [104.47.1.61])
 by dpdk.org (Postfix) with ESMTP id 1B2EE1B1B9
 for <dev@dpdk.org>; Mon,  2 Apr 2018 20:50:31 +0200 (CEST)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=Mellanox.com;
 s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version;
 bh=RrInTk2xAEYNbbiobq+rgbkdsp6CkbEuzNe9RGWd1Y0=;
 b=dt9DeH8ltl6t0crv6YWC5tBqRACawC+yTXsxcf3RgguMCnOcgCXE59ijQBc2qrOBbanr38QQpGLkpxC2iqzX81e/p9nU+D4Oi54+UaUuTnzjrjbnmZ8ICzkD4aflW3pbmZO4JQkm0+8swKtCKgXc4djbmcwHdmvYDF2sBqX20PQ=
Received: from mellanox.com (209.116.155.178) by
 AM5PR0501MB2036.eurprd05.prod.outlook.com (2603:10a6:203:1a::22) with
 Microsoft SMTP Server (version=TLS1_2,
 cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.631.10; Mon, 2 Apr
 2018 18:50:28 +0000
From: Yongseok Koh <yskoh@mellanox.com>
To: wenzhuo.lu@intel.com, jingjing.wu@intel.com, adrien.mazarguil@6wind.com,
 nelio.laranjeiro@6wind.com, olivier.matz@6wind.com
Cc: dev@dpdk.org,
	Yongseok Koh <yskoh@mellanox.com>
Date: Mon,  2 Apr 2018 11:50:03 -0700
Message-Id: <20180402185008.13073-2-yskoh@mellanox.com>
X-Mailer: git-send-email 2.11.0
In-Reply-To: <20180402185008.13073-1-yskoh@mellanox.com>
References: <20180310012532.15809-1-yskoh@mellanox.com>
 <20180402185008.13073-1-yskoh@mellanox.com>
MIME-Version: 1.0
Content-Type: text/plain
X-Originating-IP: [209.116.155.178]
X-ClientProxiedBy: DM3PR12CA0064.namprd12.prod.outlook.com
 (2603:10b6:0:56::32) To AM5PR0501MB2036.eurprd05.prod.outlook.com
 (2603:10a6:203:1a::22)
X-MS-PublicTrafficType: Email
X-MS-Office365-Filtering-HT: Tenant
X-MS-Office365-Filtering-Correlation-Id: 4e37fc69-c74a-4bbb-a877-08d598ca9b6a
X-Microsoft-Antispam: UriScan:; BCL:0; PCL:0;
 RULEID:(7020095)(4652020)(48565401081)(5600026)(4604075)(4534165)(4627221)(201703031133081)(201702281549075)(2017052603328)(7153060)(7193020);
 SRVR:AM5PR0501MB2036; 
X-Microsoft-Exchange-Diagnostics: 1; AM5PR0501MB2036;
 3:SAjlbuvvhuZ8tSKnDwF5AWTrghyLouNAxU12ZpC9H7Uvjx5aH1HJ6yCeBjx4iIDzJJ7qpwVuEasrq26awpIdBCXiKpcQtk6Pn+K7sHLzsFBpufvviyUoesuwhmchoiTXsaGQSJBgndSfEp63o8Tc+02EuKEEE0AFu9KJzFI4dDrUoERRnKC8AU4wgzqzYPK6afk542WO6yvYOwbSqu/CauxiryyO5s27I6ePh0gFQucwPIP4U9Fhshk5kwRMIWKW;
 25:plN5hJX9o6qdn8AYKqVDbE9i1NA+SRdHQe/AsFg1SLGBs9p14OTLOUb46MIFd0zvjn7JFnoiwqVlnXNc/AfljctUt/1tFBL4wgFWnNrerJDKlqL9594P7gceouh/+3s/KhFbPrE5l1+Fgey063lDIaB8sYvwuNg2dxr9zRyclRz96efVSgNhl3aYd9lK5IZ1NquD7v/4afL+Z4JLaTk53OL0d9p2xL4V/vjcEWwAMw64YoRCyRJtukZlDvYIWx5LBNpEbpNcK1jMtq6aSmgIu+X45mM9vdzvwq+vHaKoX3N3z3zs71EiYtxphPya+ySCk5TEX4QMo0+bixWeV4BgHQ==;
 31:MBxvD/+dmCInSJsWP98NO/6HTERap+EezafB6qCwUXyGZlZIJNa4JTjHH30NEjpQMEiEf3RoXRvhgycPnHDfpcegawbOrFV9GGEhQXLscywRv53Nd++hbqlLf7ypyvxRRW5+LqTNPow4zfyHYVrM4llUEtU9H1osmk1M2ywjt0xlVMpy2Tp1+SoMlJPFwxKqZFcakwaZsXyPBvMAiz+EBHo3Uy0p5xeiUhM7IIpkeNE=
X-MS-TrafficTypeDiagnostic: AM5PR0501MB2036:
X-LD-Processed: a652971c-7d2e-4d9b-a6a4-d149256f461b,ExtAddr
X-Microsoft-Exchange-Diagnostics: 1; AM5PR0501MB2036;
 20:euBx5NL5HOG6SOkE4MRg9OpRjPXsy0bIfFyHIbdaJINhryGLA0sTeFjdjvlU8i79fXwq8V6rNa7nEnRw9+GGKnTHBcMWcOURfxI862hJkTJEbh++vLqDZJFXNxfSpktfO11JXT/7OSaUMG66KGAw4otN0sL13Y6g4KsNbWel2hmHA0oIv9PY27k5H4N4oPWGAcrZ9KqpDdOUGzl5WRB2iRLyb/lbqoU0bm39KjIRF5K79VIAhD9enVRb4e5yO4eKctYSr8XUKzp0rw/cCTFX9FQjF4H7xQcwLMj8el59sw79uh2EOtO3KkRE8Sis3xPYPKD5amiXNfLrY+uEqU9vONlxO1b7eHsfsVmgCVp/rcFIFWS2N1xd3hSlCafGRQwyTdntZyJhwfwV2AtrrqCXFd2oOYqh3dYu8ofimMbrv9FydJiBI1RnRE0j97/Xb/zPzn5Fueo8Hj85k9QFpqr7t624lrapume2wZQJIScyFCiT61ePbbcYp7R45oqhhrfV;
 4:urW/1Z/jWFZiscAJo1Dcn2o15XyeWdO8Q//zfLyiu3Si2PyifZAWaINH5taSwdZ/tb4y41EFZMfPpNpKTjfbdRuz42X04y6lQnLPugDgMgO9u6U2ESwZIkmV9ISgCajMe8fqjKFfGWh7P6bBfYYfpuI4GR4nld8QtJLyQYRNu+ywpslMV9VQ9WBm5Wtsvb62Q4cKqpdzZNZiEoVGx3AYG5/I9z4PblMk07erQoI0rFNHh3hyd9eL4rUAlt4a54lksdPsGFD0/RDEiU7nVs46XQ==
X-Microsoft-Antispam-PRVS: <AM5PR0501MB20365534FE632D17B7E9D698C3A60@AM5PR0501MB2036.eurprd05.prod.outlook.com>
X-Exchange-Antispam-Report-Test: UriScan:;
X-Exchange-Antispam-Report-CFA-Test: BCL:0; PCL:0;
 RULEID:(8211001083)(6040522)(2401047)(8121501046)(5005006)(10201501046)(93006095)(93001095)(3231221)(944501327)(52105095)(3002001)(6055026)(6041310)(201703131423095)(201702281528075)(20161123555045)(201703061421075)(201703061406153)(20161123564045)(20161123562045)(20161123560045)(20161123558120)(6072148)(201708071742011);
 SRVR:AM5PR0501MB2036; BCL:0; PCL:0; RULEID:; SRVR:AM5PR0501MB2036; 
X-Forefront-PRVS: 0630013541
X-Forefront-Antispam-Report: SFV:NSPM;
 SFS:(10009020)(39380400002)(39860400002)(366004)(396003)(346002)(376002)(189003)(199004)(25786009)(106356001)(7696005)(81166006)(956004)(5660300001)(76176011)(16526019)(26005)(8936002)(186003)(51416003)(478600001)(47776003)(69596002)(66066001)(50466002)(105586002)(476003)(5890100001)(316002)(486005)(446003)(6346003)(21086003)(81156014)(52116002)(8676002)(16586007)(86362001)(2616005)(50226002)(68736007)(386003)(59450400001)(11346002)(486005)(107886003)(3846002)(1076002)(2906002)(6116002)(55016002)(36756003)(7736002)(53936002)(4326008)(48376002)(97736004)(305945005)(6666003);
 DIR:OUT; SFP:1101; SCL:1; SRVR:AM5PR0501MB2036; H:mellanox.com; FPR:; SPF:None;
 LANG:en; PTR:InfoNoRecords; MX:1; A:1; 
Received-SPF: None (protection.outlook.com: mellanox.com does not designate
 permitted sender hosts)
Authentication-Results: spf=none (sender IP is )
 smtp.mailfrom=yskoh@mellanox.com; 
X-Microsoft-Exchange-Diagnostics: =?us-ascii?Q?1; AM5PR0501MB2036;
 23:RSiTHGie9axVApB3JKUcD6RRubugYCHIJlFGXjZ?=
 =?us-ascii?Q?dgmdbi8AX8H4sBRI3/UhPtGJyzfYpqBgA31XH08wPDY+kSw+BJ8Aagqhe88x?=
 =?us-ascii?Q?1D/TJNkTJWqs4WeJLVmkMdVp4mUeCD9yPdFAW/tjEs4bdDIP4hLTEM7Oztym?=
 =?us-ascii?Q?CvWcRMmkJN32iv2Qbbbr+IHGR1vrtneG+z7SHi2XaJEetckLEXV4hLRBAtWy?=
 =?us-ascii?Q?2NgP2RtvVng1u44CwWyhe5tImhxsy8d4LvT4rllCUZ1spzHddP2AVaucOZZX?=
 =?us-ascii?Q?wzOh8yXENuNQMGb6LOuJ32uqbsN+daOXRg6topZZLfg6YOwJFgcHz2lNoamU?=
 =?us-ascii?Q?wldCOTrLFer1DCI7hxZv6lQ1lw4SO9RJ99rWfAH6g3wH1bxgYCiWy3egn8ur?=
 =?us-ascii?Q?lyatMFEg9Oe7i5K3mlBU82HN840wLIZgWuin0woFwrB8sPUV6IGmBgilXtGV?=
 =?us-ascii?Q?PT4Jj0fFwkvNHoEeqLTPVZJLmKDhHXJviPsptKs4dgSu/h6mrXRnR2HM0s71?=
 =?us-ascii?Q?yOl4sq721TU1vcIfEGWLqH8cJs32Tr7t6tJNmJfwLzo+F4cUSZntOh/Jd7Fr?=
 =?us-ascii?Q?TU/JlInOVR/iZwffoWeaSLHuFT00YSBS79hvTQPb28ATp6re0fIs8jXg2Nci?=
 =?us-ascii?Q?tVNlxdEGg69c6Ii4n8skMDjxrisEJa0dZevT/3Vg7akjAIBuS9Iprr5lQOTS?=
 =?us-ascii?Q?67hF7A6Qn2ywtBq22AVmvE9cJSYVsR7JmGhpFe2M7ENRcreyLpHoQxhzSzxd?=
 =?us-ascii?Q?ZRelRLhEm6QRR5TK/b+MviH+cxxaQKAfDSjWgdSDX8KjVhcPQB3MMstjRgLc?=
 =?us-ascii?Q?+ZT9wz6F2D+/k+S5gIL50exjNlD8zTqg2Ti3NjeeyXn2bEqdA+mCLlXDI5St?=
 =?us-ascii?Q?cpTsg7ZT1ILXZZtBoW7P/ZZ1xrlLjdqr+rxSI+ERnud1ZBIrPp/10MQ8RcDj?=
 =?us-ascii?Q?2XGKcup2WPcmSHUJ2Hf/EW6wjt0svSxOwLyqpQ98WnpRetnh8URsqges31CM?=
 =?us-ascii?Q?Fm8lhT2h27zUrkKY/cqJeTDXfanjPc1Cy+MXHtl6xpHeJvhrVQiuEYXl2MiZ?=
 =?us-ascii?Q?xiqHaO5fenYzzLiJ+59S1Sc/L12jx1voWIPQt31f5KcnRmyvdyeADe4mj9Ha?=
 =?us-ascii?Q?Ll7UOWwiI2XzHXbIIvf4kKs+eaayFsa97Mrursw2oItkEU1gHcOCQ5uv7NDY?=
 =?us-ascii?Q?YnhmUNW/mHEkawhQNkR1Ut7/+sYR/BaCohQdkeWiuQHCaAc8mF18bZRBq6oi?=
 =?us-ascii?Q?xagRfbvQnExUMGPSM27H1jqOnX/ACLTvPiKWm7VDU0lm+FnG89yxLHaG0g2r?=
 =?us-ascii?Q?ygvx72koxEPMP5yLlROSlSlY=3D?=
X-Microsoft-Antispam-Message-Info: JptJB5TMUmapBy5jGnlLIIozCOfcef+e2s8RzOUnAvFPUchJoERnA+Q8h1Hg8q3NiFLBSgA3ej0/vI+EriXH3LNS7I4GtOd7xWsdy8l8ZwG1bZjwjk0aPKjLEGw1QPnZansGdAoyVFBZeczT4QN/q2tPKMRmY5mq8bTrArCnP5e/xfkYFA4spuibq+PILMi5
X-Microsoft-Exchange-Diagnostics: 1; AM5PR0501MB2036;
 6:ehNSR5cJaWaLhCqcYjqNF3hiDYUrXD4o6VsoMzZKPvxNNPndzClHrO0lyebFOUOVnbnubMx+xa/Jvu2baDTxK36F6JWg+pdAwFVjNJE0c8V/SuFhi/Uy14Qzfkc2zbWS0bOqheQ0Y2t8KCEbzXSPvEAluK5pCfcozwW630G1KgmUCu8AuoNhABNq8qFmjYzXDJr/tCvFjQS5avsW5v7Thos7Y0ua/mNADMkSQB98gs7dCxs7BE8k/iRlWX17tlziOS63MVErUyASgFbayjCdOeRPpT0WCAbK5Tosan+EG2f5JKdn9RNJAGtm4vn70UWX/HlpZTJLVFb8tVHZd6nBgA7pHqN2xFJmeV7xiR+mYRU9SRUxlW/13UAks+YehFMxLyq6qY0srYArQaqNYEGuJfX12C06XbB6nwqX50MTujVRxIvIgUrQyOyH2CjEyaVWwIu9WMM6KxB8Y7Ypwdg3nw==;
 5:jtnxF8NVwPCWUv0us4E7tqSjwxTK71gS3qx7qTIo9L8S+vU6azJRpdIPVrZdfSzMHle2dN9r5MVPagi+Q4vO3UHqRwrYwyz4FEbjm/DVNCirWkV1Na/r1RH0zTgS6xoXIaVpXC9Vd677aUlSbwYUwsBsB744BVDUxXOAyY2TMkU=;
 24:WBhS3doqsouB3CXkA+/C/Ul2KeDHRJDBYUYGnYGbBlks/yYvTNeV+WEf9HafVe7qL+bmf+p7e8ocum6znvxlDqpLJD0BlVtwNO5/xRiLRjE=
SpamDiagnosticOutput: 1:99
SpamDiagnosticMetadata: NSPM
X-Microsoft-Exchange-Diagnostics: 1; AM5PR0501MB2036;
 7:BweYt6ij3qAowP/E7TpsgdB5W7+ED1JtvYPES1ZItwNTtfXQm6Rt+zu7hFtkUBFK4keTuAWlMhyFqoR0jM4jyvsui61W01SrJYhgMevPIKXj+vAQPNduJsC6fD5H3CvB6ktBICSgnwtLKaTq7r87+eBcBC5lPkEJv7dzytEDmlY1TbYCrT9a+C1yyGYasSofhFD9Oc8ag7+BWBIJvwLEkQIxzvnLLyfRHFW55oP1GzF2NBw5cRH4stj48CP+G2Bx
X-OriginatorOrg: Mellanox.com
X-MS-Exchange-CrossTenant-OriginalArrivalTime: 02 Apr 2018 18:50:28.6405 (UTC)
X-MS-Exchange-CrossTenant-Network-Message-Id: 4e37fc69-c74a-4bbb-a877-08d598ca9b6a
X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted
X-MS-Exchange-CrossTenant-Id: a652971c-7d2e-4d9b-a6a4-d149256f461b
X-MS-Exchange-Transport-CrossTenantHeadersStamped: AM5PR0501MB2036
Subject: [dpdk-dev] [PATCH v2 1/6] mbuf: add buffer offset field for
	flexible indirection
X-BeenThere: dev@dpdk.org
X-Mailman-Version: 2.1.15
Precedence: list
List-Id: DPDK patches and discussions <dev.dpdk.org>
List-Unsubscribe: <https://dpdk.org/ml/options/dev>,
 <mailto:dev-request@dpdk.org?subject=unsubscribe>
List-Archive: <http://dpdk.org/ml/archives/dev/>
List-Post: <mailto:dev@dpdk.org>
List-Help: <mailto:dev-request@dpdk.org?subject=help>
List-Subscribe: <https://dpdk.org/ml/listinfo/dev>,
 <mailto:dev-request@dpdk.org?subject=subscribe>
X-List-Received-Date: Mon, 02 Apr 2018 18:50:31 -0000

When attaching a mbuf, indirect mbuf has to point to start of buffer of
direct mbuf. By adding buf_off field to rte_mbuf, this becomes more
flexible. Indirect mbuf can point to any part of direct mbuf by calling
rte_pktmbuf_attach_at().

Possible use-cases could be:
- If a packet has multiple layers of encapsulation, multiple indirect
  buffers can reference different layers of the encapsulated packet.
- A large direct mbuf can even contain multiple packets in series and
  each packet can be referenced by multiple mbuf indirections.

Signed-off-by: Yongseok Koh <yskoh@mellanox.com>
---
 lib/librte_mbuf/rte_mbuf.h | 158 ++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 157 insertions(+), 1 deletion(-)

diff --git a/lib/librte_mbuf/rte_mbuf.h b/lib/librte_mbuf/rte_mbuf.h
index 62740254d..053db32d0 100644
--- a/lib/librte_mbuf/rte_mbuf.h
+++ b/lib/librte_mbuf/rte_mbuf.h
@@ -559,6 +559,11 @@ struct rte_mbuf {
 		};
 	};
 
+	/** Buffer offset of direct mbuf if attached. Indirect mbuf can point to
+	 * any part of direct mbuf.
+	 */
+	uint16_t buf_off;
+
 	/** Size of the application private data. In case of an indirect
 	 * mbuf, it stores the direct mbuf private data size. */
 	uint16_t priv_size;
@@ -671,7 +676,9 @@ rte_mbuf_data_dma_addr_default(const struct rte_mbuf *mb)
 static inline struct rte_mbuf *
 rte_mbuf_from_indirect(struct rte_mbuf *mi)
 {
-	return (struct rte_mbuf *)RTE_PTR_SUB(mi->buf_addr, sizeof(*mi) + mi->priv_size);
+	return (struct rte_mbuf *)
+		RTE_PTR_SUB(mi->buf_addr,
+				sizeof(*mi) + mi->priv_size + mi->buf_off);
 }
 
 /**
@@ -1281,6 +1288,98 @@ static inline int rte_pktmbuf_alloc_bulk(struct rte_mempool *pool,
 }
 
 /**
+ * Adjust tailroom of indirect mbuf. If offset is positive, enlarge the
+ * tailroom of the mbuf. If negative, shrink the tailroom.
+ *
+ * If length is out of range, then the function will fail and return -1,
+ * without modifying the indirect mbuf.
+ *
+ * @param mi
+ *   The indirect packet mbuf.
+ * @param len
+ *   The amount of length to adjust (in bytes).
+ * @return
+ *   - 0: On success.
+ *   - -1: On error.
+ */
+static inline int rte_pktmbuf_adj_indirect_tail(struct rte_mbuf *mi, int len)
+{
+	struct rte_mbuf *md;
+	uint16_t tailroom;
+	int delta;
+
+	RTE_ASSERT(RTE_MBUF_INDIRECT(mi));
+
+	md = rte_mbuf_from_indirect(mi);
+	if (unlikely(mi->buf_len + len <= 0 ||
+			mi->buf_off + mi->buf_len + len >= md->buf_len))
+		return -1;
+
+	mi->buf_len += len;
+
+	tailroom = mi->buf_len - mi->data_off - mi->data_len;
+	delta = tailroom + len;
+	if (delta > 0) {
+		/* Adjust tailroom */
+		delta = 0;
+	} else if (delta + mi->data_len < 0) {
+		/* No data */
+		mi->data_off += delta + mi->data_len;
+		delta = mi->data_len;
+	}
+	mi->data_len += delta;
+	mi->pkt_len += delta;
+	return 0;
+}
+
+/**
+ * Shift buffer reference of indirect mbuf. If offset is positive, push
+ * the offset of the mbuf. If negative, pull the offset.
+ *
+ * Returns a pointer to the start address of the new data area. If offset
+ * is out of range, then the function will fail and return NULL, without
+ * modifying the indirect mbuf.
+ *
+ * @param mi
+ *   The indirect packet mbuf.
+ * @param off
+ *   The amount of offset to adjust (in bytes).
+ * @return
+ *   A pointer to the new start of the data.
+ */
+static inline char *rte_pktmbuf_adj_indirect_head(struct rte_mbuf *mi, int off)
+{
+	int delta;
+
+	RTE_ASSERT(RTE_MBUF_INDIRECT(mi));
+
+	if (unlikely(off >= mi->buf_len || mi->buf_off + off < 0))
+		return NULL;
+
+	mi->buf_iova += off;
+	mi->buf_addr = (char *)mi->buf_addr + off;
+	mi->buf_len -= off;
+	mi->buf_off += off;
+
+	delta = off - mi->data_off;
+	if (delta < 0) {
+		/* Adjust headroom */
+		mi->data_off -= off;
+		delta = 0;
+	} else if (delta < mi->data_len) {
+		/* No headroom */
+		mi->data_off = 0;
+	} else {
+		/* No data */
+		mi->data_off = 0;
+		delta = mi->data_len;
+	}
+	mi->data_len -= delta;
+	mi->pkt_len -= delta;
+	return (char *)mi->buf_addr + mi->data_off;
+}
+
+/**
  * Attach packet mbuf to another packet mbuf.
  *
  * After attachment we refer the mbuf we attached as 'indirect',
@@ -1315,6 +1414,7 @@ static inline void rte_pktmbuf_attach(struct rte_mbuf *mi, struct rte_mbuf *m)
 	mi->buf_iova = m->buf_iova;
 	mi->buf_addr = m->buf_addr;
 	mi->buf_len = m->buf_len;
+	mi->buf_off = 0;
 
 	mi->data_off = m->data_off;
 	mi->data_len = m->data_len;
@@ -1336,6 +1436,62 @@ static inline void rte_pktmbuf_attach(struct rte_mbuf *mi, struct rte_mbuf *m)
 }
 
 /**
+ * Attach packet mbuf to another packet mbuf pointing by given offset.
+ *
+ * After attachment we refer the mbuf we attached as 'indirect',
+ * while mbuf we attached to as 'direct'.
+ *
+ * The indirect mbuf can reference to anywhere in the buffer of the direct
+ * mbuf by the given offset. And the indirect mbuf is also be trimmed by
+ * the given buffer length.
+ *
+ * As a result, if a direct mbuf has multiple layers of encapsulation,
+ * multiple indirect buffers can reference different layers of the packet.
+ * Or, a large direct mbuf can even contain multiple packets in series and
+ * each packet can be referenced by multiple mbuf indirections.
+ *
+ * Returns a pointer to the start address of the new data area. If offset
+ * or buffer length is out of range, then the function will fail and return
+ * NULL, without attaching the mbuf.
+ *
+ * @param mi
+ *   The indirect packet mbuf.
+ * @param m
+ *   The packet mbuf we're attaching to.
+ * @param off
+ *   The amount of offset to push (in bytes).
+ * @param buf_len
+ *   The buffer length of the indirect mbuf (in bytes).
+ * @return
+ *   A pointer to the new start of the data.
+ */
+static inline char *rte_pktmbuf_attach_at(struct rte_mbuf *mi,
+	struct rte_mbuf *m, uint16_t off, uint16_t buf_len)
+{
+	struct rte_mbuf *md;
+	char *ret;
+
+	if (RTE_MBUF_DIRECT(m))
+		md = m;
+	else
+		md = rte_mbuf_from_indirect(m);
+
+	if (off + buf_len > md->buf_len)
+		return NULL;
+
+	rte_pktmbuf_attach(mi, m);
+
+	/* Push reference of indirect mbuf */
+	ret = rte_pktmbuf_adj_indirect_head(mi, off);
+	RTE_ASSERT(ret != NULL);
+
+	/* Trim reference of indirect mbuf */
+	rte_pktmbuf_adj_indirect_tail(mi, off + buf_len - md->buf_len);
+
+	return ret;
+}
+
+/**
  * Detach an indirect packet mbuf.
  *
  *  - restore original mbuf address and length values.
-- 
2.11.0