/
githubmirror
/
ndctl
Обзор
Документация
Войти
/
githubmirror
/
ndctl
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
test/dax-pmd.c
377 строк
8 KB
Dan Williams
test: Prepare out of line builds
14 янв 2022, 01:17
14 янв 2022, 01:17
d12d5f8
Код
Авторство
О чём код?
// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2015-2020 Intel Corporation. All rights reserved. #include <stdio.h> #include <unistd.h> #include <setjmp.h> #include <sys/mman.h> #include <linux/mman.h> #include <sys/types.h> #include <sys/wait.h> #include <sys/stat.h> #include <fcntl.h> #include <string.h> #include <errno.h> #include <sys/ioctl.h> #include <stdlib.h> #include <linux/fs.h> #include <test.h> #include <util/size.h> #include <linux/fiemap.h> #include <linux/version.h> #define NUM_EXTENTS 5 #define fail() fprintf(stderr, "%s: failed at: %d (%s)\n", \ __func__, __LINE__, strerror(errno)) #define faili(i) fprintf(stderr, "%s: failed at: %d: %d (%s)\n", \ __func__, __LINE__, i, strerror(errno)) #define TEST_DIR "test_dax_mnt" #define TEST_FILE TEST_DIR "/test_dax_data" #define REGION_MEM_SIZE 4096*4 #define REGION_PM_SIZE 4096*512 #define REMAP_SIZE 4096 static sigjmp_buf sj_env; static void sigbus(int sig, siginfo_t *siginfo, void *d) { siglongjmp(sj_env, 1); } int test_dax_remap(struct ndctl_test *test, int dax_fd, unsigned long align, void *dax_addr, off_t offset, bool fsdax) { void *anon, *remap, *addr; struct sigaction act; int rc, val; if ((fsdax || align == SZ_2M) && !ndctl_test_attempt(test, KERNEL_VERSION(5, 8, 0))) { /* kernel's prior to 5.8 may crash on this test */ fprintf(stderr, "%s: SKIP mremap() test\n", __func__); return 0; } anon = mmap(NULL, REGION_MEM_SIZE, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); addr = mmap(dax_addr, 2*align, PROT_READ|PROT_WRITE, MAP_SHARED, dax_fd, offset); fprintf(stderr, "%s: addr: %p size: %#lx\n", __func__, addr, 2*align); if (addr == MAP_FAILED) { rc = -errno; faili(0); return rc; } memset(anon, 'a', REGION_MEM_SIZE); memset(addr, 'i', align*2); remap = mremap(addr, REMAP_SIZE, REMAP_SIZE, MREMAP_MAYMOVE|MREMAP_FIXED, anon); if (remap == MAP_FAILED) { fprintf(stderr, "%s: mremap failed, that's ok too\n", __func__); return 0; } if (remap != anon) { rc = -ENXIO; perror("mremap"); faili(1); return rc; } fprintf(stderr, "%s: addr: %p size: %#x\n", __func__, remap, REMAP_SIZE); memset(&act, 0, sizeof(act)); act.sa_sigaction = sigbus; act.sa_flags = SA_SIGINFO; if (sigaction(SIGBUS, &act, 0)) { perror("sigaction"); return EXIT_FAILURE; } /* test fault after device-dax instance disabled */ if (sigsetjmp(sj_env, 1)) { if (!fsdax && align > SZ_4K) { fprintf(stderr, "got expected SIGBUS after mremap() of device-dax\n"); return 0; } else { fprintf(stderr, "unpexpected SIGBUS after mremap()\n"); return -EIO; } } *(int *) anon = 0xAA; val = *(int *) anon; if (val != 0xAA) { faili(2); return -ENXIO; } return 0; } int test_dax_directio(int dax_fd, unsigned long align, void *dax_addr, off_t offset) { int i, rc = -ENXIO; void *buf; if (posix_memalign(&buf, 4096, 4096) != 0) return -ENOMEM; for (i = 0; i < 5; i++) { unsigned long flags; void *addr; int fd2; if (dax_fd >= 0) flags = MAP_SHARED; else { /* hugetlbfs instead of device-dax */ const char *base = "/sys/kernel/mm/hugepages"; FILE *f_nrhuge; char path[256]; flags = MAP_SHARED | MAP_ANONYMOUS; if (align >= SZ_2M) { char setting[] = { "2\n" }; sprintf(path, "%s/hugepages-%ldkB/nr_hugepages", base, align / 1024); f_nrhuge = fopen(path, "r+"); if (!f_nrhuge) { rc = -errno; faili(i); return rc; } if (fwrite(setting, sizeof(setting), 1, f_nrhuge) != 1) { rc = -errno; faili(i); fclose(f_nrhuge); return rc; } fclose(f_nrhuge); /* FIXME: support non-x86 page sizes */ if (align > SZ_2M) flags |= MAP_HUGETLB | MAP_HUGE_1GB; else flags |= MAP_HUGETLB | MAP_HUGE_2MB; } } addr = mmap(dax_addr, 2*align, PROT_READ|PROT_WRITE, flags, dax_fd, offset); if (addr == MAP_FAILED) { rc = -errno; faili(i); break; } rc = -ENXIO; rc = mkdir(TEST_DIR, 0600); if (rc < 0 && errno != EEXIST) { faili(i); munmap(addr, 2 * align); break; } fd2 = open(TEST_FILE, O_CREAT|O_TRUNC|O_DIRECT|O_RDWR, 0600); if (fd2 < 0) { faili(i); munmap(addr, 2*align); break; } fprintf(stderr, "%s: test: %d\n", __func__, i); rc = 0; switch (i) { case 0: /* test O_DIRECT read of unfaulted address */ if (write(fd2, addr, 4096) != 4096) { faili(i); rc = -ENXIO; } /* * test O_DIRECT write of pre-faulted read-only * address */ if (pread(fd2, addr, 4096, 0) != 4096) { faili(i); rc = -ENXIO; } break; case 1: /* test O_DIRECT of pre-faulted address */ sprintf(addr, "odirect data"); if (pwrite(fd2, addr, 4096, 0) != 4096) { faili(i); rc = -ENXIO; } ((char *) buf)[0] = 0; if (pread(fd2, buf, 4096, 0) != 4096) { faili(i); rc = -ENXIO; } if (strcmp(buf, "odirect data") != 0) { faili(i); rc = -ENXIO; } break; case 2: /* fork with pre-faulted pmd */ sprintf(addr, "fork data"); rc = fork(); if (rc == 0) { /* child */ if (strcmp(addr, "fork data") == 0) exit(EXIT_SUCCESS); else exit(EXIT_FAILURE); } else if (rc > 0) { /* parent */ wait(&rc); rc = WEXITSTATUS(rc); if (rc != EXIT_SUCCESS) { faili(i); } } else faili(i); break; case 3: /* convert ro mapping to rw */ rc = *(volatile int *) addr; *(volatile int *) addr = rc; rc = 0; break; case 4: /* test O_DIRECT write of unfaulted address */ sprintf(buf, "O_DIRECT write of unfaulted address\n"); if (pwrite(fd2, buf, 4096, 0) < 4096) { faili(i); rc = -ENXIO; break; } if (pread(fd2, addr, 4096, 0) < 4096) { faili(i); rc = -ENXIO; break; } rc = 0; break; default: faili(i); rc = -ENXIO; break; } munmap(addr, 2*align); addr = MAP_FAILED; unlink(TEST_FILE); close(fd2); fd2 = -1; if (rc) break; } free(buf); return rc; } /* test_pmd assumes that fd references a pre-allocated + dax-capable file */ static int test_pmd(struct ndctl_test *test, int fd) { unsigned long long m_align, p_align, pmd_off; static const bool fsdax = true; struct fiemap_extent *ext; void *base, *pmd_addr; struct fiemap *map; int rc = -ENXIO; unsigned long i; if (fd < 0) { fail(); return -ENXIO; } map = calloc(1, sizeof(struct fiemap) + sizeof(struct fiemap_extent) * NUM_EXTENTS); if (!map) { fail(); return -ENXIO; } base = mmap(NULL, 4*HPAGE_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (base == MAP_FAILED) { fail(); goto err_mmap; } munmap(base, 4*HPAGE_SIZE); map->fm_start = 0; map->fm_length = -1; map->fm_extent_count = NUM_EXTENTS; rc = ioctl(fd, FS_IOC_FIEMAP, map); if (rc < 0) { fail(); goto err_extent; } for (i = 0; i < map->fm_mapped_extents; i++) { ext = &map->fm_extents[i]; p_align = ALIGN(ext->fe_physical, HPAGE_SIZE) - ext->fe_physical; fprintf(stderr, "[%ld]: l: %llx p: %llx len: %llx flags: %x\n", i, ext->fe_logical, ext->fe_physical, ext->fe_length, ext->fe_flags); if (ext->fe_length > 2 * HPAGE_SIZE && p_align == 0) { fprintf(stderr, "found potential huge extent\n"); break; } } if (i >= map->fm_mapped_extents) { fail(); goto err_extent; } m_align = ALIGN(base, HPAGE_SIZE) - ((unsigned long) base); p_align = ALIGN(ext->fe_physical, HPAGE_SIZE) - ext->fe_physical; pmd_addr = (char *) base + m_align; pmd_off = ext->fe_logical + p_align; rc = test_dax_remap(test, fd, HPAGE_SIZE, pmd_addr, pmd_off, fsdax); if (rc) goto err_test; rc = test_dax_directio(fd, HPAGE_SIZE, pmd_addr, pmd_off); if (rc) goto err_test; rc = test_dax_poison(test, fd, HPAGE_SIZE, pmd_addr, pmd_off, fsdax); err_test: err_extent: err_mmap: free(map); return rc; } int __attribute__((weak)) main(int argc, char *argv[]) { struct ndctl_test *test = ndctl_test_new(0); int fd, rc; if (!test) { fprintf(stderr, "failed to initialize test\n"); return EXIT_FAILURE; } if (argc < 1) return -EINVAL; fd = open(argv[1], O_RDWR); rc = test_pmd(test, fd); if (fd >= 0) close(fd); return ndctl_test_result(test, rc); }