Validate that data written through /dev/pmemX is retrievable intact through /dev/daxX. This tests that pmem and device-dax have the same device-to-physical address translation.
Signed-off-by: Dan Williams <[email protected]> --- test/device-dax.c | 139 +++++++++++++++++++++++++++++++++++++++++++++++------ util/size.h | 1 2 files changed, 125 insertions(+), 15 deletions(-) diff --git a/test/device-dax.c b/test/device-dax.c index c1409ac8b8b8..82154d5c1fff 100644 --- a/test/device-dax.c +++ b/test/device-dax.c @@ -9,7 +9,9 @@ #include <setjmp.h> #include <sys/stat.h> #include <sys/mman.h> +#include <sys/time.h> #include <sys/types.h> +#include <util/size.h> #include <linux/falloc.h> #include <linux/version.h> #include <ndctl/libndctl.h> @@ -51,21 +53,79 @@ static int setup_device_dax(struct ndctl_namespace *ndns) return create_namespace(argc, argv, ctx); } +static int setup_pmem_memory_mode(struct ndctl_namespace *ndns) +{ + struct ndctl_ctx *ctx = ndctl_namespace_get_ctx(ndns); + const char *argv[] = { + "__func__", "-v", "-m", "memory", "-M", "dev", "-f", "-e", "", + }; + int argc = ARRAY_SIZE(argv); + + argv[argc - 1] = ndctl_namespace_get_devname(ndns); + return create_namespace(argc, argv, ctx); +} + static void sigbus(int sig, siginfo_t *siginfo, void *d) { siglongjmp(sj_env, 1); } +#define VERIFY_SIZE SZ_4M +#define VERIFY_BUF_SIZE 4096 + +static int verify_data(struct daxctl_dev *dev, char *dax_buf, int salt, + struct ndctl_test *test) +{ + struct timeval tv1, tv2, tv_diff; + int i; + + if (!ndctl_test_attempt(test, KERNEL_VERSION(4, 9, 0))) + return 0; + + /* verify data and cache mode */ + gettimeofday(&tv1, NULL); + for (i = 0; i < VERIFY_SIZE; i += VERIFY_BUF_SIZE) { + unsigned int *verify = (unsigned int *) (dax_buf + i), j; + + for (j = 0; j < VERIFY_BUF_SIZE / sizeof(int); j++) + if (verify[j] != salt + i + j) + break; + if (j < VERIFY_BUF_SIZE / sizeof(int)) { + fprintf(stderr, "%s: @ %#x expected %#x got %#x\n", + daxctl_dev_get_devname(dev), i, + verify[j], salt + i + j); + return -ENXIO; + } + } + gettimeofday(&tv2, NULL); + timersub(&tv2, &tv1, &tv_diff); + tv_diff.tv_usec += tv_diff.tv_sec * 1000000; + if (tv_diff.tv_usec > 15000) { + /* + * Checks whether the kernel correctly mapped the + * device-dax range as cacheable. The numbers were + * derived from an Intel(R) Xeon(R) CPU E5-2690 v2 @ + * 3.00GHz where the loop completes in 7500us when + * cached and 200ms when uncached. + */ + fprintf(stderr, "%s: verify loop took too long usecs: %ld\n", + daxctl_dev_get_devname(dev), tv_diff.tv_usec); + return -ENXIO; + } + return 0; +} + static int test_device_dax(int loglevel, struct ndctl_test *test, struct ndctl_ctx *ctx) { - int fd, rc, *p; struct sigaction act; struct ndctl_dax *dax; + struct ndctl_pfn *pfn; struct daxctl_dev *dev; + int i, fd, rc, *p, salt; struct ndctl_namespace *ndns; struct daxctl_region *dax_region; - char *buf, path[100], data[4096]; + char *buf, path[100], data[VERIFY_BUF_SIZE]; memset (&act, 0, sizeof(act)); act.sa_sigaction = sigbus; @@ -88,11 +148,46 @@ static int test_device_dax(int loglevel, struct ndctl_test *test, if (!ndctl_test_attempt(test, KERNEL_VERSION(4, 7, 0))) return 77; + /* setup up memory mode pmem device and seed with verification data */ + rc = setup_pmem_memory_mode(ndns); + if (rc < 0 || !(pfn = ndctl_namespace_get_pfn(ndns))) { + fprintf(stderr, "%s: failed device-dax setup\n", + ndctl_namespace_get_devname(ndns)); + goto out; + } + + sprintf(path, "/dev/%s", ndctl_pfn_get_block_device(pfn)); + fd = open(path, O_RDWR); + if (fd < 0) { + fprintf(stderr, "%s: failed to open pmem device\n", path); + rc = -ENXIO; + goto out; + } + + srand(getpid()); + salt = rand(); + for (i = 0; i < VERIFY_SIZE; i += VERIFY_BUF_SIZE) { + unsigned int *verify = (unsigned int *) data, j; + + for (j = 0; j < VERIFY_BUF_SIZE / sizeof(int); j++) + verify[j] = salt + i + j; + + if (write(fd, data, sizeof(data)) != sizeof(data)) { + fprintf(stderr, "%s: failed data setup\n", + path); + rc = -ENXIO; + goto out; + } + } + fsync(fd); + close(fd); + + /* switch the namespace to device-dax mode and verify data via mmap */ rc = setup_device_dax(ndns); if (rc < 0) { fprintf(stderr, "%s: failed device-dax setup\n", ndctl_namespace_get_devname(ndns)); - return rc; + goto out; } dax = ndctl_namespace_get_dax(ndns); @@ -101,27 +196,35 @@ static int test_device_dax(int loglevel, struct ndctl_test *test, if (!dev) { fprintf(stderr, "%s: failed to find device-dax instance\n", ndctl_namespace_get_devname(ndns)); - return -ENXIO; + rc = -ENXIO; + goto out; } sprintf(path, "/dev/%s", daxctl_dev_get_devname(dev)); - fd = open(path, O_RDWR); if (fd < 0) { fprintf(stderr, "%s: failed to open device-dax instance\n", daxctl_dev_get_devname(dev)); - return -ENXIO; + rc = -ENXIO; + goto out; } - buf = mmap(NULL, 2UL << 20, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); + buf = mmap(NULL, VERIFY_SIZE, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); if (buf != MAP_FAILED) { fprintf(stderr, "%s: expected MAP_PRIVATE failure\n", path); + rc = -ENXIO; + goto out; + } + + buf = mmap(NULL, VERIFY_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + if (buf == MAP_FAILED) { + fprintf(stderr, "%s: expected MAP_SHARED success\n", path); return -ENXIO; } - buf = mmap(NULL, 2UL << 20, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); - p = (int *) (buf + (1UL << 20)); - *p = 0; + rc = verify_data(dev, buf, salt, test); + if (rc) + goto out; /* * Prior to 4.8-final these tests cause crashes, or are @@ -134,14 +237,15 @@ static int test_device_dax(int loglevel, struct ndctl_test *test, if (rc) { fprintf(stderr, "%s: failed dax direct-i/o\n", ndctl_namespace_get_devname(ndns)); - return rc; + goto out; } fd2 = open("/proc/self/smaps", O_RDONLY); if (fd2 < 0) { fprintf(stderr, "%s: failed smaps open\n", ndctl_namespace_get_devname(ndns)); - return -ENXIO; + rc = -ENXIO; + goto out; } do { @@ -151,7 +255,8 @@ static int test_device_dax(int loglevel, struct ndctl_test *test, if (rc) { fprintf(stderr, "%s: failed smaps retrieval\n", ndctl_namespace_get_devname(ndns)); - return -ENXIO; + rc = -ENXIO; + goto out; } } @@ -159,17 +264,19 @@ static int test_device_dax(int loglevel, struct ndctl_test *test, if (rc < 0) { fprintf(stderr, "%s: failed to reset device-dax instance\n", ndctl_namespace_get_devname(ndns)); - return rc; + goto out; } /* test fault after device-dax instance disabled */ if (sigsetjmp(sj_env, 1)) { /* got sigbus, success */ close(fd); - return 0; + rc = 0; + goto out; } rc = EXIT_SUCCESS; + p = (int *) (buf + (1UL << 20)); *p = 0xff; if (ndctl_test_attempt(test, KERNEL_VERSION(4, 9, 0))) { /* after 4.9 this test will properly get sigbus above */ @@ -178,6 +285,8 @@ static int test_device_dax(int loglevel, struct ndctl_test *test, daxctl_dev_get_devname(dev)); } close(fd); + out: + reset_device_dax(ndns); return rc; } diff --git a/util/size.h b/util/size.h index 64c146480ef0..9befecb28b14 100644 --- a/util/size.h +++ b/util/size.h @@ -4,6 +4,7 @@ #define SZ_1K 0x00000400 #define SZ_1M 0x00100000 #define SZ_2M 0x00200000 +#define SZ_4M 0x00400000 #define SZ_1G 0x40000000 #define SZ_1T 0x10000000000ULL _______________________________________________ Linux-nvdimm mailing list [email protected] https://lists.01.org/mailman/listinfo/linux-nvdimm
