diff --git a/lib/librte_eal/bsdapp/eal/rte_eal_version.map b/lib/librte_eal/bsdapp/eal/rte_eal_version.map
index 0401be2..7110816 100644
--- a/lib/librte_eal/bsdapp/eal/rte_eal_version.map
+++ b/lib/librte_eal/bsdapp/eal/rte_eal_version.map
@@ -105,3 +105,9 @@ DPDK_2.0 {
 
 	local: *;
 };
+
+DPDK_2.1 {
+	global:
+
+	rte_memzone_free;
+} DPDK_2.0;
diff --git a/lib/librte_eal/common/eal_common_memzone.c b/lib/librte_eal/common/eal_common_memzone.c
index 943012b..5bc4ab4 100644
--- a/lib/librte_eal/common/eal_common_memzone.c
+++ b/lib/librte_eal/common/eal_common_memzone.c
@@ -77,6 +77,23 @@ memzone_lookup_thread_unsafe(const char *name)
 	return NULL;
 }
 
+static inline struct rte_memzone *
+get_next_free_memzone(void)
+{
+	struct rte_mem_config *mcfg;
+	unsigned i = 0;
+
+	/* get pointer to global configuration */
+	mcfg = rte_eal_get_configuration()->mem_config;
+
+	for (i = 0; i < RTE_MAX_MEMZONE; i++) {
+		if (mcfg->memzone[i].addr == NULL)
+			return &mcfg->memzone[i];
+	}
+
+	return NULL;
+}
+
 /*
  * Return a pointer to a correctly filled memzone descriptor. If the
  * allocation cannot be done, return NULL.
@@ -141,7 +158,7 @@ memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,
 	mcfg = rte_eal_get_configuration()->mem_config;
 
 	/* no more room in config */
-	if (mcfg->memzone_idx >= RTE_MAX_MEMZONE) {
+	if (mcfg->memzone_cnt >= RTE_MAX_MEMZONE) {
 		RTE_LOG(ERR, EAL, "%s(): No more room in config\n", __func__);
 		rte_errno = ENOSPC;
 		return NULL;
@@ -215,7 +232,16 @@ memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,
 	const struct malloc_elem *elem = malloc_elem_from_data(mz_addr);
 
 	/* fill the zone in config */
-	struct rte_memzone *mz = &mcfg->memzone[mcfg->memzone_idx++];
+	struct rte_memzone *mz = get_next_free_memzone();
+
+	if (mz == NULL) {
+		RTE_LOG(ERR, EAL, "%s(): Cannot find free memzone but there is room "
+				"in config!\n", __func__);
+		rte_errno = ENOSPC;
+		return NULL;
+	}
+
+	mcfg->memzone_cnt++;
 	snprintf(mz->name, sizeof(mz->name), "%s", name);
 	mz->phys_addr = rte_malloc_virt2phy(mz_addr);
 	mz->addr = mz_addr;
@@ -291,6 +317,41 @@ rte_memzone_reserve_bounded(const char *name, size_t len,
 	return mz;
 }
 
+int
+rte_memzone_free(const struct rte_memzone *mz)
+{
+	struct rte_mem_config *mcfg;
+	int ret = 0;
+	void *addr;
+	unsigned idx;
+
+	if (mz == NULL)
+		return -EINVAL;
+
+	mcfg = rte_eal_get_configuration()->mem_config;
+
+	rte_rwlock_write_lock(&mcfg->mlock);
+
+	idx = ((uintptr_t)mz - (uintptr_t)mcfg->memzone);
+	idx = idx / sizeof(struct rte_memzone);
+
+	addr = mcfg->memzone[idx].addr;
+	if (addr == NULL)
+		ret = -EINVAL;
+	else if (mcfg->memzone_cnt == 0) {
+		rte_panic("%s(): memzone address not NULL but memzone_cnt is 0!\n",
+				__func__);
+	} else {
+		memset(&mcfg->memzone[idx], 0, sizeof(mcfg->memzone[idx]));
+		mcfg->memzone_cnt--;
+	}
+
+	rte_rwlock_write_unlock(&mcfg->mlock);
+
+	rte_free(addr);
+
+	return ret;
+}
 
 /*
  * Lookup for the memzone identified by the given name
@@ -364,7 +425,7 @@ rte_eal_memzone_init(void)
 	rte_rwlock_write_lock(&mcfg->mlock);
 
 	/* delete all zones */
-	mcfg->memzone_idx = 0;
+	mcfg->memzone_cnt = 0;
 	memset(mcfg->memzone, 0, sizeof(mcfg->memzone));
 
 	rte_rwlock_write_unlock(&mcfg->mlock);
diff --git a/lib/librte_eal/common/include/rte_eal_memconfig.h b/lib/librte_eal/common/include/rte_eal_memconfig.h
index 7de906b..2b5e0b1 100644
--- a/lib/librte_eal/common/include/rte_eal_memconfig.h
+++ b/lib/librte_eal/common/include/rte_eal_memconfig.h
@@ -67,7 +67,7 @@ struct rte_mem_config {
 	rte_rwlock_t qlock;   /**< used for tailq operation for thread safe. */
 	rte_rwlock_t mplock;  /**< only used by mempool LIB for thread-safe. */
 
-	uint32_t memzone_idx; /**< Index of memzone */
+	uint32_t memzone_cnt; /**< Number of allocated memzones */
 
 	/* memory segments and zones */
 	struct rte_memseg memseg[RTE_MAX_MEMSEG];    /**< Physmem descriptors. */
diff --git a/lib/librte_eal/common/include/rte_memzone.h b/lib/librte_eal/common/include/rte_memzone.h
index ee62680..5223b1e 100644
--- a/lib/librte_eal/common/include/rte_memzone.h
+++ b/lib/librte_eal/common/include/rte_memzone.h
@@ -240,6 +240,17 @@ const struct rte_memzone *rte_memzone_reserve_bounded(const char *name,
 			unsigned flags, unsigned align, unsigned bound);
 
 /**
+ * Free a memzone.
+ *
+ * @param mz
+ *   A pointer to the memzone
+ * @return
+ *  -EINVAL - invalid parameter
+ *  0 - success
+ */
+int rte_memzone_free(const struct rte_memzone *mz);
+
+/**
  * Lookup for a memzone.
  *
  * Get a pointer to a descriptor of an already reserved memory
diff --git a/lib/librte_eal/linuxapp/eal/eal_ivshmem.c b/lib/librte_eal/linuxapp/eal/eal_ivshmem.c
index facfb80..589019b 100644
--- a/lib/librte_eal/linuxapp/eal/eal_ivshmem.c
+++ b/lib/librte_eal/linuxapp/eal/eal_ivshmem.c
@@ -768,12 +768,12 @@ rte_eal_ivshmem_obj_init(void)
 		seg = &ivshmem_config->segment[i];
 
 		/* add memzone */
-		if (mcfg->memzone_idx == RTE_MAX_MEMZONE) {
+		if (mcfg->memzone_cnt == RTE_MAX_MEMZONE) {
 			RTE_LOG(ERR, EAL, "No more memory zones available!\n");
 			return -1;
 		}
 
-		idx = mcfg->memzone_idx;
+		idx = mcfg->memzone_cnt;
 
 		RTE_LOG(DEBUG, EAL, "Found memzone: '%s' at %p (len 0x%" PRIx64 ")\n",
 				seg->entry.mz.name, seg->entry.mz.addr, seg->entry.mz.len);
@@ -796,13 +796,13 @@ rte_eal_ivshmem_obj_init(void)
 			}
 		}
 
-		mcfg->memzone_idx++;
+		mcfg->memzone_cnt++;
 	}
 
 	rte_rwlock_write_lock(RTE_EAL_TAILQ_RWLOCK);
 
 	/* find rings */
-	for (i = 0; i < mcfg->memzone_idx; i++) {
+	for (i = 0; i < mcfg->memzone_cnt; i++) {
 		mz = &mcfg->memzone[i];
 
 		/* check if memzone has a ring prefix */
diff --git a/lib/librte_eal/linuxapp/eal/rte_eal_version.map b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
index c107b05..e537b42 100644
--- a/lib/librte_eal/linuxapp/eal/rte_eal_version.map
+++ b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
@@ -111,3 +111,9 @@ DPDK_2.0 {
 
 	local: *;
 };
+
+DPDK_2.1 {
+	global:
+
+	rte_memzone_free;
+} DPDK_2.0;
