From d160381146f1b25a5ee815f86c25080f646b9e85 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=81ukasz=20Plewa?= <lukasz.plewa@intel.com>
Date: Wed, 12 Aug 2020 16:34:34 +0200
Subject: [PATCH] pmem2: add pmem2_map_from_existing function

---
 doc/Makefile                                  |   3 +-
 doc/libpmem2/.gitignore                       |   1 +
 doc/libpmem2/pmem2_map_delete.3.md            |   4 +
 doc/libpmem2/pmem2_map_from_existing.3.md     |  63 ++++++
 src/PMDK.sln                                  |   7 +
 src/include/libpmem2.h                        |   4 +
 src/libpmem2/libpmem2.def                     |   1 +
 src/libpmem2/libpmem2.link.in                 |   1 +
 src/libpmem2/map.c                            |  52 +++++
 src/libpmem2/map_posix.c                      |  37 ++--
 src/libpmem2/map_windows.c                    |  28 +--
 src/test/Makefile                             |   1 +
 src/test/pmem2_integration/TESTS.py           |  16 ++
 .../pmem2_integration/pmem2_integration.c     |  90 +++++++++
 src/test/pmem2_map_from_existing/.gitignore   |   1 +
 src/test/pmem2_map_from_existing/Makefile     |  20 ++
 src/test/pmem2_map_from_existing/TESTS.py     |  42 ++++
 .../pmem2_map_from_existing.c                 | 189 ++++++++++++++++++
 .../pmem2_map_from_existing.vcxproj           | 116 +++++++++++
 .../pmem2_map_from_existing.vcxproj.filters   | 106 ++++++++++
 src/test/scope/out13.log.match                |   1 +
 src/test/scope/out14.log.match                |   1 +
 src/test/unittest/unittest.h                  |   6 +
 src/test/unittest/ut_file.c                   |  52 ++++-
 24 files changed, 812 insertions(+), 30 deletions(-)
 create mode 100644 doc/libpmem2/pmem2_map_from_existing.3.md
 create mode 100644 src/test/pmem2_map_from_existing/.gitignore
 create mode 100644 src/test/pmem2_map_from_existing/Makefile
 create mode 100644 src/test/pmem2_map_from_existing/TESTS.py
 create mode 100644 src/test/pmem2_map_from_existing/pmem2_map_from_existing.c
 create mode 100644 src/test/pmem2_map_from_existing/pmem2_map_from_existing.vcxproj
 create mode 100644 src/test/pmem2_map_from_existing/pmem2_map_from_existing.vcxproj.filters

diff --git a/doc/Makefile b/doc/Makefile
index c0b4e4bec..d05bc8a5e 100644
--- a/doc/Makefile
+++ b/doc/Makefile
@@ -107,7 +107,8 @@ MANPAGES_3_MD_PMEM2 = libpmem2/pmem2_errormsg.3.md libpmem2/pmem2_config_new.3.m
 		libpmem2/pmem2_badblock_context_new.3.md libpmem2/pmem2_badblock_next.3.md \
 		libpmem2/pmem2_badblock_clear.3.md libpmem2/pmem2_config_set_protection.3.md \
 		libpmem2/pmem2_deep_flush.3.md libpmem2/pmem2_source_from_anon.3.md \
-		libpmem2/pmem2_source_device_id.3.md libpmem2/pmem2_source_device_usc.3.md
+		libpmem2/pmem2_source_device_id.3.md libpmem2/pmem2_source_device_usc.3.md \
+		libpmem2/pmem2_map_from_existing.3.md
 MANPAGES_1_MD_PMEM2 =
 ifeq ($(PMEM2_INSTALL),y)
 MANPAGES_3_DUMMY += libpmem2/pmem2_config_delete.3 libpmem2/pmem2_source_from_handle.3 libpmem2/pmem2_source_delete.3 \
diff --git a/doc/libpmem2/.gitignore b/doc/libpmem2/.gitignore
index 82f48e6f3..822fbc50c 100644
--- a/doc/libpmem2/.gitignore
+++ b/doc/libpmem2/.gitignore
@@ -19,6 +19,7 @@ pmem2_get_flush_fn.3
 pmem2_get_memmove_fn.3
 pmem2_get_persist_fn.3
 pmem2_map_delete.3
+pmem2_map_from_existing.3
 pmem2_map_new.3
 pmem2_map_get_address.3
 pmem2_map_get_size.3
diff --git a/doc/libpmem2/pmem2_map_delete.3.md b/doc/libpmem2/pmem2_map_delete.3.md
index 04dbfae48..4ecf12385 100644
--- a/doc/libpmem2/pmem2_map_delete.3.md
+++ b/doc/libpmem2/pmem2_map_delete.3.md
@@ -42,6 +42,10 @@ If the function fails, the *map_ptr* variable and the map object itself are left
 unmodified and appropriate error value is returned. For a list of possible
 return values please see [RETURN VALUE](#return-value).
 
+The **pmem2_map_delete**() function will not unmap mapping provided by the user
+by **pmem2_map_from_existing**() function. In such case it will only free
+*struct pmem2_map* object.
+
 # RETURN VALUE #
 
 The **pmem2_map_delete**() function returns 0 on success
diff --git a/doc/libpmem2/pmem2_map_from_existing.3.md b/doc/libpmem2/pmem2_map_from_existing.3.md
new file mode 100644
index 000000000..002fad9d7
--- /dev/null
+++ b/doc/libpmem2/pmem2_map_from_existing.3.md
@@ -0,0 +1,63 @@
+---
+layout: manual
+Content-Style: 'text/css'
+title: _MP(PMEM2_MAP_FROM_EXISTING, 3)
+collection: libpmem2
+header: PMDK
+date: pmem2 API version 1.0
+...
+
+[comment]: <> (SPDX-License-Identifier: BSD-3-Clause)
+[comment]: <> (Copyright 2020, Intel Corporation)
+
+[comment]: <> (pmem2_map_from_existing.3 -- man page for libpmem2 pmem2_map_from_existing operation)
+
+[NAME](#name)<br />
+[SYNOPSIS](#synopsis)<br />
+[DESCRIPTION](#description)<br />
+[RETURN VALUE](#return-value)<br />
+[ERRORS](#errors)<br />
+[SEE ALSO](#see-also)<br />
+
+# NAME #
+
+**pmem2_map_from_existing**() - creates a pmem2_map object from an existing mapping
+
+# SYNOPSIS #
+
+```c
+#include <libpmem2.h>
+
+int pmem2_map_from_existing(struct pmem2_map **map, const struct pmem2_source *src,
+	void *addr, size_t len, enum pmem2_granularity gran);
+
+```
+
+# DESCRIPTION #
+
+The **pmem2_map_from_existing**() returns a new *struct pmem2_map** for mapping
+provided by the user. This function allows usage of **libpmem2**(7) API without **pmem2_map_new**(3) for mapping file.
+Mapping is defined by *addr* and *len*. You have to specify underlying file as a *src*, and define granularity of this mapping.
+See **pmem2_config_set_required_store_granularity**(3) and **libpmem2**(7) for more details.
+
+For the *pmem2_map* object created by the **pmem2_map_from_existing**(3) function, the **pmem2_map_delete**(3) will only destroy the object,
+but it won't unmap the mapping this object describes.
+
+# RETURN VALUE #
+
+The **pmem2_map_from_existing**() function returns 0 when it succeeds
+or a negative error code on failure.
+
+# ERRORS #
+
+The **pmem2_map_from_existing**() can fail with the following errors:
+
+**PMEM2_E_MAPPING_EXISTS** - when contiguous region (*addr*, *addr* + *len*)
+is all ready registered by *libpmem2*
+
+It can also return **-ENOMEM**  from the underlying **malloc**(2) function.
+
+# SEE ALSO #
+
+**malloc(2)**, **pmem2_map_delete**(3), **pmem2_map_new**(3),
+**pmem2_source_from_fd**(3), **libpmem2**(7) and **<http://pmem.io>**
diff --git a/src/PMDK.sln b/src/PMDK.sln
index 7a5569642..f80df1322 100644
--- a/src/PMDK.sln
+++ b/src/PMDK.sln
@@ -747,6 +747,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pmem_is_pmem", "test\pmem_i
 EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "util_poolset_foreach", "test\util_poolset_foreach\util_poolset_foreach.vcxproj", "{E648732D-78FA-427A-928C-9A59222D37B7}"
 EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pmem2_map_from_existing", "test\pmem2_map_from_existing\pmem2_map_from_existing.vcxproj", "{E660218B-3B2D-4378-A2CD-78B865764CF1}"
+EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "log_pool_lock", "test\log_pool_lock\log_pool_lock.vcxproj", "{E68DEB59-C709-4945-AF80-EEBCADDED944}"
 EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "obj_constructor", "test\obj_constructor\obj_constructor.vcxproj", "{E7691F81-86EF-467D-82E1-F5B9416386F9}"
@@ -1785,6 +1787,10 @@ Global
 		{E648732D-78FA-427A-928C-9A59222D37B7}.Debug|x64.Build.0 = Debug|x64
 		{E648732D-78FA-427A-928C-9A59222D37B7}.Release|x64.ActiveCfg = Release|x64
 		{E648732D-78FA-427A-928C-9A59222D37B7}.Release|x64.Build.0 = Release|x64
+		{E660218B-3B2D-4378-A2CD-78B865764CF1}.Debug|x64.ActiveCfg = Debug|x64
+		{E660218B-3B2D-4378-A2CD-78B865764CF1}.Debug|x64.Build.0 = Debug|x64
+		{E660218B-3B2D-4378-A2CD-78B865764CF1}.Release|x64.ActiveCfg = Release|x64
+		{E660218B-3B2D-4378-A2CD-78B865764CF1}.Release|x64.Build.0 = Release|x64
 		{E68DEB59-C709-4945-AF80-EEBCADDED944}.Debug|x64.ActiveCfg = Debug|x64
 		{E68DEB59-C709-4945-AF80-EEBCADDED944}.Debug|x64.Build.0 = Debug|x64
 		{E68DEB59-C709-4945-AF80-EEBCADDED944}.Release|x64.ActiveCfg = Release|x64
@@ -2183,6 +2189,7 @@ Global
 		{E3229AF7-1FA2-4632-BB0B-B74F709F1A33} = {F42C09CD-ABA5-4DA9-8383-5EA40FA4D763}
 		{E4E2EC33-7902-45D0-9C3C-ADBAFA46874A} = {F8373EDD-1B9E-462D-BF23-55638E23E98B}
 		{E648732D-78FA-427A-928C-9A59222D37B7} = {4C291EEB-3874-4724-9CC2-1335D13FF0EE}
+		{E660218B-3B2D-4378-A2CD-78B865764CF1} = {A14A4556-9092-430D-B9CA-B2B1223D56CB}
 		{E68DEB59-C709-4945-AF80-EEBCADDED944} = {1A36B57B-2E88-4D81-89C0-F575C9895E36}
 		{E7691F81-86EF-467D-82E1-F5B9416386F9} = {63C9B3F8-437D-4AD9-B32D-D04AE38C35B6}
 		{E796AA20-D664-4D05-ABD9-C93A4FBE3E5C} = {4C291EEB-3874-4724-9CC2-1335D13FF0EE}
diff --git a/src/include/libpmem2.h b/src/include/libpmem2.h
index 3c2fcb77c..f57cf7e70 100644
--- a/src/include/libpmem2.h
+++ b/src/include/libpmem2.h
@@ -69,6 +69,7 @@ extern "C" {
 #define PMEM2_E_INVALID_PROT_FLAG		(-100031)
 #define PMEM2_E_NO_ACCESS			(-100032)
 #define PMEM2_E_VM_RESERVATION_NOT_EMPTY	(-100033)
+#define PMEM2_E_MAP_EXISTS			(-100034)
 
 /* source setup */
 
@@ -153,6 +154,9 @@ void pmem2_config_clear_address(struct pmem2_config *cfg);
 /* mapping */
 
 struct pmem2_map;
+int pmem2_map_from_existing(struct pmem2_map **map,
+	const struct pmem2_source *src, void *addr, size_t len,
+	enum pmem2_granularity gran);
 
 int pmem2_map_new(struct pmem2_map **map_ptr, const struct pmem2_config *cfg,
 		const struct pmem2_source *src);
diff --git a/src/libpmem2/libpmem2.def b/src/libpmem2/libpmem2.def
index 86a2d6b5b..1f3beb6f6 100644
--- a/src/libpmem2/libpmem2.def
+++ b/src/libpmem2/libpmem2.def
@@ -35,6 +35,7 @@ EXPORTS
 	pmem2_map_get_size
 	pmem2_map_get_store_granularity
 	pmem2_map_new
+	pmem2_map_from_existing
 	pmem2_perrorU
 	pmem2_perrorW
 	pmem2_source_alignment
diff --git a/src/libpmem2/libpmem2.link.in b/src/libpmem2/libpmem2.link.in
index d9a9674c2..ba6ea7e2e 100644
--- a/src/libpmem2/libpmem2.link.in
+++ b/src/libpmem2/libpmem2.link.in
@@ -33,6 +33,7 @@ LIBPMEM2_1.0 {
 		pmem2_map_get_size;
 		pmem2_map_get_store_granularity;
 		pmem2_map_new;
+		pmem2_map_from_existing;
 		pmem2_perror;
 		pmem2_source_alignment;
 		pmem2_source_delete;
diff --git a/src/libpmem2/map.c b/src/libpmem2/map.c
index edea3131c..514bed7ec 100644
--- a/src/libpmem2/map.c
+++ b/src/libpmem2/map.c
@@ -7,15 +7,18 @@
 
 #include "out.h"
 
+#include "alloc.h"
 #include "config.h"
 #include "map.h"
 #include "ravl_interval.h"
 #include "os.h"
 #include "os_thread.h"
+#include "persist.h"
 #include "pmem2.h"
 #include "pmem2_utils.h"
 #include "ravl.h"
 #include "sys_util.h"
+#include "valgrind_internal.h"
 
 #include <libpmem2.h>
 
@@ -234,3 +237,52 @@ pmem2_map_find(const void *addr, size_t len)
 
 	return (struct pmem2_map *)ravl_interval_data(node);
 }
+
+/*
+ * pmem2_map_from_existing -- create map object for exisiting mapping
+ */
+int
+pmem2_map_from_existing(struct pmem2_map **map_ptr,
+	const struct pmem2_source *src, void *addr, size_t len,
+	enum pmem2_granularity gran)
+{
+	int ret;
+	struct pmem2_map *map =
+		(struct pmem2_map *)pmem2_malloc(sizeof(*map), &ret);
+
+	if (!map)
+		return ret;
+
+	map->reserv = NULL;
+	map->addr = addr;
+	map->reserved_length = 0;
+	map->content_length = len;
+	map->effective_granularity = gran;
+	pmem2_set_flush_fns(map);
+	pmem2_set_mem_fns(map);
+	map->source = *src;
+
+#ifndef _WIN32
+	/* fd should not be used after map */
+	map->source.value.fd = INVALID_FD;
+#endif
+	ret = pmem2_register_mapping(map);
+	if (ret) {
+		Free(map);
+		if (ret == -EEXIST) {
+			ERR(
+				"Provided mappping(addr %p len %zu) is already registered by libpmem2",
+				addr, len);
+			return PMEM2_E_MAP_EXISTS;
+		}
+		return ret;
+	}
+#ifndef _WIN32
+	if (src->type == PMEM2_SOURCE_FD) {
+		VALGRIND_REGISTER_PMEM_MAPPING(map->addr,
+			map->content_length);
+	}
+#endif
+	*map_ptr = map;
+	return 0;
+}
diff --git a/src/libpmem2/map_posix.c b/src/libpmem2/map_posix.c
index 90e694666..176257d81 100644
--- a/src/libpmem2/map_posix.c
+++ b/src/libpmem2/map_posix.c
@@ -584,21 +584,28 @@ pmem2_map_delete(struct pmem2_map **map_ptr)
 	if (ret)
 		return ret;
 
-	VALGRIND_REMOVE_PMEM_MAPPING(map->addr, map->content_length);
-
-	if (map->reserv) {
-		ret = vm_reservation_map_unregister(map->reserv, map);
-		if (ret)
-			return ret;
-
-		ret = vm_reservation_mend(map->reserv, map->addr,
-				map->reserved_length);
-		if (ret)
-			return ret;
-	} else {
-		ret = unmap(map->addr, map->reserved_length);
-		if (ret)
-			return ret;
+	/*
+	 * when reserved_length==0 mapping is created by pmem2_map_from_existing
+	 * such mappings are provided by the users and shouldn't be unmapped
+	 * by pmem2.
+	 */
+	if (map->reserved_length) {
+		VALGRIND_REMOVE_PMEM_MAPPING(map->addr, map->content_length);
+
+		if (map->reserv) {
+			ret = vm_reservation_map_unregister(map->reserv, map);
+			if (ret)
+				return ret;
+
+			ret = vm_reservation_mend(map->reserv, map->addr,
+					map->reserved_length);
+			if (ret)
+				return ret;
+		} else {
+			ret = unmap(map->addr, map->reserved_length);
+			if (ret)
+				return ret;
+		}
 	}
 
 	Free(map);
diff --git a/src/libpmem2/map_windows.c b/src/libpmem2/map_windows.c
index 315dffbcd..d009fc031 100644
--- a/src/libpmem2/map_windows.c
+++ b/src/libpmem2/map_windows.c
@@ -529,21 +529,23 @@ pmem2_map_delete(struct pmem2_map **map_ptr)
 	if (ret)
 		return ret;
 
-	if (map->reserv) {
-		util_rwlock_wrlock(&split_merge_lock);
-		ret = reservation_mend(map->reserv, map->addr,
+	if (map->reserved_length != 0) {
+		if (map->reserv) {
+			util_rwlock_wrlock(&split_merge_lock);
+			ret = reservation_mend(map->reserv, map->addr,
 				map->reserved_length);
-		util_rwlock_unlock(&split_merge_lock);
-		if (ret)
-			return ret;
+			util_rwlock_unlock(&split_merge_lock);
+			if (ret)
+				return ret;
 
-		ret = vm_reservation_map_unregister(map->reserv, map);
-		if (ret)
-			return ret;
-	} else {
-		if (!UnmapViewOfFile(map->addr)) {
-			ERR("!!UnmapViewOfFile");
-			return pmem2_lasterror_to_err();
+			ret = vm_reservation_map_unregister(map->reserv, map);
+			if (ret)
+				return ret;
+		} else {
+			if (!UnmapViewOfFile(map->addr)) {
+				ERR("!!UnmapViewOfFile");
+				return pmem2_lasterror_to_err();
+			}
 		}
 	}
 
diff --git a/src/test/Makefile b/src/test/Makefile
index 0568d5ff1..d07df8e40 100644
--- a/src/test/Makefile
+++ b/src/test/Makefile
@@ -207,6 +207,7 @@ PMEM2_TESTS = \
 	pmem2_integration\
 	pmem2_granularity_detection\
 	pmem2_map\
+	pmem2_map_from_existing\
 	pmem2_map_prot\
 	pmem2_persist\
 	pmem2_persist_valgrind\
diff --git a/src/test/pmem2_integration/TESTS.py b/src/test/pmem2_integration/TESTS.py
index 24ba2991c..faf90e8cd 100755
--- a/src/test/pmem2_integration/TESTS.py
+++ b/src/test/pmem2_integration/TESTS.py
@@ -287,3 +287,19 @@ class TEST37(PMEM2_INTEGRATION_DEV_DAXES):
 class TEST38(PMEM2_INTEGRATION):
     """test for unaligned persists"""
     test_case = "test_unaligned_persist"
+
+
+class TEST39(PMEM2_INTEGRATION):
+    """compare normal map vs map_from_existing"""
+    test_case = "test_map_from_existing"
+
+
+class TES40(PMEM2_INTEGRATION):
+    """test map_from_existing from pmem2_mapping"""
+    test_case = "test_map_from_existing_map"
+
+
+@t.windows_exclude
+class TEST41(PMEM2_INTEGRATION_DEV_DAXES):
+    """compare normal map vs map_from_existing on devdax"""
+    test_case = "test_map_from_existing"
diff --git a/src/test/pmem2_integration/pmem2_integration.c b/src/test/pmem2_integration/pmem2_integration.c
index d7fc7aed8..8b186a248 100644
--- a/src/test/pmem2_integration/pmem2_integration.c
+++ b/src/test/pmem2_integration/pmem2_integration.c
@@ -897,6 +897,94 @@ test_source_anon_too_small(const struct test_case *tc, int argc, char *argv[])
 	return 1;
 }
 
+/*
+ * test_map_from_existing_map -- compare normal map with map_from_existing
+ */
+static int
+test_map_from_existing_map(const struct test_case *tc, int argc, char *argv[])
+{
+	if (argc < 1)
+		UT_FATAL("usage: map_from_existing_map <file>");
+
+	char *file = argv[0];
+	int fd = OPEN(file, O_RDWR);
+
+	struct pmem2_config *cfg;
+	struct pmem2_source *src;
+	PMEM2_PREPARE_CONFIG_INTEGRATION(&cfg, &src, fd,
+		PMEM2_GRANULARITY_PAGE);
+
+	size_t size;
+	UT_ASSERTeq(pmem2_source_size(src, &size), 0);
+
+	struct pmem2_map *map1 = map_valid(cfg, src, size);
+
+	/* cleanup after the test */
+	struct pmem2_map *map2;
+	UT_PMEM2_EXPECT_RETURN(pmem2_map_from_existing(&map2, src,
+			pmem2_map_get_address(map1), pmem2_map_get_size(map1),
+			pmem2_map_get_store_granularity(map1)),
+			PMEM2_E_MAP_EXISTS);
+
+	pmem2_map_delete(&map1);
+	pmem2_config_delete(&cfg);
+	pmem2_source_delete(&src);
+	CLOSE(fd);
+	return 1;
+}
+
+/*
+ * test_map_from_existing -- compare normal map with map_from_existing
+ */
+static int
+test_map_from_existing(const struct test_case *tc, int argc, char *argv[])
+{
+	if (argc < 1)
+		UT_FATAL("usage: test_map_from_existing <file>");
+
+	char *file = argv[0];
+	int fd = OPEN(file, O_RDWR);
+
+	struct pmem2_source *src;
+	struct pmem2_config *cfg;
+	PMEM2_PREPARE_CONFIG_INTEGRATION(&cfg, &src, fd,
+		PMEM2_GRANULARITY_PAGE);
+
+	size_t size;
+	UT_ASSERTeq(pmem2_source_size(src, &size), 0);
+
+	struct pmem2_map *map = map_valid(cfg, src, size);
+	enum pmem2_granularity gran = pmem2_map_get_store_granularity(map);
+
+	void *persist = pmem2_get_persist_fn(map);
+	void *flush = pmem2_get_flush_fn(map);
+	void *drain = pmem2_get_drain_fn(map);
+	void *memmove = pmem2_get_memmove_fn(map);
+	void *memcpy = pmem2_get_memcpy_fn(map);
+	void *memset = pmem2_get_memset_fn(map);
+
+	UT_PMEM2_EXPECT_RETURN(pmem2_map_delete(&map), 0);
+	void *addr = FILE_MAP(fd, size);
+	UT_ASSERTeq(pmem2_map_from_existing(&map, src,
+			addr, size, gran), 0);
+	UT_ASSERTeq(pmem2_map_get_address(map), addr);
+	UT_ASSERTeq(pmem2_map_get_size(map), size);
+	UT_ASSERTeq(pmem2_map_get_store_granularity(map), gran);
+	UT_ASSERTeq(pmem2_get_persist_fn(map), persist);
+	UT_ASSERTeq(pmem2_get_flush_fn(map), flush);
+	UT_ASSERTeq(pmem2_get_drain_fn(map), drain);
+	UT_ASSERTeq(pmem2_get_memmove_fn(map), memmove);
+	UT_ASSERTeq(pmem2_get_memcpy_fn(map), memcpy);
+	UT_ASSERTeq(pmem2_get_memset_fn(map), memset);
+
+	pmem2_map_delete(&map);
+	pmem2_config_delete(&cfg);
+	pmem2_source_delete(&src);
+	CLOSE(fd);
+	return 1;
+}
+#undef COMPARE_FUNCS
+
 /*
  * test_cases -- available test cases
  */
@@ -922,6 +1010,8 @@ static struct test_case test_cases[] = {
 	TEST_CASE(test_source_anon_too_small),
 	TEST_CASE(test_source_anon_zero_len),
 	TEST_CASE(test_unaligned_persist),
+	TEST_CASE(test_map_from_existing_map),
+	TEST_CASE(test_map_from_existing)
 };
 
 #define NTESTS (sizeof(test_cases) / sizeof(test_cases[0]))
diff --git a/src/test/pmem2_map_from_existing/.gitignore b/src/test/pmem2_map_from_existing/.gitignore
new file mode 100644
index 000000000..264b9b7af
--- /dev/null
+++ b/src/test/pmem2_map_from_existing/.gitignore
@@ -0,0 +1 @@
+pmem2_map_from_existing
diff --git a/src/test/pmem2_map_from_existing/Makefile b/src/test/pmem2_map_from_existing/Makefile
new file mode 100644
index 000000000..cc28900ba
--- /dev/null
+++ b/src/test/pmem2_map_from_existing/Makefile
@@ -0,0 +1,20 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright 2020, Intel Corporation
+#
+
+#
+# src/test/pmem2_map_from_existing/Makefile -- build pmem2_map unit test
+#
+TOP = ../../..
+
+vpath %.c $(TOP)/src/test/unittest
+
+INCS += -I$(TOP)/src/libpmem2
+TARGET = pmem2_map_from_existing
+OBJS += pmem2_map_from_existing.o\
+	ut_pmem2_utils.o\
+	ut_pmem2_source.o
+
+LIBPMEM2=internal-debug
+LIBPMEMCORE=y
+include ../Makefile.inc
diff --git a/src/test/pmem2_map_from_existing/TESTS.py b/src/test/pmem2_map_from_existing/TESTS.py
new file mode 100644
index 000000000..7fbb6ca7b
--- /dev/null
+++ b/src/test/pmem2_map_from_existing/TESTS.py
@@ -0,0 +1,42 @@
+#!../env.py
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright 2020, Intel Corporation
+#
+
+
+import testframework as t
+from testframework import granularity as g
+
+
+@g.require_granularity(g.ANY)
+class Pmem2_from_existing(t.Test):
+    test_type = t.Short
+
+    def run(self, ctx):
+        filepath = ctx.create_holey_file(16 * t.MiB, 'testfile1')
+        ctx.exec('pmem2_map_from_existing', self.test_case, filepath)
+
+
+class TEST0(Pmem2_from_existing):
+    """try to create two the same mappings"""
+    test_case = "test_two_same_mappings"
+
+
+class TEST1(Pmem2_from_existing):
+    """try to map which overlap bottom part of exisiting mapping"""
+    test_case = "test_mapping_overlap_bottom"
+
+
+class TEST2(Pmem2_from_existing):
+    """try to map which overlap upper part of exisiting mapping"""
+    test_case = "test_mapping_overlap_upper"
+
+
+class TEST3(Pmem2_from_existing):
+    """inject enomem in to allocation of map object"""
+    test_case = "test_map_allocation_enomem"
+
+
+class TEST4(Pmem2_from_existing):
+    """inject enomem during adding map to ravl"""
+    test_case = "test_register_mapping_enomem"
diff --git a/src/test/pmem2_map_from_existing/pmem2_map_from_existing.c b/src/test/pmem2_map_from_existing/pmem2_map_from_existing.c
new file mode 100644
index 000000000..5382c7c81
--- /dev/null
+++ b/src/test/pmem2_map_from_existing/pmem2_map_from_existing.c
@@ -0,0 +1,189 @@
+// SPDX-License-Identifier: BSD-3-Clause
+/* Copyright 2020, Intel Corporation */
+
+/*
+ * pmem2_map_from_existing.c -- pmem2_map_from_existing unittests
+ */
+
+#include <stdbool.h>
+#include "fault_injection.h"
+#include "unittest.h"
+#include "ut_pmem2_utils.h"
+
+/*
+ * test_two_same_mappings - try to create two the same mappings
+ */
+static int
+test_two_same_mappings(const struct test_case *tc, int argc, char *argv[])
+{
+	struct pmem2_map *map1 = NULL;
+	struct pmem2_map *map2 = NULL;
+	struct pmem2_source *src;
+
+	int fd = OPEN(argv[0], O_RDWR);
+	int ret = pmem2_source_from_fd(&src, fd);
+	UT_ASSERTeq(ret, 0);
+
+	ret = pmem2_map_from_existing(&map1, src, (void *)0xFFFF, 0xFF,
+		PMEM2_GRANULARITY_PAGE);
+
+	UT_PMEM2_EXPECT_RETURN(ret, 0);
+	UT_ASSERTne(map1, NULL);
+
+	ret = pmem2_map_from_existing(&map2, src, (void *)0xFFFF, 0xFF,
+		PMEM2_GRANULARITY_PAGE);
+
+	UT_PMEM2_EXPECT_RETURN(ret, PMEM2_E_MAP_EXISTS);
+	UT_ASSERTeq(map2, NULL);
+
+	pmem2_map_delete(&map1);
+	CLOSE(fd);
+	return 1;
+}
+
+/*
+ * test_mapping_overlap_bottom - try to map which overlap
+ *		bottom part of exisiting mapping
+ */
+static int
+test_mapping_overlap_bottom(const struct test_case *tc, int argc, char *argv[])
+{
+	struct pmem2_map *map1 = NULL;
+	struct pmem2_map *map2 = NULL;
+	struct pmem2_source *src;
+
+	int fd = OPEN(argv[0], O_RDWR);
+	int ret = pmem2_source_from_fd(&src, fd);
+	UT_ASSERTeq(ret, 0);
+
+	ret = pmem2_map_from_existing(&map1, src, (void *)0xFFFF, 0xFF,
+		PMEM2_GRANULARITY_PAGE);
+
+	UT_PMEM2_EXPECT_RETURN(ret, 0);
+	UT_ASSERTne(map1, NULL);
+
+	ret = pmem2_map_from_existing(&map2, src, (void *)0xFFF0, 0xFF,
+		PMEM2_GRANULARITY_PAGE);
+
+	UT_PMEM2_EXPECT_RETURN(ret, PMEM2_E_MAP_EXISTS);
+	UT_ASSERTeq(map2, NULL);
+
+	pmem2_map_delete(&map1);
+	CLOSE(fd);
+	return 1;
+}
+
+/*
+ * test_mapping_overlap_upper - try to map which overlap
+ *		upper part of exisiting mapping
+ */
+static int
+test_mapping_overlap_upper(const struct test_case *tc, int argc, char *argv[])
+{
+	struct pmem2_map *map1 = NULL;
+	struct pmem2_map *map2 = NULL;
+	struct pmem2_source *src;
+
+	int fd = OPEN(argv[0], O_RDWR);
+	int ret = pmem2_source_from_fd(&src, fd);
+	UT_ASSERTeq(ret, 0);
+
+	ret = pmem2_map_from_existing(&map1, src, (void *)0x0FFFF, 0xFF,
+		PMEM2_GRANULARITY_PAGE);
+
+	UT_PMEM2_EXPECT_RETURN(ret, 0);
+	UT_ASSERTne(map1, NULL);
+
+	ret = pmem2_map_from_existing(&map2, src, (void *)(0x0FFFF + 0x1),
+		0xFFFF, PMEM2_GRANULARITY_PAGE);
+
+	UT_PMEM2_EXPECT_RETURN(ret, PMEM2_E_MAP_EXISTS);
+	UT_ASSERTeq(map2, NULL);
+
+	pmem2_map_delete(&map1);
+	CLOSE(fd);
+	return 1;
+}
+
+/*
+ * test_map_allocation_enomem - inject enomem in to allocation of map object
+ */
+static int
+test_map_allocation_enomem(const struct test_case *tc, int argc, char *argv[])
+{
+	struct pmem2_map *map = NULL;
+	struct pmem2_source *src;
+
+	if (!core_fault_injection_enabled()) {
+		return 1;
+	}
+
+	int fd = OPEN(argv[0], O_RDWR);
+	int ret = pmem2_source_from_fd(&src, fd);
+	UT_ASSERTeq(ret, 0);
+
+	core_inject_fault_at(PMEM_MALLOC, 1, "pmem2_malloc");
+	ret = pmem2_map_from_existing(&map, src, (void *)0x0FFFF, 0xFF,
+		PMEM2_GRANULARITY_PAGE);
+
+	UT_PMEM2_EXPECT_RETURN(ret, -ENOMEM);
+	UT_ASSERTeq(map, NULL);
+
+	CLOSE(fd);
+	return 1;
+}
+
+/*
+ * test_register_mapping_enomem - inject enomem during adding map to ravl
+ */
+static int
+test_register_mapping_enomem(const struct test_case *tc, int argc, char *argv[])
+{
+	struct pmem2_map *map = NULL;
+	struct pmem2_source *src;
+
+	if (!core_fault_injection_enabled()) {
+		return 1;
+	}
+
+	int fd = OPEN(argv[0], O_RDWR);
+	int ret = pmem2_source_from_fd(&src, fd);
+	UT_ASSERTeq(ret, 0);
+
+	core_inject_fault_at(PMEM_MALLOC, 1, "ravl_new_node");
+	ret = pmem2_map_from_existing(&map, src, (void *)0x0FFFF, 0xFF,
+		PMEM2_GRANULARITY_PAGE);
+
+	UT_PMEM2_EXPECT_RETURN(ret, -ENOMEM);
+	UT_ASSERTeq(map, NULL);
+
+	CLOSE(fd);
+	return 1;
+}
+
+/*
+ * test_cases -- available test cases
+ */
+static struct test_case test_cases[] = {
+	TEST_CASE(test_two_same_mappings),
+	TEST_CASE(test_mapping_overlap_bottom),
+	TEST_CASE(test_mapping_overlap_upper),
+	TEST_CASE(test_map_allocation_enomem),
+	TEST_CASE(test_register_mapping_enomem),
+
+};
+
+#define NTESTS (sizeof(test_cases) / sizeof(test_cases[0]))
+
+int
+main(int argc, char *argv[])
+{
+	START(argc, argv, "pmem2_map_from_existing");
+	TEST_CASE_PROCESS(argc, argv, test_cases, NTESTS);
+	DONE(NULL);
+}
+
+#ifdef _MSC_VER
+MSVC_CONSTR(libpmem2_init)
+MSVC_DESTR(libpmem2_fini)
+#endif
diff --git a/src/test/pmem2_map_from_existing/pmem2_map_from_existing.vcxproj b/src/test/pmem2_map_from_existing/pmem2_map_from_existing.vcxproj
new file mode 100644
index 000000000..8fadc4080
--- /dev/null
+++ b/src/test/pmem2_map_from_existing/pmem2_map_from_existing.vcxproj
@@ -0,0 +1,116 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{E660218B-3B2D-4378-A2CD-78B865764CF1}</ProjectGuid>
+    <RootNamespace>pmem2_map_from_existing</RootNamespace>
+    <WindowsTargetPlatformVersion>10.0.17134.0</WindowsTargetPlatformVersion>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v140</PlatformToolset>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v140</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="Shared">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="..\test_debug.props" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="..\test_release.props" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup />
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <SDLCheck>true</SDLCheck>
+      <PreprocessorDefinitions>SSE2_AVAILABLE=0;AVX_AVAILABLE=0;AVX512F_AVAILABLE=0;PMDK_UTF8_API;SDS_ENABLED;NTDDI_VERSION=NTDDI_WIN10_RS1;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>$(SolutionDir)\libpmem2;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ClCompile>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <AdditionalIncludeDirectories>$(SolutionDir)\libpmem2;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>SSE2_AVAILABLE=0;AVX_AVAILABLE=0;AVX512F_AVAILABLE=0;PMDK_UTF8_API;SDS_ENABLED;NTDDI_VERSION=NTDDI_WIN10_RS1;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+    <Link>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\..\common\libpmemcommon.vcxproj">
+      <Project>{492baa3d-0d5d-478e-9765-500463ae69aa}</Project>
+    </ProjectReference>
+    <ProjectReference Include="..\unittest\libut.vcxproj">
+      <Project>{ce3f2dfb-8470-4802-ad37-21caf6cb2681}</Project>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\common\ravl.c" />
+    <ClCompile Include="..\..\libpmem2\config.c" />
+    <ClCompile Include="..\..\libpmem2\deep_flush_windows.c" />
+    <ClCompile Include="..\..\libpmem2\errormsg.c" />
+    <ClCompile Include="..\..\libpmem2\libpmem2.c" />
+    <ClCompile Include="..\..\libpmem2\map.c" />
+    <ClCompile Include="..\..\libpmem2\map_windows.c" />
+    <ClCompile Include="..\..\libpmem2\memops_generic.c" />
+    <ClCompile Include="..\..\libpmem2\persist.c" />
+    <ClCompile Include="..\..\libpmem2\persist_windows.c" />
+    <ClCompile Include="..\..\libpmem2\pmem2_utils.c" />
+    <ClCompile Include="..\..\libpmem2\ravl_interval.c" />
+    <ClCompile Include="..\..\libpmem2\vm_reservation.c" />
+    <ClCompile Include="..\..\libpmem2\vm_reservation_windows.c" />
+    <ClCompile Include="..\..\libpmem2\x86_64\cpu.c" />
+    <ClCompile Include="..\..\libpmem2\x86_64\init.c" />
+    <ClCompile Include="..\unittest\ut_pmem2_setup.c" />
+    <ClCompile Include="..\unittest\ut_pmem2_source.c" />
+    <ClCompile Include="..\unittest\ut_pmem2_utils.c" />
+    <ClCompile Include="pmem2_map_from_existing.c" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\libpmem2\config.h" />
+    <ClInclude Include="..\..\libpmem2\map.h" />
+    <ClInclude Include="..\..\libpmem2\pmem2.h" />
+    <ClInclude Include="..\..\libpmem2\pmem2_utils.h" />
+    <ClInclude Include="..\..\libpmem2\ravl_interval.h" />
+    <ClInclude Include="..\..\libpmem2\vm_reservation.h" />
+    <ClInclude Include="..\unittest\ut_pmem2.h" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="TESTS.py" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file
diff --git a/src/test/pmem2_map_from_existing/pmem2_map_from_existing.vcxproj.filters b/src/test/pmem2_map_from_existing/pmem2_map_from_existing.vcxproj.filters
new file mode 100644
index 000000000..62a83a5b0
--- /dev/null
+++ b/src/test/pmem2_map_from_existing/pmem2_map_from_existing.vcxproj.filters
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="Header Files">
+      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+      <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
+    </Filter>
+    <Filter Include="Test Scripts">
+      <UniqueIdentifier>{4557bbd3-f8c2-4d53-821f-ec79100e2b50}</UniqueIdentifier>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="pmem2_map_from_existing.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\unittest\ut_pmem2_utils.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\common\ravl.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\libpmem2\pmem2_utils.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\libpmem2\errormsg.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\libpmem2\config.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\libpmem2\map.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\libpmem2\map_windows.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\libpmem2\libpmem2.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\libpmem2\persist.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\libpmem2\persist_windows.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\libpmem2\x86_64\cpu.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\libpmem2\x86_64\init.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\libpmem2\memops_generic.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\unittest\ut_pmem2_source.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\libpmem2\deep_flush_windows.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\unittest\ut_pmem2_setup.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\libpmem2\ravl_interval.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\libpmem2\vm_reservation_windows.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\libpmem2\vm_reservation.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\libpmem2\config.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\libpmem2\pmem2_utils.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\libpmem2\map.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\libpmem2\pmem2.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\unittest\ut_pmem2.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\libpmem2\ravl_interval.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\libpmem2\vm_reservation.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="TESTS.py">
+      <Filter>Test Scripts</Filter>
+    </None>
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/src/test/scope/out13.log.match b/src/test/scope/out13.log.match
index c95fcca7c..dc6a99df0 100644
--- a/src/test/scope/out13.log.match
+++ b/src/test/scope/out13.log.match
@@ -21,6 +21,7 @@ pmem2_get_memmove_fn$(nW)
 pmem2_get_memset_fn$(nW)
 pmem2_get_persist_fn$(nW)
 pmem2_map_delete$(nW)
+pmem2_map_from_existing$(nW)
 pmem2_map_get_address$(nW)
 pmem2_map_get_size$(nW)
 pmem2_map_get_store_granularity$(nW)
diff --git a/src/test/scope/out14.log.match b/src/test/scope/out14.log.match
index 5cdcc3df9..2eb857499 100644
--- a/src/test/scope/out14.log.match
+++ b/src/test/scope/out14.log.match
@@ -22,6 +22,7 @@ pmem2_get_memmove_fn
 pmem2_get_memset_fn
 pmem2_get_persist_fn
 pmem2_map_delete
+pmem2_map_from_existing
 pmem2_map_get_address
 pmem2_map_get_size
 pmem2_map_get_store_granularity
diff --git a/src/test/unittest/unittest.h b/src/test/unittest/unittest.h
index 1be17390d..96db8a1c8 100644
--- a/src/test/unittest/unittest.h
+++ b/src/test/unittest/unittest.h
@@ -399,6 +399,9 @@ int ut_mprotect(const char *file, int line, const char *func, void *addr,
 int ut_ftruncate(const char *file, int line, const char *func,
     int fd, os_off_t length);
 
+void *ut_file_map(const char *file, int line, const char *func, int fd,
+    size_t size);
+
 long long ut_strtoll(const char *file, int line, const char *func,
     const char *nptr, char **endptr, int base);
 
@@ -483,6 +486,9 @@ int ut_snprintf(const char *file, int line, const char *func,
 #define FTRUNCATE(fd, length)\
     ut_ftruncate(__FILE__, __LINE__, __func__, fd, length)
 
+#define FILE_MAP(fd, size)\
+    ut_file_map(__FILE__, __LINE__, __func__, fd, size);
+
 #define ATOU(nptr) STRTOU(nptr, NULL, 10)
 #define ATOUL(nptr) STRTOUL(nptr, NULL, 10)
 #define ATOULL(nptr) STRTOULL(nptr, NULL, 10)
diff --git a/src/test/unittest/ut_file.c b/src/test/unittest/ut_file.c
index 26f5cbda6..54cb1bb6f 100644
--- a/src/test/unittest/ut_file.c
+++ b/src/test/unittest/ut_file.c
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: BSD-3-Clause
-/* Copyright 2014-2018, Intel Corporation */
+/* Copyright 2014-2020, Intel Corporation */
 
 /*
  * ut_file.c -- unit test file operations
@@ -327,3 +327,53 @@ ut_ftruncate(const char *file, int line, const char *func, int fd,
 
 	return retval;
 }
+
+#ifdef _WIN32
+/*
+ * file_map -- map file without using pmdk api
+ */
+void *
+ut_file_map(const char *file, int line, const char *func, int fd, size_t size)
+{
+	void *addr = NULL;
+	HANDLE handle = (HANDLE)_get_osfhandle(fd);
+	UT_ASSERTne(handle, INVALID_HANDLE_VALUE);
+
+	HANDLE mh = CreateFileMapping(handle,
+		NULL,
+		PAGE_READWRITE,
+		size >> 32,
+		size & 0xFFFFFFFF,
+		NULL);
+
+	if (mh == INVALID_HANDLE_VALUE)
+		ut_fatal(file, line, func, "!!CreateFileMapping");
+
+	addr = MapViewOfFileEx(mh,
+		FILE_MAP_ALL_ACCESS,
+		0,
+		0,
+		size,
+		NULL);
+
+	if (addr == NULL)
+		ut_fatal(file, line, func, "!!CreateFileMapping");
+
+	if (CloseHandle(mh) == 0)
+		ut_fatal(file, line, func, "!!CloseHandle");
+
+	return addr;
+}
+#else
+/*
+ * file_map -- map file without using pmdk api
+ */
+void *
+ut_file_map(const char *file, int line, const char *func, int fd, size_t size)
+{
+	void *addr = NULL;
+	ut_mmap(file, line, func, addr, size, PROT_READ | PROT_WRITE,
+		MAP_SHARED, fd, 0);
+	return addr;
+}
+#endif
-- 
GitLab