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);
/* 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;
}
/* 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.