/* * There is no easy way to check if there are KSM pages mapped into * this range. We only check that the range does not map the same PFN * twice by comparing each pair of mapped pages.
*/ for (offs_a = 0; offs_a < size; offs_a += pagesize) {
pfn_a = pagemap_get_pfn(pagemap_fd, addr + offs_a); /* Page not present or PFN not exposed by the kernel. */ if (pfn_a == -1ul || !pfn_a) continue;
ret = pread(ksm_full_scans_fd, buf, sizeof(buf) - 1, 0); if (ret <= 0) return -errno;
buf[ret] = 0;
return strtol(buf, NULL, 10);
}
staticint ksm_merge(void)
{ long start_scans, end_scans;
/* Wait for two full scans such that any possible merging happened. */
start_scans = ksm_get_full_scans(); if (start_scans < 0) return start_scans; if (write(ksm_fd, "1", 1) != 1) return -errno; do {
end_scans = ksm_get_full_scans(); if (end_scans < 0) return end_scans;
} while (end_scans < start_scans + 2);
/* Don't use THP. Ignore if THP are not around on a kernel. */ if (madvise(map, size, MADV_NOHUGEPAGE) && errno != EINVAL) {
ksft_print_msg("MADV_NOHUGEPAGE failed\n"); goto unmap;
}
/* Make sure each page contains the same values to merge them. */
memset(map, val, size);
switch (mode) { case KSM_MERGE_PRCTL:
ret = prctl(PR_SET_MEMORY_MERGE, 1, 0, 0, 0); if (ret < 0 && errno == EINVAL) {
ksft_print_msg("PR_SET_MEMORY_MERGE not supported\n");
err_map = MAP_MERGE_SKIP; goto unmap;
} elseif (ret) {
ksft_print_msg("PR_SET_MEMORY_MERGE=1 failed\n"); goto unmap;
} break; case KSM_MERGE_MADVISE: if (madvise(map, size, MADV_MERGEABLE)) {
ksft_print_msg("MADV_MERGEABLE failed\n"); goto unmap;
} break; case KSM_MERGE_NONE: break;
}
/* Run KSM to trigger merging and wait. */ if (ksm_merge()) {
ksft_print_msg("Running KSM failed\n"); goto unmap;
}
/* * Check if anything was merged at all. Ignore the zero page that is * accounted differently (depending on kernel support).
*/ if (val && !get_my_merging_pages()) {
ksft_print_msg("No pages got merged\n"); goto unmap;
}
/* Let KSM deduplicate zero pages. */
map = mmap_and_merge_range(0x00, size, PROT_READ | PROT_WRITE, KSM_MERGE_MADVISE); if (map == MAP_FAILED) return;
/* Check if ksm_zero_pages is updated correctly after KSM merging */
pages_expected = size / pagesize; if (pages_expected != get_my_ksm_zero_pages()) {
ksft_test_result_fail("'ksm_zero_pages' updated after merging\n"); goto unmap;
}
/* Try to unmerge half of the region */ if (madvise(map, size / 2, MADV_UNMERGEABLE)) {
ksft_test_result_fail("MADV_UNMERGEABLE failed\n"); goto unmap;
}
/* Check if ksm_zero_pages is updated correctly after unmerging */
pages_expected /= 2; if (pages_expected != get_my_ksm_zero_pages()) {
ksft_test_result_fail("'ksm_zero_pages' updated after unmerging\n"); goto unmap;
}
/* Trigger unmerging of the other half by writing to the pages. */ for (offs = size / 2; offs < size; offs += pagesize)
*((unsignedint *)&map[offs]) = offs;
/* Now we should have no zeropages remaining. */ if (get_my_ksm_zero_pages()) {
ksft_test_result_fail("'ksm_zero_pages' updated after write fault\n"); goto unmap;
}
/* Check if ksm zero pages are really unmerged */
ksft_test_result(!range_maps_duplicates(map, size), "KSM zero pages were unmerged\n");
unmap:
munmap(map, size);
}
/* Discard half of all mapped pages so we have pte_none() entries. */ if (madvise(map, size / 2, MADV_DONTNEED)) {
ksft_test_result_fail("MADV_DONTNEED failed\n"); goto unmap;
}
if (madvise(map, size, MADV_UNMERGEABLE)) {
ksft_test_result_fail("MADV_UNMERGEABLE failed\n"); goto unmap;
}
ksft_test_result(!range_maps_duplicates(map, size), "Pages were unmerged\n");
unmap:
munmap(map, size);
}
/* See if UFFD is around. */
uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK); if (uffd < 0) {
ksft_test_result_skip("__NR_userfaultfd failed\n"); goto unmap;
}
/* See if UFFD-WP is around. */
uffdio_api.api = UFFD_API;
uffdio_api.features = 0; if (ioctl(uffd, UFFDIO_API, &uffdio_api) < 0) { if (errno == EINVAL)
ksft_test_result_skip("The API version requested is not supported\n"); else
ksft_test_result_fail("UFFDIO_API failed: %s\n", strerror(errno));
goto close_uffd;
} if (!(uffdio_api.features & UFFD_FEATURE_PAGEFAULT_FLAG_WP)) {
ksft_test_result_skip("UFFD_FEATURE_PAGEFAULT_FLAG_WP not available\n"); goto close_uffd;
}
/* * UFFDIO_API must only be called once to enable features. * So we close the old userfaultfd and create a new one to * actually enable UFFD_FEATURE_PAGEFAULT_FLAG_WP.
*/
close(uffd);
uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK); if (uffd < 0) {
ksft_test_result_fail("__NR_userfaultfd failed\n"); goto unmap;
}
/* Register UFFD-WP, no need for an actual handler. */ if (uffd_register(uffd, map, size, false, true, false)) {
ksft_test_result_fail("UFFDIO_REGISTER_MODE_WP failed\n"); goto close_uffd;
}
/* Write-protect the range using UFFD-WP. */
uffd_writeprotect.range.start = (unsignedlong) map;
uffd_writeprotect.range.len = size;
uffd_writeprotect.mode = UFFDIO_WRITEPROTECT_MODE_WP; if (ioctl(uffd, UFFDIO_WRITEPROTECT, &uffd_writeprotect)) {
ksft_test_result_fail("UFFDIO_WRITEPROTECT failed\n"); goto close_uffd;
}
if (madvise(map, size, MADV_UNMERGEABLE)) {
ksft_test_result_fail("MADV_UNMERGEABLE failed\n"); goto close_uffd;
}
/* Store a unique value in each page on one half using ptrace */ for (i = 0; i < size / 2; i += pagesize) {
lseek(mem_fd, (uintptr_t) map + i, SEEK_SET); if (write(mem_fd, &i, sizeof(i)) != sizeof(i)) {
ksft_test_result_fail("ptrace write failed\n"); goto unmap;
}
}
/* Trigger unsharing on the other half. */ if (madvise(map + size / 2, size / 2, MADV_UNMERGEABLE)) {
ksft_test_result_fail("MADV_UNMERGEABLE failed\n"); goto unmap;
}
ksft_test_result(!range_maps_duplicates(map, size), "Pages were unmerged\n");
unmap:
munmap(map, size);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.