Index: head/tests/sys/posixshm/posixshm_test.c =================================================================== --- head/tests/sys/posixshm/posixshm_test.c +++ head/tests/sys/posixshm/posixshm_test.c @@ -28,10 +28,12 @@ __FBSDID("$FreeBSD$"); #include +#include #include #include #include #include +#include #include #include @@ -956,6 +958,720 @@ close(fd); } +static int +shm_open_large(int psind, int policy, size_t sz) +{ + int error, fd; + + fd = shm_create_largepage(SHM_ANON, O_CREAT | O_RDWR, psind, policy, 0); + if (fd < 0 && errno == ENOTTY) + atf_tc_skip("no large page support"); + ATF_REQUIRE_MSG(fd >= 0, "shm_create_largepage failed; errno=%d", errno); + + error = ftruncate(fd, sz); + if (error != 0 && errno == ENOMEM) + /* + * The test system might not have enough memory to accommodate + * the request. + */ + atf_tc_skip("failed to allocate %zu-byte superpage", sz); + ATF_REQUIRE_MSG(error == 0, "ftruncate failed; errno=%d", errno); + + return (fd); +} + +static int +pagesizes(size_t ps[MAXPAGESIZES]) +{ + int pscnt; + + pscnt = getpagesizes(ps, MAXPAGESIZES); + ATF_REQUIRE_MSG(pscnt != -1, "getpagesizes failed; errno=%d", errno); + ATF_REQUIRE_MSG(ps[0] == PAGE_SIZE, "psind 0 is %zu", ps[0]); + ATF_REQUIRE_MSG(pscnt <= MAXPAGESIZES, "invalid pscnt %d", pscnt); + if (pscnt == 1) + atf_tc_skip("no large page support"); + return (pscnt); +} + +ATF_TC_WITHOUT_HEAD(largepage_basic); +ATF_TC_BODY(largepage_basic, tc) +{ + char zeroes[PAGE_SIZE]; + char *addr, *vec; + size_t ps[MAXPAGESIZES]; + int error, fd, pscnt; + + memset(zeroes, 0, PAGE_SIZE); + + pscnt = pagesizes(ps); + for (int i = 1; i < pscnt; i++) { + fd = shm_open_large(i, SHM_LARGEPAGE_ALLOC_DEFAULT, ps[i]); + + addr = mmap(NULL, ps[i], PROT_READ | PROT_WRITE, MAP_SHARED, fd, + 0); + ATF_REQUIRE_MSG(addr != MAP_FAILED, + "mmap(%zu bytes) failed; errno=%d", ps[i], errno); + ATF_REQUIRE_MSG(((uintptr_t)addr & (ps[i] - 1)) == 0, + "mmap(%zu bytes) returned unaligned mapping; addr=%p", + ps[i], addr); + + /* Force a page fault. */ + *(volatile char *)addr = 0; + + vec = malloc(ps[i] / PAGE_SIZE); + ATF_REQUIRE(vec != NULL); + error = mincore(addr, ps[i], vec); + ATF_REQUIRE_MSG(error == 0, "mincore failed; errno=%d", errno); + + /* Verify that all pages in the run are mapped. */ + for (size_t p = 0; p < ps[i] / PAGE_SIZE; p++) { + ATF_REQUIRE_MSG((vec[p] & MINCORE_INCORE) != 0, + "page %zu is not mapped", p); + ATF_REQUIRE_MSG((vec[p] & MINCORE_PSIND(i)) != 0, + "page %zu is not in a %zu-byte superpage", + p, ps[i]); + } + + /* Validate zeroing. */ + for (size_t p = 0; p < ps[i] / PAGE_SIZE; p++) { + ATF_REQUIRE_MSG(memcmp(addr + p * PAGE_SIZE, zeroes, + PAGE_SIZE) == 0, "page %zu miscompare", p); + } + + free(vec); + ATF_REQUIRE(munmap(addr, ps[i]) == 0); + ATF_REQUIRE(close(fd) == 0); + } +} + +extern int __sys_shm_open2(const char *, int, mode_t, int, const char *); + +ATF_TC_WITHOUT_HEAD(largepage_config); +ATF_TC_BODY(largepage_config, tc) +{ + struct shm_largepage_conf lpc; + char *addr, *buf; + size_t ps[MAXPAGESIZES + 1]; /* silence warnings if MAXPAGESIZES == 1 */ + int error, fd, pscnt; + + pscnt = pagesizes(ps); + + fd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0); + ATF_REQUIRE_MSG(fd >= 0, "shm_open failed; error=%d", errno); + + /* + * Configure a large page policy for an object created without + * SHM_LARGEPAGE. + */ + lpc.psind = 1; + lpc.alloc_policy = SHM_LARGEPAGE_ALLOC_DEFAULT; + error = ioctl(fd, FIOSSHMLPGCNF, &lpc); + ATF_REQUIRE(error != 0); + ATF_REQUIRE_MSG(errno == ENOTTY, "ioctl(FIOSSHMLPGCNF) returned %d", + errno); + ATF_REQUIRE(close(fd) == 0); + + /* + * Create a largepage object and try to use it without actually + * configuring anything. + */ + fd = __sys_shm_open2(SHM_ANON, O_CREAT | O_RDWR, 0, SHM_LARGEPAGE, + NULL); + if (fd < 0 && errno == ENOTTY) + atf_tc_skip("no large page support"); + ATF_REQUIRE_MSG(fd >= 0, "shm_open2 failed; error=%d", errno); + + error = ftruncate(fd, ps[1]); + ATF_REQUIRE(error != 0); + ATF_REQUIRE_MSG(errno == EINVAL, "ftruncate returned %d", errno); + + addr = mmap(NULL, ps[1], PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + ATF_REQUIRE(addr == MAP_FAILED); + ATF_REQUIRE_MSG(errno == EINVAL, "mmap returned %d", errno); + addr = mmap(NULL, 0, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + ATF_REQUIRE(addr == MAP_FAILED); + ATF_REQUIRE_MSG(errno == EINVAL, "mmap returned %d", errno); + + buf = calloc(1, PAGE_SIZE); + ATF_REQUIRE(buf != NULL); + ATF_REQUIRE(write(fd, buf, PAGE_SIZE) == -1); + ATF_REQUIRE_MSG(errno == EINVAL, "write returned %d", errno); + free(buf); + buf = calloc(1, ps[1]); + ATF_REQUIRE(buf != NULL); + ATF_REQUIRE(write(fd, buf, ps[1]) == -1); + ATF_REQUIRE_MSG(errno == EINVAL, "write returned %d", errno); + free(buf); + + error = posix_fallocate(fd, 0, PAGE_SIZE); + ATF_REQUIRE_MSG(error == EINVAL, "posix_fallocate returned %d", error); + + ATF_REQUIRE(close(fd) == 0); +} + +ATF_TC_WITHOUT_HEAD(largepage_mmap); +ATF_TC_BODY(largepage_mmap, tc) +{ + char *addr, *addr1, *vec; + size_t ps[MAXPAGESIZES]; + int fd, pscnt; + + pscnt = pagesizes(ps); + for (int i = 1; i < pscnt; i++) { + fd = shm_open_large(i, SHM_LARGEPAGE_ALLOC_DEFAULT, ps[i]); + + /* For mincore(). */ + vec = malloc(ps[i]); + ATF_REQUIRE(vec != NULL); + + /* + * Wrong mapping size. + */ + addr = mmap(NULL, ps[i - 1], PROT_READ | PROT_WRITE, MAP_SHARED, + fd, 0); + ATF_REQUIRE_MSG(addr == MAP_FAILED, + "mmap(%zu bytes) succeeded", ps[i - 1]); + ATF_REQUIRE_MSG(errno == EINVAL, + "mmap(%zu bytes) failed; error=%d", ps[i - 1], errno); + + /* + * Fixed mappings. + */ + addr = mmap(NULL, ps[i], PROT_READ | PROT_WRITE, MAP_SHARED, fd, + 0); + ATF_REQUIRE_MSG(addr != MAP_FAILED, + "mmap(%zu bytes) failed; errno=%d", ps[i], errno); + ATF_REQUIRE_MSG(((uintptr_t)addr & (ps[i] - 1)) == 0, + "mmap(%zu bytes) returned unaligned mapping; addr=%p", + ps[i], addr); + + /* Try mapping a small page with anonymous memory. */ + addr1 = mmap(addr, ps[i - 1], PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0); + ATF_REQUIRE_MSG(addr1 == MAP_FAILED, + "anon mmap(%zu bytes) succeeded", ps[i - 1]); + ATF_REQUIRE_MSG(errno == EINVAL, "mmap returned %d", errno); + + /* Check MAP_EXCL when creating a second largepage mapping. */ + addr1 = mmap(addr, ps[i], PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_FIXED | MAP_EXCL, fd, 0); + ATF_REQUIRE_MSG(addr1 == MAP_FAILED, + "mmap(%zu bytes) succeeded", ps[i]); + /* XXX wrong errno */ + ATF_REQUIRE_MSG(errno == ENOSPC, "mmap returned %d", errno); + + /* Overwrite a largepage mapping with a lagepage mapping. */ + addr1 = mmap(addr, ps[i], PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_FIXED, fd, 0); + ATF_REQUIRE_MSG(addr1 != MAP_FAILED, + "mmap(%zu bytes) failed; errno=%d", ps[i], errno); + ATF_REQUIRE_MSG(addr == addr1, + "mmap(%zu bytes) moved from %p to %p", ps[i], addr, addr1); + + ATF_REQUIRE(munmap(addr, ps[i] == 0)); + + /* Clobber an anonymous mapping with a superpage. */ + addr1 = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, + MAP_ANON | MAP_PRIVATE | MAP_ALIGNED(ffsl(ps[i]) - 1), -1, + 0); + ATF_REQUIRE_MSG(addr1 != MAP_FAILED, + "mmap failed; error=%d", errno); + *(volatile char *)addr1 = '\0'; + addr = mmap(addr1, ps[i], PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_FIXED, fd, 0); + ATF_REQUIRE_MSG(addr != MAP_FAILED, + "mmap failed; error=%d", errno); + ATF_REQUIRE_MSG(addr == addr1, + "mmap disobeyed MAP_FIXED, %p %p", addr, addr1); + *(volatile char *)addr = 0; /* fault */ + ATF_REQUIRE(mincore(addr, ps[i], vec) == 0); + for (size_t p = 0; p < ps[i] / PAGE_SIZE; p++) { + ATF_REQUIRE_MSG((vec[p] & MINCORE_INCORE) != 0, + "page %zu is not resident", p); + ATF_REQUIRE_MSG((vec[p] & MINCORE_PSIND(i)) != 0, + "page %zu is not resident", p); + } + + /* + * Copy-on-write mappings are not permitted. + */ + addr = mmap(NULL, ps[i], PROT_READ | PROT_WRITE, MAP_PRIVATE, + fd, 0); + ATF_REQUIRE_MSG(addr == MAP_FAILED, + "mmap(%zu bytes) succeeded", ps[i]); + + ATF_REQUIRE(close(fd) == 0); + } +} + +ATF_TC_WITHOUT_HEAD(largepage_munmap); +ATF_TC_BODY(largepage_munmap, tc) +{ + char *addr; + size_t ps[MAXPAGESIZES], ps1; + int fd, pscnt; + + pscnt = pagesizes(ps); + for (int i = 1; i < pscnt; i++) { + fd = shm_open_large(i, SHM_LARGEPAGE_ALLOC_DEFAULT, ps[i]); + ps1 = ps[i - 1]; + + addr = mmap(NULL, ps[i], PROT_READ | PROT_WRITE, MAP_SHARED, fd, + 0); + ATF_REQUIRE_MSG(addr != MAP_FAILED, + "mmap(%zu bytes) failed; errno=%d", ps[i], errno); + + /* Try several unaligned munmap() requests. */ + ATF_REQUIRE(munmap(addr, ps1) != 0); + ATF_REQUIRE_MSG(errno == EINVAL, + "unexpected error %d from munmap", errno); + ATF_REQUIRE(munmap(addr, ps[i] - ps1)); + ATF_REQUIRE_MSG(errno == EINVAL, + "unexpected error %d from munmap", errno); + ATF_REQUIRE(munmap(addr + ps1, ps1) != 0); + ATF_REQUIRE_MSG(errno == EINVAL, + "unexpected error %d from munmap", errno); + ATF_REQUIRE(munmap(addr, 0)); + ATF_REQUIRE_MSG(errno == EINVAL, + "unexpected error %d from munmap", errno); + + ATF_REQUIRE(munmap(addr, ps[i]) == 0); + ATF_REQUIRE(close(fd) == 0); + } +} + +static void +largepage_madvise(char *addr, size_t sz, int advice, int error) +{ + if (error == 0) { + ATF_REQUIRE_MSG(madvise(addr, sz, advice) == 0, + "madvise(%zu, %d) failed; error=%d", sz, advice, errno); + } else { + ATF_REQUIRE_MSG(madvise(addr, sz, advice) != 0, + "madvise(%zu, %d) succeeded", sz, advice); + ATF_REQUIRE_MSG(errno == error, + "unexpected error %d from madvise(%zu, %d)", + errno, sz, advice); + } +} + +ATF_TC_WITHOUT_HEAD(largepage_madvise); +ATF_TC_BODY(largepage_madvise, tc) +{ + char *addr; + size_t ps[MAXPAGESIZES]; + int fd, pscnt; + + pscnt = pagesizes(ps); + for (int i = 1; i < pscnt; i++) { + fd = shm_open_large(i, SHM_LARGEPAGE_ALLOC_DEFAULT, ps[i]); + addr = mmap(NULL, ps[i], PROT_READ | PROT_WRITE, MAP_SHARED, fd, + 0); + ATF_REQUIRE_MSG(addr != MAP_FAILED, + "mmap(%zu bytes) failed; error=%d", ps[i], errno); + + /* Advice that requires clipping. */ + largepage_madvise(addr, PAGE_SIZE, MADV_NORMAL, EINVAL); + largepage_madvise(addr, ps[i], MADV_NORMAL, 0); + largepage_madvise(addr, PAGE_SIZE, MADV_RANDOM, EINVAL); + largepage_madvise(addr, ps[i], MADV_RANDOM, 0); + largepage_madvise(addr, PAGE_SIZE, MADV_SEQUENTIAL, EINVAL); + largepage_madvise(addr, ps[i], MADV_SEQUENTIAL, 0); + largepage_madvise(addr, PAGE_SIZE, MADV_NOSYNC, EINVAL); + largepage_madvise(addr, ps[i], MADV_NOSYNC, 0); + largepage_madvise(addr, PAGE_SIZE, MADV_AUTOSYNC, EINVAL); + largepage_madvise(addr, ps[i], MADV_AUTOSYNC, 0); + largepage_madvise(addr, PAGE_SIZE, MADV_CORE, EINVAL); + largepage_madvise(addr, ps[i], MADV_CORE, 0); + largepage_madvise(addr, PAGE_SIZE, MADV_NOCORE, EINVAL); + largepage_madvise(addr, ps[i], MADV_NOCORE, 0); + + /* Advice that does not result in clipping. */ + largepage_madvise(addr, PAGE_SIZE, MADV_DONTNEED, 0); + largepage_madvise(addr, ps[i], MADV_DONTNEED, 0); + largepage_madvise(addr, PAGE_SIZE, MADV_WILLNEED, 0); + largepage_madvise(addr, ps[i], MADV_WILLNEED, 0); + largepage_madvise(addr, PAGE_SIZE, MADV_FREE, 0); + largepage_madvise(addr, ps[i], MADV_FREE, 0); + + ATF_REQUIRE(munmap(addr, ps[i]) == 0); + ATF_REQUIRE(close(fd) == 0); + } +} + +ATF_TC(largepage_mlock); +ATF_TC_HEAD(largepage_mlock, tc) +{ + /* Needed to set rlimit. */ + atf_tc_set_md_var(tc, "require.user", "root"); +} +ATF_TC_BODY(largepage_mlock, tc) +{ + struct rlimit rl; + char *addr; + size_t ps[MAXPAGESIZES], sz; + u_long max_wired, wired; + int fd, error, pscnt; + + rl.rlim_cur = rl.rlim_max = RLIM_INFINITY; + ATF_REQUIRE_MSG(setrlimit(RLIMIT_MEMLOCK, &rl) == 0, + "setrlimit failed; error=%d", errno); + + sz = sizeof(max_wired); + error = sysctlbyname("vm.max_user_wired", &max_wired, &sz, NULL, 0); + ATF_REQUIRE_MSG(error == 0, + "sysctlbyname(vm.max_user_wired) failed; error=%d", errno); + ATF_REQUIRE(max_wired >= 0); + + sz = sizeof(wired); + error = sysctlbyname("vm.stats.vm.v_user_wire_count", &wired, &sz, NULL, + 0); + ATF_REQUIRE_MSG(error == 0, + "sysctlbyname(vm.stats.vm.v_user_wire_count) failed; error=%d", + errno); + ATF_REQUIRE(wired >= 0); + + pscnt = pagesizes(ps); + for (int i = 1; i < pscnt; i++) { + if (ps[i] / PAGE_SIZE > max_wired - wired) { + /* Cannot wire past the limit. */ + atf_tc_skip("test would exceed wiring limit"); + } + + fd = shm_open_large(i, SHM_LARGEPAGE_ALLOC_DEFAULT, ps[i]); + addr = mmap(NULL, ps[i], PROT_READ | PROT_WRITE, MAP_SHARED, fd, + 0); + ATF_REQUIRE_MSG(addr != MAP_FAILED, + "mmap(%zu bytes) failed; error=%d", ps[i], errno); + + ATF_REQUIRE(mlock(addr, PAGE_SIZE) != 0); + ATF_REQUIRE_MSG(errno == EINVAL, + "unexpected error %d from mlock(%zu bytes)", errno, ps[i]); + ATF_REQUIRE(mlock(addr, ps[i] - PAGE_SIZE) != 0); + ATF_REQUIRE_MSG(errno == EINVAL, + "unexpected error %d from mlock(%zu bytes)", errno, ps[i]); + + ATF_REQUIRE_MSG(mlock(addr, ps[i]) == 0, + "mlock failed; error=%d", errno); + + ATF_REQUIRE(munmap(addr, ps[i]) == 0); + + ATF_REQUIRE(mlockall(MCL_FUTURE) == 0); + addr = mmap(NULL, ps[i], PROT_READ | PROT_WRITE, MAP_SHARED, fd, + 0); + ATF_REQUIRE_MSG(addr != MAP_FAILED, + "mmap(%zu bytes) failed; error=%d", ps[i], errno); + + ATF_REQUIRE(munmap(addr, ps[i]) == 0); + ATF_REQUIRE(close(fd) == 0); + } +} + +ATF_TC_WITHOUT_HEAD(largepage_msync); +ATF_TC_BODY(largepage_msync, tc) +{ + char *addr; + size_t ps[MAXPAGESIZES]; + int fd, pscnt; + + pscnt = pagesizes(ps); + for (int i = 1; i < pscnt; i++) { + fd = shm_open_large(i, SHM_LARGEPAGE_ALLOC_DEFAULT, ps[i]); + addr = mmap(NULL, ps[i], PROT_READ | PROT_WRITE, MAP_SHARED, fd, + 0); + ATF_REQUIRE_MSG(addr != MAP_FAILED, + "mmap(%zu bytes) failed; error=%d", ps[i], errno); + + memset(addr, 0, ps[i]); + + /* + * "Sync" requests are no-ops for SHM objects, so small + * PAGE_SIZE-sized requests succeed. + */ + ATF_REQUIRE_MSG(msync(addr, PAGE_SIZE, MS_ASYNC) == 0, + "msync(MS_ASYNC) failed; error=%d", errno); + ATF_REQUIRE_MSG(msync(addr, ps[i], MS_ASYNC) == 0, + "msync(MS_ASYNC) failed; error=%d", errno); + ATF_REQUIRE_MSG(msync(addr, PAGE_SIZE, MS_SYNC) == 0, + "msync(MS_SYNC) failed; error=%d", errno); + ATF_REQUIRE_MSG(msync(addr, ps[i], MS_SYNC) == 0, + "msync(MS_SYNC) failed; error=%d", errno); + + ATF_REQUIRE_MSG(msync(addr, PAGE_SIZE, MS_INVALIDATE) != 0, + "msync(MS_INVALIDATE) succeeded"); + /* XXX wrong errno */ + ATF_REQUIRE_MSG(errno == EBUSY, + "unexpected error %d from msync(MS_INVALIDATE)", errno); + ATF_REQUIRE_MSG(msync(addr, ps[i], MS_INVALIDATE) == 0, + "msync(MS_INVALIDATE) failed; error=%d", errno); + memset(addr, 0, ps[i]); + + ATF_REQUIRE(munmap(addr, ps[i]) == 0); + ATF_REQUIRE(close(fd) == 0); + } +} + +static void +largepage_protect(char *addr, size_t sz, int prot, int error) +{ + if (error == 0) { + ATF_REQUIRE_MSG(mprotect(addr, sz, prot) == 0, + "mprotect(%zu, %x) failed; error=%d", sz, prot, errno); + } else { + ATF_REQUIRE_MSG(mprotect(addr, sz, prot) != 0, + "mprotect(%zu, %x) succeeded", sz, prot); + ATF_REQUIRE_MSG(errno == error, + "unexpected error %d from mprotect(%zu, %x)", + errno, sz, prot); + } +} + +ATF_TC_WITHOUT_HEAD(largepage_mprotect); +ATF_TC_BODY(largepage_mprotect, tc) +{ + char *addr, *addr1; + size_t ps[MAXPAGESIZES]; + int fd, pscnt; + + pscnt = pagesizes(ps); + for (int i = 1; i < pscnt; i++) { + fd = shm_open_large(i, SHM_LARGEPAGE_ALLOC_DEFAULT, ps[i]); + addr = mmap(NULL, ps[i], PROT_READ | PROT_WRITE, MAP_SHARED, fd, + 0); + ATF_REQUIRE_MSG(addr != MAP_FAILED, + "mmap(%zu bytes) failed; error=%d", ps[i], errno); + + /* + * These should be no-ops from the pmap perspective since the + * page is not yet entered into the pmap. + */ + largepage_protect(addr, PAGE_SIZE, PROT_READ, EINVAL); + largepage_protect(addr, ps[i], PROT_READ, 0); + largepage_protect(addr, PAGE_SIZE, PROT_NONE, EINVAL); + largepage_protect(addr, ps[i], PROT_NONE, 0); + largepage_protect(addr, PAGE_SIZE, + PROT_READ | PROT_WRITE | PROT_EXEC, EINVAL); + largepage_protect(addr, ps[i], + PROT_READ | PROT_WRITE | PROT_EXEC, 0); + + /* Trigger creation of a mapping and try again. */ + *(volatile char *)addr = 0; + largepage_protect(addr, PAGE_SIZE, PROT_READ, EINVAL); + largepage_protect(addr, ps[i], PROT_READ, 0); + largepage_protect(addr, PAGE_SIZE, PROT_NONE, EINVAL); + largepage_protect(addr, ps[i], PROT_NONE, 0); + largepage_protect(addr, PAGE_SIZE, + PROT_READ | PROT_WRITE | PROT_EXEC, EINVAL); + largepage_protect(addr, ps[i], + PROT_READ | PROT_WRITE | PROT_EXEC, 0); + + memset(addr, 0, ps[i]); + + /* Map two contiguous large pages and merge map entries. */ + addr1 = mmap(addr + ps[i], ps[i], PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_FIXED | MAP_EXCL, fd, 0); + ATF_REQUIRE_MSG(addr1 != MAP_FAILED, + "mmap(%zu bytes) failed; error=%d", ps[i], errno); + + largepage_protect(addr1 - PAGE_SIZE, PAGE_SIZE * 2, + PROT_READ | PROT_WRITE, EINVAL); + largepage_protect(addr, ps[i] * 2, PROT_READ | PROT_WRITE, 0); + + memset(addr, 0, ps[i] * 2); + + ATF_REQUIRE(munmap(addr, ps[i]) == 0); + ATF_REQUIRE(munmap(addr1, ps[i]) == 0); + ATF_REQUIRE(close(fd) == 0); + } +} + +ATF_TC_WITHOUT_HEAD(largepage_minherit); +ATF_TC_BODY(largepage_minherit, tc) +{ + char *addr; + size_t ps[MAXPAGESIZES]; + pid_t child; + int fd, pscnt, status; + + pscnt = pagesizes(ps); + for (int i = 1; i < pscnt; i++) { + fd = shm_open_large(i, SHM_LARGEPAGE_ALLOC_DEFAULT, ps[i]); + addr = mmap(NULL, ps[i], PROT_READ | PROT_WRITE, MAP_SHARED, fd, + 0); + ATF_REQUIRE_MSG(addr != MAP_FAILED, + "mmap(%zu bytes) failed; error=%d", ps[i], errno); + + ATF_REQUIRE(minherit(addr, PAGE_SIZE, INHERIT_SHARE) != 0); + + ATF_REQUIRE_MSG(minherit(addr, ps[i], INHERIT_SHARE) == 0, + "minherit(%zu bytes) failed; error=%d", ps[i], errno); + child = fork(); + ATF_REQUIRE_MSG(child != -1, "fork failed; error=%d", errno); + if (child == 0) { + char v; + + *(volatile char *)addr = 0; + if (mincore(addr, PAGE_SIZE, &v) != 0) + _exit(1); + if ((v & MINCORE_PSIND(i)) == 0) + _exit(2); + _exit(0); + } + ATF_REQUIRE_MSG(waitpid(child, &status, 0) == child, + "waitpid failed; error=%d", errno); + ATF_REQUIRE_MSG(WIFEXITED(status), + "child was killed by signal %d", WTERMSIG(status)); + ATF_REQUIRE_MSG(WEXITSTATUS(status) == 0, + "child exited with status %d", WEXITSTATUS(status)); + + ATF_REQUIRE_MSG(minherit(addr, ps[i], INHERIT_NONE) == 0, + "minherit(%zu bytes) failed; error=%d", ps[i], errno); + child = fork(); + ATF_REQUIRE_MSG(child != -1, "fork failed; error=%d", errno); + if (child == 0) { + char v; + + if (mincore(addr, PAGE_SIZE, &v) == 0) + _exit(1); + _exit(0); + } + ATF_REQUIRE_MSG(waitpid(child, &status, 0) == child, + "waitpid failed; error=%d", errno); + ATF_REQUIRE_MSG(WIFEXITED(status), + "child was killed by signal %d", WTERMSIG(status)); + ATF_REQUIRE_MSG(WEXITSTATUS(status) == 0, + "child exited with status %d", WEXITSTATUS(status)); + + /* Copy-on-write is not supported for static large pages. */ + ATF_REQUIRE_MSG(minherit(addr, ps[i], INHERIT_COPY) != 0, + "minherit(%zu bytes) succeeded", ps[i]); + + ATF_REQUIRE_MSG(minherit(addr, ps[i], INHERIT_ZERO) == 0, + "minherit(%zu bytes) failed; error=%d", ps[i], errno); + child = fork(); + ATF_REQUIRE_MSG(child != -1, "fork failed; error=%d", errno); + if (child == 0) { + char v; + + *(volatile char *)addr = 0; + if (mincore(addr, PAGE_SIZE, &v) != 0) + _exit(1); + if ((v & MINCORE_SUPER) != 0) + _exit(2); + _exit(0); + } + ATF_REQUIRE_MSG(waitpid(child, &status, 0) == child, + "waitpid failed; error=%d", errno); + ATF_REQUIRE_MSG(WIFEXITED(status), + "child was killed by signal %d", WTERMSIG(status)); + ATF_REQUIRE_MSG(WEXITSTATUS(status) == 0, + "child exited with status %d", WEXITSTATUS(status)); + + ATF_REQUIRE(munmap(addr, ps[i]) == 0); + ATF_REQUIRE(close(fd) == 0); + } +} + +ATF_TC_WITHOUT_HEAD(largepage_pipe); +ATF_TC_BODY(largepage_pipe, tc) +{ + size_t ps[MAXPAGESIZES]; + char *addr; + ssize_t len; + int fd, pfd[2], pscnt, status; + pid_t child; + + pscnt = pagesizes(ps); + + for (int i = 1; i < pscnt; i++) { + fd = shm_open_large(i, SHM_LARGEPAGE_ALLOC_DEFAULT, ps[i]); + addr = mmap(NULL, ps[i], PROT_READ | PROT_WRITE, MAP_SHARED, fd, + 0); + ATF_REQUIRE_MSG(addr != MAP_FAILED, + "mmap(%zu bytes) failed; error=%d", ps[i], errno); + + /* Trigger creation of a mapping. */ + *(volatile char *)addr = '\0'; + + ATF_REQUIRE(pipe(pfd) == 0); + child = fork(); + ATF_REQUIRE_MSG(child != -1, "fork() failed; error=%d", errno); + if (child == 0) { + char buf[BUFSIZ]; + ssize_t resid; + + (void)close(pfd[0]); + for (resid = (size_t)ps[i]; resid > 0; resid -= len) { + len = read(pfd[1], buf, sizeof(buf)); + if (len < 0) + _exit(1); + } + _exit(0); + } + ATF_REQUIRE(close(pfd[1]) == 0); + len = write(pfd[0], addr, ps[i]); + ATF_REQUIRE_MSG(len >= 0, "write() failed; error=%d", errno); + ATF_REQUIRE_MSG(len == (ssize_t)ps[i], + "short write; len=%zd", len); + ATF_REQUIRE(close(pfd[0]) == 0); + + ATF_REQUIRE_MSG(waitpid(child, &status, 0) == child, + "waitpid() failed; error=%d", errno); + ATF_REQUIRE_MSG(WIFEXITED(status), + "child was killed by signal %d", WTERMSIG(status)); + ATF_REQUIRE_MSG(WEXITSTATUS(status) == 0, + "child exited with status %d", WEXITSTATUS(status)); + + ATF_REQUIRE(munmap(addr, ps[i]) == 0); + ATF_REQUIRE(close(fd) == 0); + } +} + +ATF_TC_WITHOUT_HEAD(largepage_reopen); +ATF_TC_BODY(largepage_reopen, tc) +{ + char *addr, *vec; + size_t ps[MAXPAGESIZES]; + int fd, psind; + + (void)pagesizes(ps); + psind = 1; + + gen_test_path(); + fd = shm_create_largepage(test_path, O_CREAT | O_RDWR, psind, + SHM_LARGEPAGE_ALLOC_DEFAULT, 0600); + if (fd < 0 && errno == ENOTTY) + atf_tc_skip("no large page support"); + ATF_REQUIRE_MSG(fd >= 0, "shm_create_largepage failed; error=%d", errno); + + ATF_REQUIRE_MSG(ftruncate(fd, ps[psind]) == 0, + "ftruncate failed; error=%d", errno); + + ATF_REQUIRE_MSG(close(fd) == 0, "close failed; error=%d", errno); + + fd = shm_open(test_path, O_RDWR, 0); + ATF_REQUIRE_MSG(fd >= 0, "shm_open failed; error=%d", errno); + + addr = mmap(NULL, ps[psind], PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + ATF_REQUIRE_MSG(addr != MAP_FAILED, "mmap failed; error=%d", errno); + + /* Trigger a fault and mapping creation. */ + *(volatile char *)addr = 0; + + vec = malloc(ps[psind] / PAGE_SIZE); + ATF_REQUIRE(vec != NULL); + ATF_REQUIRE_MSG(mincore(addr, ps[psind], vec) == 0, + "mincore failed; error=%d", errno); + ATF_REQUIRE_MSG((vec[0] & MINCORE_PSIND(psind)) != 0, + "page not mapped into a %zu-byte superpage", ps[psind]); + + ATF_REQUIRE_MSG(shm_unlink(test_path) == 0, + "shm_unlink failed; errno=%d", errno); + ATF_REQUIRE_MSG(close(fd) == 0, + "close failed; errno=%d", errno); +} + ATF_TP_ADD_TCS(tp) { @@ -990,6 +1706,17 @@ ATF_TP_ADD_TC(tp, cloexec); ATF_TP_ADD_TC(tp, mode); ATF_TP_ADD_TC(tp, fallocate); + ATF_TP_ADD_TC(tp, largepage_basic); + ATF_TP_ADD_TC(tp, largepage_config); + ATF_TP_ADD_TC(tp, largepage_mmap); + ATF_TP_ADD_TC(tp, largepage_munmap); + ATF_TP_ADD_TC(tp, largepage_madvise); + ATF_TP_ADD_TC(tp, largepage_mlock); + ATF_TP_ADD_TC(tp, largepage_msync); + ATF_TP_ADD_TC(tp, largepage_mprotect); + ATF_TP_ADD_TC(tp, largepage_minherit); + ATF_TP_ADD_TC(tp, largepage_pipe); + ATF_TP_ADD_TC(tp, largepage_reopen); return (atf_no_error()); }