/
githubmirror
/
ndctl
Обзор
Документация
Войти
/
githubmirror
/
ndctl
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
test/device-dax.c
442 строки
11 KB
Dan Williams
util: Distribute 'filter' and 'json' helpers to per-tool objects
14 янв 2022, 01:17
14 янв 2022, 01:17
965fa02
Код
Авторство
О чём код?
// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2015-2020 Intel Corporation. All rights reserved. #include <fcntl.h> #include <stdio.h> #include <errno.h> #include <unistd.h> #include <stdlib.h> #include <syslog.h> #include <string.h> #include <signal.h> #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> #include <daxctl/libdaxctl.h> #include <ccan/array_size/array_size.h> #include <ndctl/builtin.h> #include <test.h> static sigjmp_buf sj_env; static int create_namespace(int argc, const char **argv, void *ctx) { builtin_xaction_namespace_reset(); return cmd_create_namespace(argc, argv, ctx); } static int reset_device_dax(struct ndctl_namespace *ndns) { struct ndctl_ctx *ctx = ndctl_namespace_get_ctx(ndns); const char *argv[] = { "__func__", "-v", "-m", "raw", "-f", "-e", "", }; int argc = ARRAY_SIZE(argv); argv[argc - 1] = ndctl_namespace_get_devname(ndns); return create_namespace(argc, argv, ctx); } static int setup_device_dax(struct ndctl_namespace *ndns, unsigned long __align) { struct ndctl_ctx *ctx = ndctl_namespace_get_ctx(ndns); char align[32]; const char *argv[] = { "__func__", "-v", "-m", "devdax", "-M", "dev", "-f", "-a", align, "-e", "", }; int argc = ARRAY_SIZE(argv); argv[argc - 1] = ndctl_namespace_get_devname(ndns); sprintf(align, "%ld", __align); return create_namespace(argc, argv, ctx); } static int setup_pmem_fsdax_mode(struct ndctl_namespace *ndns, unsigned long __align) { struct ndctl_ctx *ctx = ndctl_namespace_get_ctx(ndns); char align[32]; const char *argv[] = { "__func__", "-v", "-m", "fsdax", "-M", "dev", "-f", "-a", align, "-e", "", }; int argc = ARRAY_SIZE(argv); argv[argc - 1] = ndctl_namespace_get_devname(ndns); sprintf(align, "%ld", __align); return create_namespace(argc, argv, ctx); } static void sigbus(int sig, siginfo_t *siginfo, void *d) { siglongjmp(sj_env, 1); } #define VERIFY_SIZE(x) (x * 2) #define VERIFY_BUF_SIZE 4096 /* * This timeout value derived from an Intel(R) Xeon(R) CPU E5-2690 v2 @ * 3.00GHz where the loop, for the align == 2M case, completes in 7500us * when cached and 200ms when uncached. */ #define VERIFY_TIME(x) (suseconds_t) ((ALIGN(x, SZ_2M) / SZ_4K) * 60) static int verify_data(struct daxctl_dev *dev, char *dax_buf, unsigned long align, int salt, struct ndctl_test *test) { struct timeval tv1, tv2, tv_diff; unsigned long 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(align); 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: @ %#lx expected %#x got %#lx\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 > VERIFY_TIME(align)) { /* * Checks whether the kernel correctly mapped the * device-dax range as cacheable. */ 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_dax_soft_offline(struct ndctl_test *test, struct ndctl_namespace *ndns) { unsigned long long resource = ndctl_namespace_get_resource(ndns); int fd, rc; char *buf; if (resource == ULLONG_MAX) { fprintf(stderr, "failed to get resource: %s\n", ndctl_namespace_get_devname(ndns)); return -ENXIO; } fd = open("/sys/devices/system/memory/soft_offline_page", O_WRONLY); if (fd < 0) { fprintf(stderr, "failed to open soft_offline_page\n"); return -ENOENT; } rc = asprintf(&buf, "%#llx\n", resource); if (rc < 0) { fprintf(stderr, "failed to alloc resource\n"); close(fd); return -ENOMEM; } fprintf(stderr, "%s: try to offline page @%#llx\n", __func__, resource); rc = write(fd, buf, rc); free(buf); close(fd); if (rc >= 0) { fprintf(stderr, "%s: should have failed\n", __func__); return -ENXIO; } return 0; } static int __test_device_dax(unsigned long align, int loglevel, struct ndctl_test *test, struct ndctl_ctx *ctx) { unsigned long i; struct sigaction act; struct ndctl_dax *dax; struct ndctl_pfn *pfn; struct daxctl_dev *dev; int fd, rc, *p, salt; struct ndctl_namespace *ndns; struct daxctl_region *dax_region; char *buf, path[100], data[VERIFY_BUF_SIZE]; ndctl_set_log_priority(ctx, loglevel); ndns = ndctl_get_test_dev(ctx); if (!ndns) { fprintf(stderr, "%s: failed to find suitable namespace\n", __func__); return 77; } if (align > SZ_2M && !ndctl_test_attempt(test, KERNEL_VERSION(4, 11, 0))) return 77; if (!ndctl_test_attempt(test, KERNEL_VERSION(4, 7, 0))) return 77; /* setup up fsdax mode pmem device and seed with verification data */ rc = setup_pmem_fsdax_mode(ndns, align); 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(align); 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, align); if (rc < 0) { fprintf(stderr, "%s: failed device-dax setup\n", ndctl_namespace_get_devname(ndns)); goto out; } dax = ndctl_namespace_get_dax(ndns); dax_region = ndctl_dax_get_daxctl_region(dax); dev = daxctl_dev_get_first(dax_region); if (!dev) { fprintf(stderr, "%s: failed to find device-dax instance\n", ndctl_namespace_get_devname(ndns)); rc = -ENXIO; goto out; } sprintf(path, "/dev/%s", daxctl_dev_get_devname(dev)); fd = open(path, O_RDONLY); if (fd < 0) { fprintf(stderr, "%s: failed to open(O_RDONLY) device-dax instance\n", daxctl_dev_get_devname(dev)); rc = -ENXIO; goto out; } buf = mmap(NULL, VERIFY_SIZE(align), PROT_READ, 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(align), PROT_READ, MAP_SHARED, fd, 0); if (buf == MAP_FAILED) { fprintf(stderr, "%s: expected MAP_SHARED success\n", path); return -ENXIO; } rc = verify_data(dev, buf, align, salt, test); if (rc) goto out; close(fd); munmap(buf, VERIFY_SIZE(align)); /* * Prior to 4.8-final these tests cause crashes, or are * otherwise not supported. */ if (ndctl_test_attempt(test, KERNEL_VERSION(4, 9, 0))) { static const bool devdax = false; int fd2; fd = open(path, O_RDWR); if (fd < 0) { fprintf(stderr, "%s: failed to open for direct-io test\n", daxctl_dev_get_devname(dev)); rc = -ENXIO; goto out; } rc = test_dax_directio(fd, align, NULL, 0); if (rc) { fprintf(stderr, "%s: failed dax direct-i/o\n", ndctl_namespace_get_devname(ndns)); goto out; } rc = test_dax_remap(test, fd, align, NULL, 0, devdax); if (rc) { fprintf(stderr, "%s: failed dax remap\n", ndctl_namespace_get_devname(ndns)); goto out; } close(fd); fprintf(stderr, "%s: test dax poison\n", ndctl_namespace_get_devname(ndns)); fd = open(path, O_RDWR); if (fd < 0) { fprintf(stderr, "%s: failed to open for poison test\n", daxctl_dev_get_devname(dev)); rc = -ENXIO; goto out; } rc = test_dax_soft_offline(test, ndns); if (rc) { fprintf(stderr, "%s: failed dax soft offline\n", ndctl_namespace_get_devname(ndns)); goto out; } rc = test_dax_poison(test, fd, align, NULL, 0, devdax); if (rc) { fprintf(stderr, "%s: failed dax poison\n", ndctl_namespace_get_devname(ndns)); goto out; } close(fd); fd2 = open("/proc/self/smaps", O_RDONLY); if (fd2 < 0) { fprintf(stderr, "%s: failed smaps open\n", ndctl_namespace_get_devname(ndns)); rc = -ENXIO; goto out; } do { rc = read(fd2, data, sizeof(data)); } while (rc > 0); if (rc) { fprintf(stderr, "%s: failed smaps retrieval\n", ndctl_namespace_get_devname(ndns)); rc = -ENXIO; goto out; } } /* establish a writable mapping */ fd = open(path, O_RDWR); if (fd < 0) { fprintf(stderr, "%s: failed to open(O_RDWR) device-dax instance\n", daxctl_dev_get_devname(dev)); rc = -ENXIO; goto out; } buf = mmap(NULL, VERIFY_SIZE(align), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (buf == MAP_FAILED) { fprintf(stderr, "%s: expected PROT_WRITE + MAP_SHARED success\n", path); return -ENXIO; } rc = reset_device_dax(ndns); if (rc < 0) { fprintf(stderr, "%s: failed to reset device-dax instance\n", ndctl_namespace_get_devname(ndns)); goto out; } memset(&act, 0, sizeof(act)); act.sa_sigaction = sigbus; act.sa_flags = SA_SIGINFO; if (sigaction(SIGBUS, &act, 0)) { perror("sigaction"); rc = EXIT_FAILURE; goto out; } /* test fault after device-dax instance disabled */ if (sigsetjmp(sj_env, 1)) { /* got sigbus, success */ close(fd); rc = 0; goto out; } rc = EXIT_SUCCESS; p = (int *) (buf + align); *p = 0xff; if (ndctl_test_attempt(test, KERNEL_VERSION(4, 9, 0))) { /* after 4.9 this test will properly get sigbus above */ rc = EXIT_FAILURE; fprintf(stderr, "%s: failed to unmap after reset\n", daxctl_dev_get_devname(dev)); } close(fd); out: reset_device_dax(ndns); return rc; } static int test_device_dax(int loglevel, struct ndctl_test *test, struct ndctl_ctx *ctx) { unsigned long i, aligns[] = { SZ_4K, SZ_2M, SZ_1G }; int rc; for (i = 0; i < ARRAY_SIZE(aligns); i++) { rc = __test_device_dax(aligns[i], loglevel, test, ctx); if (rc && rc != 77) break; } return rc; } int __attribute__((weak)) main(int argc, char *argv[]) { struct ndctl_test *test = ndctl_test_new(0); struct ndctl_ctx *ctx; int rc; if (!test) { fprintf(stderr, "failed to initialize test\n"); return EXIT_FAILURE; } rc = ndctl_new(&ctx); if (rc < 0) return ndctl_test_result(test, rc); rc = test_device_dax(LOG_DEBUG, test, ctx); ndctl_unref(ctx); return ndctl_test_result(test, rc); }