staticvoid shmem_check_pmd_mapping(void *p, int expect_nr_hpages)
{ if (!check_huge_shmem(area_dst_alias, expect_nr_hpages,
read_pmd_pagesize()))
err("Did not find expected %d number of hugepages",
expect_nr_hpages);
}
void uffd_stats_report(struct uffd_args *args, int n_cpus)
{ int i; unsignedlonglong miss_total = 0, wp_total = 0, minor_total = 0;
for (i = 0; i < n_cpus; i++) {
miss_total += args[i].missing_faults;
wp_total += args[i].wp_faults;
minor_total += args[i].minor_faults;
}
printf("userfaults: "); if (miss_total) {
printf("%llu missing (", miss_total); for (i = 0; i < n_cpus; i++)
printf("%lu+", args[i].missing_faults);
printf("\b) ");
} if (wp_total) {
printf("%llu wp (", wp_total); for (i = 0; i < n_cpus; i++)
printf("%lu+", args[i].wp_faults);
printf("\b) ");
} if (minor_total) {
printf("%llu minor (", minor_total); for (i = 0; i < n_cpus; i++)
printf("%lu+", args[i].minor_faults);
printf("\b)");
}
printf("\n");
}
int userfaultfd_open(uint64_t *features)
{ struct uffdio_api uffdio_api;
int uffd_test_ctx_init(uint64_t features, constchar **errmsg)
{ unsignedlong nr, cpu; int ret;
if (uffd_test_case_ops && uffd_test_case_ops->pre_alloc) {
ret = uffd_test_case_ops->pre_alloc(errmsg); if (ret) return ret;
}
ret = uffd_test_ops->allocate_area((void **)&area_src, true);
ret |= uffd_test_ops->allocate_area((void **)&area_dst, false); if (ret) { if (errmsg)
*errmsg = "memory allocation failed"; return ret;
}
if (uffd_test_case_ops && uffd_test_case_ops->post_alloc) {
ret = uffd_test_case_ops->post_alloc(errmsg); if (ret) return ret;
}
ret = userfaultfd_open(&features); if (ret) { if (errmsg)
*errmsg = "possible lack of privilege"; return ret;
}
count_verify = malloc(nr_pages * sizeof(unsignedlonglong)); if (!count_verify)
err("count_verify");
for (nr = 0; nr < nr_pages; nr++) {
*area_mutex(area_src, nr) =
(pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;
count_verify[nr] = *area_count(area_src, nr) = 1; /* * In the transition between 255 to 256, powerpc will * read out of order in my_bcmp and see both bytes as * zero, so leave a placeholder below always non-zero * after the count, to avoid my_bcmp to trigger false * positives.
*/
*(area_count(area_src, nr) + 1) = 1;
}
/* * After initialization of area_src, we must explicitly release pages * for area_dst to make sure it's fully empty. Otherwise we could have * some area_dst pages be erroneously initialized with zero pages, * hence we could hit memory corruption later in the test. * * One example is when THP is globally enabled, above allocate_area() * calls could have the two areas merged into a single VMA (as they * will have the same VMA flags so they're mergeable). When we * initialize the area_src above, it's possible that some part of * area_dst could have been faulted in via one huge THP that will be * shared between area_src and area_dst. It could cause some of the * area_dst won't be trapped by missing userfaults. * * This release_pages() will guarantee even if that happened, we'll * proactively split the thp and drop any accidentally initialized * pages within area_dst.
*/
uffd_test_ops->release_pages(area_dst);
pipefd = malloc(sizeof(int) * nr_parallel * 2); if (!pipefd)
err("pipefd"); for (cpu = 0; cpu < nr_parallel; cpu++) if (pipe2(&pipefd[cpu * 2], O_CLOEXEC | O_NONBLOCK))
err("pipe");
if (ioctl(ufd, UFFDIO_CONTINUE, &req))
err("UFFDIO_CONTINUE failed for address 0x%" PRIx64,
(uint64_t)start);
/* * Error handling within the kernel for continue is subtly different * from copy or zeropage, so it may be a source of bugs. Trigger an * error (-EEXIST) on purpose, to verify doing so doesn't cause a BUG.
*/
req.mapped = 0;
ret = ioctl(ufd, UFFDIO_CONTINUE, &req); if (ret >= 0 || req.mapped != -EEXIST)
err("failed to exercise UFFDIO_CONTINUE error handling, ret=%d, mapped=%" PRId64,
ret, (int64_t) req.mapped);
}
int uffd_read_msg(int ufd, struct uffd_msg *msg)
{ int ret = read(uffd, msg, sizeof(*msg));
if (ret != sizeof(*msg)) { if (ret < 0) { if (errno == EAGAIN || errno == EINTR) return 1;
err("blocking read error");
} else {
err("short read");
}
}
/* * Minor page faults * * To prove we can modify the original range for testing * purposes, we're going to bit flip this range before * continuing. * * Note that this requires all minor page fault tests operate on * area_dst (non-UFFD-registered) and area_dst_alias * (UFFD-registered).
*/
area = (uint8_t *)(area_dst +
((char *)msg->arg.pagefault.address -
area_dst_alias)); for (b = 0; b < page_size; ++b)
area[b] = ~area[b];
continue_range(uffd, msg->arg.pagefault.address, page_size,
args->apply_wp);
args->minor_faults++;
} else { /* * Missing page faults. * * Here we force a write check for each of the missing mode * faults. It's guaranteed because the only threads that * will trigger uffd faults are the locking threads, and * their first instruction to touch the missing page will * always be pthread_mutex_lock(). * * Note that here we relied on an NPTL glibc impl detail to * always read the lock type at the entry of the lock op * (pthread_mutex_t.__data.__type, offset 0x10) before * doing any locking operations to guarantee that. It's * actually not good to rely on this impl detail because * logically a pthread-compatible lib can implement the * locks without types and we can fail when linking with * them. However since we used to find bugs with this * strict check we still keep it around. Hopefully this * could be a good hint when it fails again. If one day * it'll break on some other impl of glibc we'll revisit.
*/ if (msg->arg.pagefault.flags & UFFD_PAGEFAULT_FLAG_WRITE)
err("unexpected write fault");
int uffd_open(unsignedint flags)
{ int uffd = uffd_open_sys(flags);
if (uffd < 0)
uffd = uffd_open_dev(flags);
return uffd;
}
int uffd_get_features(uint64_t *features)
{ struct uffdio_api uffdio_api = { .api = UFFD_API, .features = 0 }; /* * This should by default work in most kernels; the feature list * will be the same no matter what we pass in here.
*/ int fd = uffd_open(UFFD_USER_MODE_ONLY);
if (fd < 0) /* Maybe the kernel is older than user-only mode? */
fd = uffd_open(0);
if (fd < 0) return fd;
if (ioctl(fd, UFFDIO_API, &uffdio_api)) {
close(fd); return -errno;
}
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.