diff mbox

ndctl: general dax mmap test

Message ID 20151207075133.32696.92131.stgit@dwillia2-desk3.jf.intel.com (mailing list archive)
State Superseded
Headers show

Commit Message

Dan Williams Dec. 7, 2015, 7:51 a.m. UTC
From: Toshi Kani <toshi.kani@hpe.com>

Permute through various parameters to mmap when establishing dax
mappings.  Ideally we would have fuzz testing for this interface, but
for now this just does nominal testing of several options.

All the test code is from Toshi, with integration into ndctl by Dan.

Not-yet-signed-off-by: Toshi Kani <toshi.kani@hpe.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
---
 Makefile.am      |    5 +
 lib/test-mmap.c  |  220 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/test-mmap.sh |   61 +++++++++++++++
 3 files changed, 284 insertions(+), 2 deletions(-)
 create mode 100644 lib/test-mmap.c
 create mode 100755 lib/test-mmap.sh

Comments

Dan Williams Dec. 7, 2015, 6:43 p.m. UTC | #1
On Sun, Dec 6, 2015 at 11:51 PM, Dan Williams <dan.j.williams@intel.com> wrote:
> From: Toshi Kani <toshi.kani@hpe.com>
>
> Permute through various parameters to mmap when establishing dax
> mappings.  Ideally we would have fuzz testing for this interface, but
> for now this just does nominal testing of several options.
>
> All the test code is from Toshi, with integration into ndctl by Dan.
>
> Not-yet-signed-off-by: Toshi Kani <toshi.kani@hpe.com>

Hi Toshi, can I add your sign-off for this implementation?  Thanks for
developing the test, it fleshed out a good handful of kernel bugs.
Kani, Toshi Dec. 7, 2015, 7:19 p.m. UTC | #2
On Mon, 2015-12-07 at 10:43 -0800, Dan Williams wrote:
> On Sun, Dec 6, 2015 at 11:51 PM, Dan Williams <dan.j.williams@intel.com>
> wrote:
> > From: Toshi Kani <toshi.kani@hpe.com>
> > 
> > Permute through various parameters to mmap when establishing dax
> > mappings.  Ideally we would have fuzz testing for this interface, but
> > for now this just does nominal testing of several options.
> > 
> > All the test code is from Toshi, with integration into ndctl by Dan.
> > 
> > Not-yet-signed-off-by: Toshi Kani <toshi.kani@hpe.com>
> 
> Hi Toshi, can I add your sign-off for this implementation?  Thanks for
> developing the test, it fleshed out a good handful of kernel bugs.

Yes, and thanks for integrating the test!

Signed-off-by: Toshi Kani <toshi.kani@hpe.com>

-Toshi
Elliott, Robert (Servers) Dec. 7, 2015, 7:32 p.m. UTC | #3
> -----Original Message-----
> From: Linux-nvdimm [mailto:linux-nvdimm-bounces@lists.01.org] On Behalf Of
> Dan Williams
> Sent: Monday, December 7, 2015 1:52 AM
> To: linux-nvdimm@lists.01.org
> Cc: toshi.kani@hp.com
> Subject: [PATCH] ndctl: general dax mmap test
> 
> From: Toshi Kani <toshi.kani@hpe.com>
> 
> Permute through various parameters to mmap when establishing dax
> mappings.  Ideally we would have fuzz testing for this interface, but
> for now this just does nominal testing of several options.
> 
> All the test code is from Toshi, with integration into ndctl by Dan.
> 
> Not-yet-signed-off-by: Toshi Kani <toshi.kani@hpe.com>
> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
...
> 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 @@
...
> +#define MB(a)           ((a) * 1024UL * 1024UL)

I suggest using MiB there.

...
> +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;

I think that should be sizeof(*wp), the size of 
unsigned long, not the size of the pointer to the 
unsigned long.

> +	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++;

sizeof(*wp) like above.

> +	tmp = tmp;

That seems like a NOP, unless it's relying on some special
compiler handling of volatile.

> +	timeval = stop();
> +	printf("Read : %10llu usec\n", timeval);
> +}
> +
> +int main(int argc, char **argv)
...
> +			case 'S':
> +				printf("> mlock - skip first ite\n");

I would spell out iteration.

...
> +	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);

I suggest using MiB rather than MB in the prints.

> +
> +#if 0   /* SIZE LIMIT */
> +	if (size >= MB(2))
> +		cpy_size = MB(32);
> +	else
> +#endif
> +		cpy_size = size;

Any real need for that if 0 code?

...
> 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
...
> +	$TEST -rwps  $MNT/$FILE     # popluate, shared (popluate no effect)
> +	$TEST -Rrps  $MNT/$FILE     # read-only popluate, shared (popluate

popluate should be populate.


---
Robert Elliott, HPE Persistent Memory
Kani, Toshi Dec. 7, 2015, 7:47 p.m. UTC | #4
On Mon, 2015-12-07 at 19:32 +0000, Elliott, Robert (Persistent Memory)
wrote:
> > -----Original Message-----
> > From: Linux-nvdimm [mailto:linux-nvdimm-bounces@lists.01.org] On Behalf
> > Of
> > Dan Williams
> > Sent: Monday, December 7, 2015 1:52 AM
> > To: linux-nvdimm@lists.01.org
> > Cc: toshi.kani@hp.com
> > Subject: [PATCH] ndctl: general dax mmap test
> > 
> > From: Toshi Kani <toshi.kani@hpe.com>
> > 
> > Permute through various parameters to mmap when establishing dax
> > mappings.  Ideally we would have fuzz testing for this interface, but
> > for now this just does nominal testing of several options.
> > 
> > All the test code is from Toshi, with integration into ndctl by Dan.
> > 
> > Not-yet-signed-off-by: Toshi Kani <toshi.kani@hpe.com>
> > Signed-off-by: Dan Williams <dan.j.williams@intel.com>
> ...
> > 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 @@
> ...
> > +#define MB(a)           ((a) * 1024UL * 1024UL)
> 
> I suggest using MiB there.
> 
> ...
> > +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;
> 
> I think that should be sizeof(*wp), the size of
> unsigned long, not the size of the pointer to the
> unsigned long.

Good catch.

> > +     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++;
> 
> sizeof(*wp) like above.

Right.

> > +     tmp = tmp;
> 
> That seems like a NOP, unless it's relying on some special
> compiler handling of volatile.
> 
> > +     timeval = stop();
> > +     printf("Read : %10llu usec\n", timeval);
> > +}
> > +
> > +int main(int argc, char **argv)
> ...
> > +                     case 'S':
> > +                             printf("> mlock - skip first ite\n");
> 
> I would spell out iteration.
> 
> ...
> > +     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);
> 
> I suggest using MiB rather than MB in the prints.
> 
> > +
> > +#if 0   /* SIZE LIMIT */
> > +     if (size >= MB(2))
> > +             cpy_size = MB(32);
> > +     else
> > +#endif
> > +             cpy_size = size;
> 
> Any real need for that if 0 code?

No, this is a dead code (and the numbers do not even match..) and can be
removed.

> ...
> > 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
> ...
> > +     $TEST -rwps  $MNT/$FILE     # popluate, shared (popluate no
> > effect)
> > +     $TEST -Rrps  $MNT/$FILE     # read-only popluate, shared
> > (popluate
> 
> popluate should be populate.

Dan, would you mind to update the patch in your branch?  (Let me know if
you'd need me to update.)

Thanks,
-Toshi
diff mbox

Patch

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