diff --git a/Makefile.am b/Makefile.am
index 2989e076972d..7fee9c688c84 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -123,8 +123,8 @@ if ENABLE_DESTRUCTIVE
 TESTS += lib/test-blk-ns lib/test-pmem-ns lib/test-pcommit
 check_PROGRAMS += lib/test-blk-ns lib/test-pmem-ns lib/test-pcommit
 
-TESTS += lib/test-dax-dev lib/test-dax.sh
-check_PROGRAMS += lib/test-dax-dev lib/test-dax-pmd
+TESTS += lib/test-dax-dev lib/test-dax.sh lib/test-mmap.sh
+check_PROGRAMS += lib/test-dax-dev lib/test-dax-pmd lib/test-mmap
 endif
 
 lib_test_libndctl_SOURCES = lib/test-libndctl.c lib/test-core.c
@@ -149,3 +149,4 @@ lib_test_dax_dev_SOURCES = lib/test-dax-dev.c lib/test-core.c
 lib_test_dax_dev_LDADD = lib/libndctl.la
 
 lib_test_dax_pmd_SOURCES = lib/test-dax-pmd.c
+lib_test_mmap_SOURCES = lib/test-mmap.c
diff --git a/lib/test-mmap.c b/lib/test-mmap.c
new file mode 100644
index 000000000000..d0134fa44712
--- /dev/null
+++ b/lib/test-mmap.c
@@ -0,0 +1,220 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#define MB(a)           ((a) * 1024UL * 1024UL)
+
+static struct timeval start_tv, stop_tv;
+
+// Calculate the difference between two time values.
+static void tvsub(struct timeval *tdiff, struct timeval *t1, struct timeval *t0)
+{
+	tdiff->tv_sec = t1->tv_sec - t0->tv_sec;
+	tdiff->tv_usec = t1->tv_usec - t0->tv_usec;
+	if (tdiff->tv_usec < 0)
+		tdiff->tv_sec--, tdiff->tv_usec += 1000000;
+}
+
+// Start timing now.
+static void start(void)
+{
+	(void) gettimeofday(&start_tv, (struct timezone *) 0);
+}
+
+// Stop timing and return real time in microseconds.
+static unsigned long long stop(void)
+{
+	struct timeval tdiff;
+
+	(void) gettimeofday(&stop_tv, (struct timezone *) 0);
+	tvsub(&tdiff, &stop_tv, &start_tv);
+	return (tdiff.tv_sec * 1000000 + tdiff.tv_usec);
+}
+
+static void test_write(unsigned long *p, size_t size)
+{
+	size_t i;
+	unsigned long *wp;
+	unsigned long long timeval;
+
+	start();
+	for (i=0, wp=p; i<(size/sizeof(wp)); i++)
+		*wp++ = 1;
+	timeval = stop();
+	printf("Write: %10llu usec\n", timeval);
+}
+
+static void test_read(unsigned long *p, size_t size)
+{
+	size_t i;
+	volatile unsigned long *wp, tmp;
+	unsigned long long timeval;
+
+	start();
+	for (i=0, wp=p; i<(size/sizeof(wp)); i++)
+		tmp = *wp++;
+	tmp = tmp;
+	timeval = stop();
+	printf("Read : %10llu usec\n", timeval);
+}
+
+int main(int argc, char **argv)
+{
+	int fd, i, opt, ret;
+	int oflags, mprot, mflags = 0;
+	int is_read_only = 0, is_mlock = 0, is_mlockall = 0;
+	int mlock_skip = 0, read_test = 0, write_test = 0;
+	void *mptr = NULL;
+	unsigned long *p;
+	struct stat stat;
+	size_t size, cpy_size;
+	const char *file_name = NULL;
+
+	while ((opt = getopt(argc, argv, "RMSApsrw")) != -1) {
+		switch (opt) {
+			case 'R':
+				printf("> mmap: read-only\n");
+				is_read_only = 1;
+				break;
+			case 'M':
+				printf("> mlock\n");
+				is_mlock = 1;
+				break;
+			case 'S':
+				printf("> mlock - skip first ite\n");
+				mlock_skip = 1;
+				break;
+			case 'A':
+				printf("> mlockall\n");
+				is_mlockall = 1;
+				break;
+			case 'p':
+				printf("> MAP_POPULATE\n");
+				mflags |= MAP_POPULATE;
+				break;
+			case 's':
+				printf("> MAP_SHARED\n");
+				mflags |= MAP_SHARED;
+				break;
+			case 'r':
+				printf("> read-test\n");
+				read_test = 1;
+				break;
+			case 'w':
+				printf("> write-test\n");
+				write_test = 1;
+				break;
+		}
+	}
+
+	if (optind == argc) {
+		printf("missing file name\n");
+		return EXIT_FAILURE;
+	}
+	file_name = argv[optind];
+
+	if (!(mflags & MAP_SHARED)) {
+		printf("> MAP_PRIVATE\n");
+		mflags |= MAP_PRIVATE;
+	}
+
+	if (is_read_only) {
+		oflags = O_RDONLY;
+		mprot = PROT_READ;
+	} else {
+		oflags = O_RDWR;
+		mprot = PROT_READ|PROT_WRITE;
+	}
+
+	fd = open(file_name, oflags);
+	if (fd == -1) {
+		perror("open failed");
+		return EXIT_FAILURE;
+	}
+
+	ret = fstat(fd, &stat);
+	if (ret < 0) {
+		perror("fstat failed");
+		return EXIT_FAILURE;
+	}
+	size = stat.st_size;
+
+	printf("> open %s size %#zx flags %#x\n", file_name, size, oflags);
+
+	ret = posix_memalign(&mptr, MB(2), size);
+	if (ret ==0)
+		free(mptr);
+
+	printf("> mmap mprot 0x%x flags 0x%x\n", mprot, mflags);
+	p = mmap(mptr, size, mprot, mflags, fd, 0x0);
+	if (!p) {
+		perror("mmap failed");
+		return EXIT_FAILURE;
+	}
+	if ((long unsigned)p & (MB(2)-1))
+		printf("> mmap: NOT 2MB aligned: 0x%p\n", p);
+	else
+		printf("> mmap: 2MB aligned: 0x%p\n", p);
+
+#if 0   /* SIZE LIMIT */
+	if (size >= MB(2))
+		cpy_size = MB(32);
+	else
+#endif
+		cpy_size = size;
+
+	for (i=0; i<3; i++) {
+
+		if (is_mlock && !mlock_skip) {
+			printf("> mlock 0x%p\n", p);
+			ret = mlock(p, size);
+			if (ret < 0) {
+				perror("mlock failed");
+				return EXIT_FAILURE;
+			}
+		} else if (is_mlockall) {
+			printf("> mlockall\n");
+			ret = mlockall(MCL_CURRENT|MCL_FUTURE);
+			if (ret < 0) {
+				perror("mlockall failed");
+				return EXIT_FAILURE;
+			}
+		}
+
+		printf("===== %d =====\n", i+1);
+		if (write_test)
+			test_write(p, cpy_size);
+		if (read_test)
+			test_read(p, cpy_size);
+
+		if (is_mlock && !mlock_skip) {
+			printf("> munlock 0x%p\n", p);
+			ret = munlock(p, size);
+			if (ret < 0) {
+				perror("munlock failed");
+				return EXIT_FAILURE;
+			}
+		} else if (is_mlockall) {
+			printf("> munlockall\n");
+			ret = munlockall();
+			if (ret < 0) {
+				perror("munlockall failed");
+				return EXIT_FAILURE;
+			}
+		}
+
+		/* skip, if requested, only the first iteration */
+		mlock_skip = 0;
+	}
+
+	printf("> munmap 0x%p\n", p);
+	munmap(p, size);
+	return EXIT_SUCCESS;
+}
+
diff --git a/lib/test-mmap.sh b/lib/test-mmap.sh
new file mode 100755
index 000000000000..8d024e57925d
--- /dev/null
+++ b/lib/test-mmap.sh
@@ -0,0 +1,61 @@
+#!/bin/bash
+MNT=test_mmap_mnt
+FILE=image
+DEV=""
+TEST=lib/test-mmap
+
+err() {
+	rc=1
+	echo "test-mmap: failed at line $1"
+	if [ -n "$DEV" ]; then
+		umount $DEV
+	else
+		rc=77
+	fi
+	rmdir $MNT
+	exit $rc
+}
+
+test_mmap() {
+	trap 'err $LINENO' ERR
+
+	# SHARED
+	$TEST -Mrwps $MNT/$FILE     # mlock, populate, shared (mlock fail)
+	$TEST -Arwps $MNT/$FILE     # mlockall, populate, shared
+	$TEST -RMrps $MNT/$FILE     # read-only, mlock, populate, shared (mlock fail)
+	$TEST -rwps  $MNT/$FILE     # popluate, shared (popluate no effect)
+	$TEST -Rrps  $MNT/$FILE     # read-only popluate, shared (popluate no effect)
+	$TEST -Mrws  $MNT/$FILE     # mlock, shared (mlock fail)
+	$TEST -RMrs  $MNT/$FILE     # read-only, mlock, shared (mlock fail)
+	$TEST -rws   $MNT/$FILE     # shared (ok)
+	$TEST -Rrs   $MNT/$FILE     # read-only, shared (ok)
+
+	# PRIVATE
+	$TEST -Mrwp  $MNT/$FILE      # mlock, populate, private (ok)
+	$TEST -RMrp  $MNT/$FILE      # read-only, mlock, populate, private (mlock fail)
+	$TEST -rwp   $MNT/$FILE      # populate, private (ok)
+	$TEST -Rrp   $MNT/$FILE      # read-only, populate, private (populate no effect)
+	$TEST -Mrw   $MNT/$FILE      # mlock, private (ok)
+	$TEST -RMr   $MNT/$FILE      # read-only, mlock, private (mlock fail)
+	$TEST -MSr   $MNT/$FILE      # private, read before mlock (ok)
+	$TEST -rw    $MNT/$FILE      # private (ok)
+	$TEST -Rr    $MNT/$FILE      # read-only, private (ok)
+}
+
+set -e
+mkdir -p $MNT
+trap 'err $LINENO' ERR
+
+DEV=$(lib/test-dax-dev)
+
+mkfs.ext4 $DEV
+mount $DEV $MNT -o dax
+fallocate -l 1GiB $MNT/$FILE
+test_mmap
+umount $MNT
+
+mkfs.xfs -f $DEV
+mount $DEV $MNT -o dax
+fallocate -l 1GiB $MNT/$FILE
+test_mmap
+umount $MNT
