diff --git a/include/odp/api/packet.h b/include/odp/api/packet.h
index 5d46b7b..bf0da17 100644
--- a/include/odp/api/packet.h
+++ b/include/odp/api/packet.h
@@ -62,7 +62,8 @@ extern "C" {
  * Pool must have been created with ODP_POOL_PACKET type. The
  * packet is initialized with data pointers and lengths set according to the
  * specified len, and the default headroom and tailroom length settings. All
- * other packet metadata are set to their default values.
+ * other packet metadata are set to their default values, packet reference count
+ * set to 1.
  *
  * @param pool          Pool handle
  * @param len           Packet data length
@@ -79,7 +80,8 @@ odp_packet_t odp_packet_alloc(odp_pool_t pool, uint32_t len);
 /**
  * Free packet
  *
- * Frees the packet into the buffer pool it was allocated from.
+ * Decrement packet reference count and free the packet back into the buffer
+ * pool it was allocated from if reference count reaches 0.
  *
  * @param pkt           Packet handle
  */
@@ -125,6 +127,45 @@ odp_packet_t odp_packet_from_event(odp_event_t ev);
  */
 odp_event_t odp_packet_to_event(odp_packet_t pkt);
 
+/**
+ * Get packet reference count
+ *
+ * @param buf	   Packet handle
+ * @return         Value of packet reference count
+ */
+uint32_t odp_packet_refcount(odp_packet_t pkt);
+
+/**
+ * Set packet reference count
+ *
+ * @param pkt	   Packet handle
+ * @param val	   New value of packet reference count
+ *
+ * @retval	   0 on success
+ * @retval	  <0 on failure
+ */
+int odp_packet_refcount_set(odp_packet_t pkt, uint32_t val);
+
+/**
+ * Increment packet reference count on val
+ *
+ * @param pkt      Packet handle
+ * @param val	   Value to add to current packet reference count
+ *
+ * @return	   New value of reference count
+ */
+uint32_t odp_packet_refcount_inc(odp_packet_t pkt, uint32_t val);
+
+/**
+ * Decrement event reference count on val
+ *
+ * @param pkt	   Packet handle
+ * @param val	   Value to subtract from current packet reference count
+ *
+ * @return	   New value of reference count
+ */
+uint32_t odp_packet_refcount_dec(odp_packet_t pkt, uint32_t val);
+
 /*
  *
  * Pointers and lengths
diff --git a/platform/linux-generic/include/odp_buffer_inlines.h b/platform/linux-generic/include/odp_buffer_inlines.h
index 3f4d9fd..da5693d 100644
--- a/platform/linux-generic/include/odp_buffer_inlines.h
+++ b/platform/linux-generic/include/odp_buffer_inlines.h
@@ -56,32 +56,6 @@ static inline odp_buffer_hdr_t *odp_buf_to_hdr(odp_buffer_t buf)
 		(pool->pool_mdata_addr + (index * ODP_CACHE_LINE_SIZE));
 }
 
-static inline uint32_t odp_buffer_refcount(odp_buffer_hdr_t *buf)
-{
-	return odp_atomic_load_u32(&buf->ref_count);
-}
-
-static inline uint32_t odp_buffer_incr_refcount(odp_buffer_hdr_t *buf,
-						uint32_t val)
-{
-	return odp_atomic_fetch_add_u32(&buf->ref_count, val) + val;
-}
-
-static inline uint32_t odp_buffer_decr_refcount(odp_buffer_hdr_t *buf,
-						uint32_t val)
-{
-	uint32_t tmp;
-
-	tmp = odp_atomic_fetch_sub_u32(&buf->ref_count, val);
-
-	if (tmp < val) {
-		odp_atomic_fetch_add_u32(&buf->ref_count, val - tmp);
-		return 0;
-	} else {
-		return tmp - val;
-	}
-}
-
 static inline odp_buffer_hdr_t *validate_buf(odp_buffer_t buf)
 {
 	odp_buffer_bits_t handle;
diff --git a/platform/linux-generic/odp_packet.c b/platform/linux-generic/odp_packet.c
index 209a6e6..f86b3f3 100644
--- a/platform/linux-generic/odp_packet.c
+++ b/platform/linux-generic/odp_packet.c
@@ -28,27 +28,33 @@
 odp_packet_t odp_packet_alloc(odp_pool_t pool_hdl, uint32_t len)
 {
 	pool_entry_t *pool = odp_pool_to_entry(pool_hdl);
+	odp_packet_t pkt = ODP_PACKET_INVALID;
 
 	if (pool->s.params.type != ODP_POOL_PACKET)
-		return ODP_PACKET_INVALID;
+		return pkt;
 
-	/* Handle special case for zero-length packets */
-	if (len == 0) {
-		odp_packet_t pkt =
-			(odp_packet_t)buffer_alloc(pool_hdl,
-						   pool->s.params.buf.size);
+	if (!len) {
+		/* Handle special case for zero-length packets */
+		pkt = (odp_packet_t)buffer_alloc(pool_hdl,
+						 pool->s.params.buf.size);
 		if (pkt != ODP_PACKET_INVALID)
 			pull_tail(odp_packet_hdr(pkt),
 				  pool->s.params.buf.size);
-
-		return pkt;
+	} else {
+		pkt = (odp_packet_t)buffer_alloc(pool_hdl, len);
 	}
 
-	return (odp_packet_t)buffer_alloc(pool_hdl, len);
+	if (pkt != ODP_PACKET_INVALID)
+		odp_packet_refcount_set(pkt, 1);
+
+	return pkt;
 }
 
 void odp_packet_free(odp_packet_t pkt)
 {
+	if (odp_packet_refcount_dec(pkt, 1) > 1)
+		return;
+
 	odp_buffer_free((odp_buffer_t)pkt);
 }
 
@@ -85,6 +91,46 @@ odp_event_t odp_packet_to_event(odp_packet_t pkt)
 	return (odp_event_t)pkt;
 }
 
+uint32_t odp_packet_refcount(odp_packet_t pkt)
+{
+	odp_buffer_t buf = _odp_packet_to_buffer(pkt);
+	odp_buffer_hdr_t *hdr = odp_buf_to_hdr(buf);
+
+	return odp_atomic_load_u32(&hdr->ref_count);
+}
+
+int odp_packet_refcount_set(odp_packet_t pkt, uint32_t val)
+{
+	odp_buffer_t buf = _odp_packet_to_buffer(pkt);
+	odp_buffer_hdr_t *hdr = odp_buf_to_hdr(buf);
+
+	odp_atomic_store_u32(&hdr->ref_count, val);
+	return 0;
+}
+
+uint32_t odp_packet_refcount_inc(odp_packet_t pkt, uint32_t val)
+{
+	odp_buffer_t buf = _odp_packet_to_buffer(pkt);
+	odp_buffer_hdr_t *hdr = odp_buf_to_hdr(buf);
+
+	return odp_atomic_fetch_add_u32(&hdr->ref_count, val) + val;
+}
+
+uint32_t odp_packet_refcount_dec(odp_packet_t pkt, uint32_t val)
+{
+	uint32_t tmp;
+	odp_buffer_t buf = _odp_packet_to_buffer(pkt);
+	odp_buffer_hdr_t *hdr = odp_buf_to_hdr(buf);
+
+	tmp = odp_atomic_fetch_sub_u32(&hdr->ref_count, val);
+	if (tmp < val) {
+		odp_atomic_fetch_add_u32(&hdr->ref_count, val - tmp);
+		return 0;
+	} else {
+		return tmp - val;
+	}
+}
+
 /*
  *
  * Pointers and lengths
