diff --git a/platform/linux-generic/include/odp_packet_internal.h b/platform/linux-generic/include/odp_packet_internal.h
index 4c4e36c..392d670 100644
--- a/platform/linux-generic/include/odp_packet_internal.h
+++ b/platform/linux-generic/include/odp_packet_internal.h
@@ -306,7 +306,9 @@ static inline int packet_parse_not_complete(odp_packet_hdr_t *pkt_hdr)
 /* Forward declarations */
 int _odp_packet_copy_md_to_packet(odp_packet_t srcpkt, odp_packet_t dstpkt);
 
-odp_packet_t packet_alloc(odp_pool_t pool_hdl, uint32_t len, int parse);
+/* Packet alloc of pktios */
+int packet_alloc_multi(odp_pool_t pool_hdl, uint32_t len,
+		       odp_packet_t pkt[], int max_num);
 
 /* Fill in parser metadata for L2 */
 void packet_parse_l2(packet_parser_t *prs, uint32_t frame_len);
diff --git a/platform/linux-generic/include/odp_packet_socket.h b/platform/linux-generic/include/odp_packet_socket.h
index ccff69a..dbfc9f1 100644
--- a/platform/linux-generic/include/odp_packet_socket.h
+++ b/platform/linux-generic/include/odp_packet_socket.h
@@ -45,6 +45,7 @@
 typedef struct {
 	int sockfd; /**< socket descriptor */
 	odp_pool_t pool; /**< pool to alloc packets from */
+	uint32_t mtu;    /**< maximum transmission unit */
 	unsigned char if_mac[ETH_ALEN];	/**< IF eth mac addr */
 	uint8_t *cache_ptr[ODP_PACKET_SOCKET_MAX_BURST_RX];
 	odp_shm_t shm;
diff --git a/platform/linux-generic/odp_packet.c b/platform/linux-generic/odp_packet.c
index 474fa81..c4cf324 100644
--- a/platform/linux-generic/odp_packet.c
+++ b/platform/linux-generic/odp_packet.c
@@ -76,35 +76,46 @@ static void packet_init(pool_entry_t *pool, odp_packet_hdr_t *pkt_hdr,
 	pkt_hdr->input = ODP_PKTIO_INVALID;
 }
 
-odp_packet_t packet_alloc(odp_pool_t pool_hdl, uint32_t len, int parse)
+int packet_alloc_multi(odp_pool_t pool_hdl, uint32_t len,
+		       odp_packet_t pkt[], int max_num)
 {
+	odp_packet_hdr_t *pkt_hdr;
+	pool_entry_t *pool = odp_pool_to_entry(pool_hdl);
+	int num, i;
+
+	num = buffer_alloc_multi(pool_hdl, len, (odp_buffer_t *)pkt, max_num);
+
+	for (i = 0; i < num; i++) {
+		pkt_hdr = odp_packet_hdr(pkt[i]);
+		packet_init(pool, pkt_hdr, len, 1 /* do parse */);
+
+		if (pkt_hdr->tailroom >= pkt_hdr->buf_hdr.segsize)
+			pull_tail_seg(pkt_hdr);
+	}
+
+	return num;
+}
+
+odp_packet_t odp_packet_alloc(odp_pool_t pool_hdl, uint32_t len)
+{
+	pool_entry_t *pool = odp_pool_to_entry(pool_hdl);
+	size_t pkt_size = len ? len : pool->s.params.buf.size;
 	odp_packet_t pkt;
 	odp_packet_hdr_t *pkt_hdr;
-	pool_entry_t *pool = odp_pool_to_entry(pool_hdl);
 
-	if (pool->s.params.type != ODP_POOL_PACKET)
+	if (pool->s.params.type != ODP_POOL_PACKET) {
+		__odp_errno = EINVAL;
 		return ODP_PACKET_INVALID;
-
-	/* Handle special case for zero-length packets */
-	if (len == 0) {
-		len = pool->s.params.buf.size;
-
-		pkt = (odp_packet_t)buffer_alloc(pool_hdl, len);
-
-		if (pkt == ODP_PACKET_INVALID)
-			return ODP_PACKET_INVALID;
-
-		pull_tail(odp_packet_hdr(pkt), len);
-
-	} else {
-		pkt = (odp_packet_t)buffer_alloc(pool_hdl, len);
-
-		if (pkt == ODP_PACKET_INVALID)
-			return ODP_PACKET_INVALID;
 	}
 
+	pkt = (odp_packet_t)buffer_alloc(pool_hdl, pkt_size);
+	if (pkt == ODP_PACKET_INVALID)
+		return ODP_PACKET_INVALID;
+
 	pkt_hdr = odp_packet_hdr(pkt);
-	packet_init(pool, pkt_hdr, len, parse);
+	packet_init(pool, pkt_hdr, pkt_size, 0 /* do not parse */);
+	if (len == 0)
+		pull_tail(pkt_hdr, pkt_size);
 
 	if (pkt_hdr->tailroom >= pkt_hdr->buf_hdr.segsize)
 		pull_tail_seg(pkt_hdr);
@@ -112,11 +123,6 @@ odp_packet_t packet_alloc(odp_pool_t pool_hdl, uint32_t len, int parse)
 	return pkt;
 }
 
-odp_packet_t odp_packet_alloc(odp_pool_t pool_hdl, uint32_t len)
-{
-	return packet_alloc(pool_hdl, len, 0);
-}
-
 int odp_packet_alloc_multi(odp_pool_t pool_hdl, uint32_t len,
 			   odp_packet_t pkt[], int num)
 {
@@ -135,9 +141,12 @@ int odp_packet_alloc_multi(odp_pool_t pool_hdl, uint32_t len,
 	for (i = 0; i < count; ++i) {
 		odp_packet_hdr_t *pkt_hdr = odp_packet_hdr(pkt[i]);
 
-		packet_init(pool, pkt_hdr, pkt_size, 0);
+		packet_init(pool, pkt_hdr, pkt_size, 0 /* do not parse */);
 		if (len == 0)
 			pull_tail(pkt_hdr, pkt_size);
+
+		if (pkt_hdr->tailroom >= pkt_hdr->buf_hdr.segsize)
+			pull_tail_seg(pkt_hdr);
 	}
 
 	return count;
@@ -145,12 +154,16 @@ int odp_packet_alloc_multi(odp_pool_t pool_hdl, uint32_t len,
 
 void odp_packet_free(odp_packet_t pkt)
 {
-	odp_buffer_free((odp_buffer_t)pkt);
+	uint32_t pool_id = pool_id_from_buf((odp_buffer_t)pkt);
+
+	buffer_free(pool_id, (odp_buffer_t)pkt);
 }
 
 void odp_packet_free_multi(const odp_packet_t pkt[], int num)
 {
-	odp_buffer_free_multi((const odp_buffer_t *)pkt, num);
+	uint32_t pool_id = pool_id_from_buf((odp_buffer_t)pkt[0]);
+
+	buffer_free_multi(pool_id, (const odp_buffer_t * const)pkt, num);
 }
 
 int odp_packet_reset(odp_packet_t pkt, uint32_t len)
diff --git a/platform/linux-generic/pktio/dpdk.c b/platform/linux-generic/pktio/dpdk.c
index a5934a5..1fad8a9 100644
--- a/platform/linux-generic/pktio/dpdk.c
+++ b/platform/linux-generic/pktio/dpdk.c
@@ -706,7 +706,7 @@ static int dpdk_stop(pktio_entry_t *pktio_entry)
 static inline int mbuf_to_pkt(pktio_entry_t *pktio_entry,
 			      odp_packet_t pkt_table[],
 			      struct rte_mbuf *mbuf_table[],
-			      uint16_t num, odp_time_t *ts)
+			      uint16_t mbuf_num, odp_time_t *ts)
 {
 	odp_packet_t pkt;
 	odp_packet_hdr_t *pkt_hdr;
@@ -715,9 +715,15 @@ static inline int mbuf_to_pkt(pktio_entry_t *pktio_entry,
 	void *buf;
 	int i, j;
 	int nb_pkts = 0;
+	int alloc_len, num;
+	odp_pool_t pool = pktio_entry->s.pkt_dpdk.pool;
+
+	/* Allocate maximum sized packets */
+	alloc_len = pktio_entry->s.pkt_dpdk.data_room;
+
+	num = packet_alloc_multi(pool, alloc_len, pkt_table, mbuf_num);
 
 	for (i = 0; i < num; i++) {
-		odp_pool_t pool = pktio_entry->s.pkt_dpdk.pool;
 		odp_packet_hdr_t parsed_hdr;
 
 		mbuf = mbuf_table[i];
@@ -738,18 +744,16 @@ static inline int mbuf_to_pkt(pktio_entry_t *pktio_entry,
 						&parsed_hdr))
 				goto fail;
 		}
-		pkt = packet_alloc(pool, pkt_len, 1);
-		if (pkt == ODP_PACKET_INVALID)
-			goto fail;
 
+		pkt     = pkt_table[i];
 		pkt_hdr = odp_packet_hdr(pkt);
+		pull_tail(pkt_hdr, alloc_len - pkt_len);
 
 		/* For now copy the data in the mbuf,
 		   worry about zero-copy later */
-		if (odp_packet_copy_from_mem(pkt, 0, pkt_len, buf) != 0) {
-			odp_packet_free(pkt);
+		if (odp_packet_copy_from_mem(pkt, 0, pkt_len, buf) != 0)
 			goto fail;
-		}
+
 		pkt_hdr->input = pktio_entry->s.handle;
 
 		if (pktio_cls_enabled(pktio_entry))
@@ -770,7 +774,9 @@ static inline int mbuf_to_pkt(pktio_entry_t *pktio_entry,
 	return nb_pkts;
 
 fail:
-	for (j = i; j < num; j++)
+	odp_packet_free_multi(&pkt_table[i], mbuf_num - i);
+
+	for (j = i; j < mbuf_num; j++)
 		rte_pktmbuf_free(mbuf_table[j]);
 
 	return (i > 0 ? i : -1);
diff --git a/platform/linux-generic/pktio/netmap.c b/platform/linux-generic/pktio/netmap.c
index 3017e40..6498429 100644
--- a/platform/linux-generic/pktio/netmap.c
+++ b/platform/linux-generic/pktio/netmap.c
@@ -599,6 +599,7 @@ static inline int netmap_pkt_to_odp(pktio_entry_t *pktio_entry,
 	odp_pool_t pool = pktio_entry->s.pkt_nm.pool;
 	odp_packet_hdr_t *pkt_hdr;
 	odp_packet_hdr_t parsed_hdr;
+	int num;
 
 	if (odp_unlikely(len > pktio_entry->s.pkt_nm.max_frame_len)) {
 		ODP_ERR("RX: frame too big %" PRIu16 " %zu!\n", len,
@@ -616,8 +617,8 @@ static inline int netmap_pkt_to_odp(pktio_entry_t *pktio_entry,
 					len, &pool, &parsed_hdr))
 			return -1;
 	}
-	pkt = packet_alloc(pool, len, 1);
-	if (pkt == ODP_PACKET_INVALID)
+	num = packet_alloc_multi(pool, len, &pkt, 1);
+	if (num != 1)
 		return -1;
 
 	pkt_hdr = odp_packet_hdr(pkt);
diff --git a/platform/linux-generic/pktio/pcap.c b/platform/linux-generic/pktio/pcap.c
index e501858..e54a56f 100644
--- a/platform/linux-generic/pktio/pcap.c
+++ b/platform/linux-generic/pktio/pcap.c
@@ -224,19 +224,9 @@ static int pcapif_recv_pkt(pktio_entry_t *pktio_entry, int index ODP_UNUSED,
 	    pktio_entry->s.config.pktin.bit.ts_ptp)
 		ts = &ts_val;
 
-	pkt = ODP_PACKET_INVALID;
-	pkt_len = 0;
-
 	for (i = 0; i < len; ) {
 		int ret;
 
-		if (pkt == ODP_PACKET_INVALID) {
-			pkt = packet_alloc(pcap->pool, 0 /*default len*/, 1);
-			if (odp_unlikely(pkt == ODP_PACKET_INVALID))
-				break;
-			pkt_len = odp_packet_len(pkt);
-		}
-
 		ret = pcap_next_ex(pcap->rx, &hdr, &data);
 
 		/* end of file, attempt to reopen if within loop limit */
@@ -246,17 +236,17 @@ static int pcapif_recv_pkt(pktio_entry_t *pktio_entry, int index ODP_UNUSED,
 		if (ret != 1)
 			break;
 
+		pkt_len = hdr->caplen;
+
+		ret = packet_alloc_multi(pcap->pool, pkt_len, &pkt, 1);
+		if (odp_unlikely(ret != 1))
+			break;
+
 		if (ts != NULL)
 			ts_val = odp_time_global();
 
 		pkt_hdr = odp_packet_hdr(pkt);
 
-		if (!odp_packet_pull_tail(pkt, pkt_len - hdr->caplen)) {
-			ODP_ERR("failed to pull tail: pkt_len: %d caplen: %d\n",
-				pkt_len, hdr->caplen);
-			break;
-		}
-
 		if (odp_packet_copy_from_mem(pkt, 0, hdr->caplen, data) != 0) {
 			ODP_ERR("failed to copy packet data\n");
 			break;
@@ -269,7 +259,6 @@ static int pcapif_recv_pkt(pktio_entry_t *pktio_entry, int index ODP_UNUSED,
 		pkt_hdr->input = pktio_entry->s.handle;
 
 		pkts[i] = pkt;
-		pkt = ODP_PACKET_INVALID;
 
 		i++;
 	}
@@ -277,9 +266,6 @@ static int pcapif_recv_pkt(pktio_entry_t *pktio_entry, int index ODP_UNUSED,
 
 	odp_ticketlock_unlock(&pktio_entry->s.rxl);
 
-	if (pkt != ODP_PACKET_INVALID)
-		odp_packet_free(pkt);
-
 	return i;
 }
 
diff --git a/platform/linux-generic/pktio/socket.c b/platform/linux-generic/pktio/socket.c
index 0309b0d..e01b0a5 100644
--- a/platform/linux-generic/pktio/socket.c
+++ b/platform/linux-generic/pktio/socket.c
@@ -527,6 +527,10 @@ static int sock_setup_pkt(pktio_entry_t *pktio_entry, const char *netdev,
 	if (err != 0)
 		goto error;
 
+	pkt_sock->mtu = mtu_get_fd(sockfd, netdev);
+	if (!pkt_sock->mtu)
+		goto error;
+
 	/* bind socket to if */
 	memset(&sa_ll, 0, sizeof(sa_ll));
 	sa_ll.sll_family = AF_PACKET;
@@ -659,6 +663,7 @@ static int sock_mmsg_recv(pktio_entry_t *pktio_entry, int index ODP_UNUSED,
 			void *base = msgvec[i].msg_hdr.msg_iov->iov_base;
 			struct ethhdr *eth_hdr = base;
 			uint16_t pkt_len = msgvec[i].msg_len;
+			int num;
 
 			/* Don't receive packets sent by ourselves */
 			if (odp_unlikely(ethaddrs_equal(pkt_sock->if_mac,
@@ -668,8 +673,8 @@ static int sock_mmsg_recv(pktio_entry_t *pktio_entry, int index ODP_UNUSED,
 			if (cls_classify_packet(pktio_entry, base, pkt_len,
 						pkt_len, &pool, &parsed_hdr))
 				continue;
-			pkt = packet_alloc(pool, pkt_len, 1);
-			if (pkt == ODP_PACKET_INVALID)
+			num = packet_alloc_multi(pool, pkt_len, &pkt, 1);
+			if (num != 1)
 				continue;
 
 			pkt_hdr = odp_packet_hdr(pkt);
@@ -690,10 +695,14 @@ static int sock_mmsg_recv(pktio_entry_t *pktio_entry, int index ODP_UNUSED,
 				   [ODP_BUFFER_MAX_SEG];
 
 		for (i = 0; i < (int)len; i++) {
-			pkt_table[i] = packet_alloc(pkt_sock->pool,
-						    0 /*default*/, 1);
-			if (odp_unlikely(pkt_table[i] == ODP_PACKET_INVALID))
+			int num;
+
+			num = packet_alloc_multi(pkt_sock->pool, pkt_sock->mtu,
+						 &pkt_table[i], 1);
+			if (odp_unlikely(num != 1)) {
+				pkt_table[i] = ODP_PACKET_INVALID;
 				break;
+			}
 
 			msgvec[i].msg_hdr.msg_iovlen =
 				_rx_pkt_to_iovec(pkt_table[i], iovecs[i]);
@@ -818,7 +827,7 @@ static int sock_mmsg_send(pktio_entry_t *pktio_entry, int index ODP_UNUSED,
  */
 static uint32_t sock_mtu_get(pktio_entry_t *pktio_entry)
 {
-	return mtu_get_fd(pktio_entry->s.pkt_sock.sockfd, pktio_entry->s.name);
+	return pktio_entry->s.pkt_sock.mtu;
 }
 
 /*
diff --git a/platform/linux-generic/pktio/socket_mmap.c b/platform/linux-generic/pktio/socket_mmap.c
index b6be81a..8b24c99 100644
--- a/platform/linux-generic/pktio/socket_mmap.c
+++ b/platform/linux-generic/pktio/socket_mmap.c
@@ -171,6 +171,7 @@ static inline unsigned pkt_mmap_v2_rx(pktio_entry_t *pktio_entry,
 		odp_packet_hdr_t *hdr;
 		odp_packet_hdr_t parsed_hdr;
 		odp_pool_t pool = pkt_sock->pool;
+		int num;
 
 		if (!mmap_rx_kernel_ready(ring->rd[frame_num].iov_base))
 			break;
@@ -208,8 +209,10 @@ static inline unsigned pkt_mmap_v2_rx(pktio_entry_t *pktio_entry,
 			}
 		}
 
-		pkt_table[nb_rx] = packet_alloc(pool, pkt_len, 1);
-		if (odp_unlikely(pkt_table[nb_rx] == ODP_PACKET_INVALID)) {
+		num = packet_alloc_multi(pool, pkt_len, &pkt_table[nb_rx], 1);
+
+		if (odp_unlikely(num != 1)) {
+			pkt_table[nb_rx] = ODP_PACKET_INVALID;
 			mmap_rx_user_ready(ppd.raw); /* drop */
 			frame_num = next_frame_num;
 			continue;
diff --git a/platform/linux-generic/pktio/tap.c b/platform/linux-generic/pktio/tap.c
index a9a8886..d758a39 100644
--- a/platform/linux-generic/pktio/tap.c
+++ b/platform/linux-generic/pktio/tap.c
@@ -185,11 +185,12 @@ static odp_packet_t pack_odp_pkt(pktio_entry_t *pktio_entry, const void *data,
 {
 	odp_packet_t pkt;
 	odp_packet_hdr_t *pkt_hdr;
+	int num;
 
-	pkt = packet_alloc(pktio_entry->s.pkt_tap.pool, len, 1);
+	num = packet_alloc_multi(pktio_entry->s.pkt_tap.pool, len, &pkt, 1);
 
-	if (pkt == ODP_PACKET_INVALID)
-		return pkt;
+	if (num != 1)
+		return ODP_PACKET_INVALID;
 
 	if (odp_packet_copy_from_mem(pkt, 0, len, data) < 0) {
 		ODP_ERR("failed to copy packet data\n");
