diff --git a/lib/librte_eal/common/include/rte_malloc.h b/lib/librte_eal/common/include/rte_malloc.h
index 40bae4478..ec59302de 100644
--- a/lib/librte_eal/common/include/rte_malloc.h
+++ b/lib/librte_eal/common/include/rte_malloc.h
@@ -268,6 +268,10 @@ rte_malloc_get_socket_stats(int socket,
  *
  * @note Multiple memory chunks can be added to the same heap
  *
+ * @note Before accessing this memory in other processes, it needs to be
+ *   attached in each of those processes by calling
+ *   ``rte_malloc_heap_memory_attach`` in each other process.
+ *
  * @note Memory must be previously allocated for DPDK to be able to use it as a
  *   malloc heap. Failing to do so will result in undefined behavior, up to and
  *   including segmentation faults.
@@ -329,21 +333,48 @@ rte_malloc_heap_memory_add(const char *heap_name, void *va_addr, size_t len,
 int __rte_experimental
 rte_malloc_heap_memory_remove(const char *heap_name, void *va_addr, size_t len);
 
+/**
+ * Attach to an already existing chunk of external memory in another process.
+ *
+ * @note This function must be called before any attempt is made to use an
+ *   already existing external memory chunk. This function does *not* need to
+ *   be called if a call to ``rte_malloc_heap_memory_add`` was made in the
+ *   current process.
+ *
+ * @param heap_name
+ *   Heap name to which this chunk of memory belongs
+ * @param va_addr
+ *   Start address of memory chunk to attach to
+ * @param len
+ *   Length of memory chunk to attach to
+ * @return
+ *   0 on successful attach
+ *   -1 on unsuccessful attach, with rte_errno set to indicate cause for error:
+ *     EINVAL - one of the parameters was invalid
+ *     EPERM  - attempted to attach memory to a reserved heap
+ *     ENOENT - heap or memory chunk was not found
+ */
+int __rte_experimental
+rte_malloc_heap_memory_attach(const char *heap_name, void *va_addr, size_t len);
+
 /**
  * Creates a new empty malloc heap with a specified name.
  *
  * @note Heaps created via this call will automatically get assigned a unique
  *   socket ID, which can be found using ``rte_malloc_heap_get_socket()``
  *
+ * @note This function can only be called in primary process.
+ *
  * @param heap_name
  *   Name of the heap to create.
  *
  * @return
  *   - 0 on successful creation
  *   - -1 in case of error, with rte_errno set to one of the following:
- *     EINVAL - ``heap_name`` was NULL, empty or too long
- *     EEXIST - heap by name of ``heap_name`` already exists
- *     ENOSPC - no more space in internal config to store a new heap
+ *     EINVAL          - ``heap_name`` was NULL, empty or too long
+ *     EEXIST          - heap by name of ``heap_name`` already exists
+ *     ENOSPC          - no more space in internal config to store a new heap
+ *     E_RTE_SECONDARY - attempted to create a heap in secondary process
  */
 int __rte_experimental
 rte_malloc_heap_create(const char *heap_name);
@@ -357,16 +388,19 @@ rte_malloc_heap_create(const char *heap_name);
  * @note This function will return a failure result if not all memory segments
  *   were removed from the heap prior to its destruction
  *
+ * @note This function can only be called in primary process.
+ *
  * @param heap_name
  *   Name of the heap to create.
  *
  * @return
  *   - 0 on success
  *   - -1 in case of error, with rte_errno set to one of the following:
- *     EINVAL - ``heap_name`` was NULL, empty or too long
- *     ENOENT - heap by the name of ``heap_name`` was not found
- *     EPERM  - attempting to destroy reserved heap
- *     EBUSY  - heap still contains data
+ *     EINVAL          - ``heap_name`` was NULL, empty or too long
+ *     ENOENT          - heap by the name of ``heap_name`` was not found
+ *     EPERM           - attempting to destroy reserved heap
+ *     EBUSY           - heap still contains data
+ *     E_RTE_SECONDARY - attempted to destroy a heap in secondary process
  */
 int __rte_experimental
 rte_malloc_heap_destroy(const char *heap_name);
diff --git a/lib/librte_eal/common/rte_malloc.c b/lib/librte_eal/common/rte_malloc.c
index bfc49d0b7..01c112a46 100644
--- a/lib/librte_eal/common/rte_malloc.c
+++ b/lib/librte_eal/common/rte_malloc.c
@@ -418,6 +418,89 @@ rte_malloc_heap_memory_remove(const char *heap_name, void *va_addr, size_t len)
 	return ret;
 }
 
+struct sync_mem_walk_arg {
+	void *va_addr;
+	size_t len;
+	int result;
+};
+
+static int
+attach_mem_walk(const struct rte_memseg_list *msl, void *arg)
+{
+	struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
+	struct sync_mem_walk_arg *wa = arg;
+	size_t len = msl->page_sz * msl->memseg_arr.len;
+
+	if (msl->base_va == wa->va_addr &&
+			len == wa->len) {
+		struct rte_memseg_list *found_msl;
+		int msl_idx, ret;
+
+		/* msl is const */
+		msl_idx = msl - mcfg->memsegs;
+		found_msl = &mcfg->memsegs[msl_idx];
+
+		ret = rte_fbarray_attach(&found_msl->memseg_arr);
+
+		if (ret < 0)
+			wa->result = -rte_errno;
+		else
+			wa->result = 0;
+		return 1;
+	}
+	return 0;
+}
+
+int
+rte_malloc_heap_memory_attach(const char *heap_name, void *va_addr, size_t len)
+{
+	struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
+	struct malloc_heap *heap = NULL;
+	struct sync_mem_walk_arg wa;
+	int ret;
+
+	if (heap_name == NULL || va_addr == NULL || len == 0 ||
+			strnlen(heap_name, RTE_HEAP_NAME_MAX_LEN) == 0 ||
+			strnlen(heap_name, RTE_HEAP_NAME_MAX_LEN) ==
+				RTE_HEAP_NAME_MAX_LEN) {
+		rte_errno = EINVAL;
+		return -1;
+	}
+	rte_rwlock_read_lock(&mcfg->memory_hotplug_lock);
+
+	/* find our heap */
+	heap = find_named_heap(heap_name);
+	if (heap == NULL) {
+		rte_errno = ENOENT;
+		ret = -1;
+		goto unlock;
+	}
+	/* we shouldn't be able to attach to internal heaps */
+	if (heap->socket_id < RTE_MAX_NUMA_NODES) {
+		rte_errno = EPERM;
+		ret = -1;
+		goto unlock;
+	}
+
+	/* find corresponding memseg list to attach to */
+	wa.va_addr = va_addr;
+	wa.len = len;
+	wa.result = -ENOENT; /* fail unless explicitly told to succeed */
+
+	/* we're already holding a read lock */
+	rte_memseg_list_walk_thread_unsafe(attach_mem_walk, &wa);
+
+	if (wa.result < 0) {
+		rte_errno = -wa.result;
+		ret = -1;
+	} else {
+		ret = 0;
+	}
+unlock:
+	rte_rwlock_read_unlock(&mcfg->memory_hotplug_lock);
+	return ret;
+}
+
 int
 rte_malloc_heap_create(const char *heap_name)
 {
@@ -425,6 +508,11 @@ rte_malloc_heap_create(const char *heap_name)
 	struct malloc_heap *heap = NULL;
 	int i, ret;
 
+	if (rte_eal_process_type() != RTE_PROC_PRIMARY) {
+		rte_errno = E_RTE_SECONDARY;
+		return -1;
+	}
+
 	if (heap_name == NULL ||
 			strnlen(heap_name, RTE_HEAP_NAME_MAX_LEN) == 0 ||
 			strnlen(heap_name, RTE_HEAP_NAME_MAX_LEN) ==
@@ -476,6 +564,11 @@ rte_malloc_heap_destroy(const char *heap_name)
 	struct malloc_heap *heap = NULL;
 	int ret;
 
+	if (rte_eal_process_type() != RTE_PROC_PRIMARY) {
+		rte_errno = E_RTE_SECONDARY;
+		return -1;
+	}
+
 	if (heap_name == NULL ||
 			strnlen(heap_name, RTE_HEAP_NAME_MAX_LEN) == 0 ||
 			strnlen(heap_name, RTE_HEAP_NAME_MAX_LEN) ==
diff --git a/lib/librte_eal/rte_eal_version.map b/lib/librte_eal/rte_eal_version.map
index 8c66d0be9..920852042 100644
--- a/lib/librte_eal/rte_eal_version.map
+++ b/lib/librte_eal/rte_eal_version.map
@@ -322,6 +322,7 @@ EXPERIMENTAL {
 	rte_malloc_heap_destroy;
 	rte_malloc_heap_get_socket;
 	rte_malloc_heap_memory_add;
+	rte_malloc_heap_memory_attach;
 	rte_malloc_heap_memory_remove;
 	rte_malloc_heap_socket_is_external;
 	rte_mem_alloc_validator_register;
