r = ioctl(uffd, UFFDIO_CONTINUE, &cont); /* * With multiple vCPU threads fault on a single page and there are * multiple readers for the UFFD, at least one of the UFFDIO_COPYs * will fail with EEXIST: handle that case without signaling an * error. * * Note that this also suppress any EEXISTs occurring from, * e.g., the first UFFDIO_COPY/CONTINUEs on a page. That never * happens here, but a realistic VMM might potentially maintain * some external state to correctly surface EEXISTs to userspace * (or prevent duplicate COPY/CONTINUEs in the first place).
*/ if (r == -1 && errno != EEXIST) {
pr_info("Failed UFFDIO_CONTINUE in 0x%lx, thread %d, errno = %d\n",
addr, tid, errno); return r;
}
} else {
TEST_FAIL("Invalid uffd mode %d", uffd_mode);
}
ts_diff = timespec_elapsed(start);
PER_PAGE_DEBUG("UFFD page-in %d \t%ld ns\n", tid,
timespec_to_ns(ts_diff));
PER_PAGE_DEBUG("Paged in %ld bytes at 0x%lx from thread %d\n",
demand_paging_size, addr, tid);
return 0;
}
struct test_params { int uffd_mode; bool single_uffd;
useconds_t uffd_delay; int readers_per_uffd; enum vm_mem_backing_src_type src_type; bool partition_vcpu_memory_access;
};
guest_data_prototype = malloc(demand_paging_size);
TEST_ASSERT(guest_data_prototype, "Failed to allocate buffer for guest data pattern");
memset(guest_data_prototype, 0xAB, demand_paging_size);
if (p->uffd_mode == UFFDIO_REGISTER_MODE_MINOR) {
num_uffds = p->single_uffd ? 1 : nr_vcpus; for (i = 0; i < num_uffds; i++) {
vcpu_args = &memstress_args.vcpu_args[i];
prefault_mem(addr_gpa2alias(vm, vcpu_args->gpa),
vcpu_args->pages * memstress_args.guest_page_size);
}
}
uffd_descs = malloc(num_uffds * sizeof(struct uffd_desc *));
TEST_ASSERT(uffd_descs, "Memory allocation failed"); for (i = 0; i < num_uffds; i++) { struct memstress_vcpu_args *vcpu_args; void *vcpu_hva;
vcpu_args = &memstress_args.vcpu_args[i];
/* Cache the host addresses of the region */
vcpu_hva = addr_gpa2hva(vm, vcpu_args->gpa); /* * Set up user fault fd to handle demand paging * requests.
*/
uffd_descs[i] = uffd_setup_demand_paging(
p->uffd_mode, p->uffd_delay, vcpu_hva,
uffd_region_size,
p->readers_per_uffd,
&handle_uffd_page_request);
}
}
pr_info("Finished creating vCPUs and starting uffd threads\n");
clock_gettime(CLOCK_MONOTONIC, &start);
memstress_start_vcpu_threads(nr_vcpus, vcpu_worker);
pr_info("Started all vCPUs\n");
free(guest_data_prototype); if (p->uffd_mode)
free(uffd_descs);
}
staticvoid help(char *name)
{
puts("");
printf("usage: %s [-h] [-m vm_mode] [-u uffd_mode] [-a]\n" " [-d uffd_delay_usec] [-r readers_per_uffd] [-b memory]\n" " [-s type] [-v vcpus] [-c cpu_list] [-o]\n", name);
guest_modes_help();
printf(" -u: use userfaultfd to handle vCPU page faults. Mode is a\n" " UFFD registration mode: 'MISSING' or 'MINOR'.\n");
kvm_print_vcpu_pinning_help();
printf(" -a: Use a single userfaultfd for all of guest memory, instead of\n" " creating one for each region paged by a unique vCPU\n" " Set implicitly with -o, and no effect without -u.\n");
printf(" -d: add a delay in usec to the User Fault\n" " FD handler to simulate demand paging\n" " overheads. Ignored without -u.\n");
printf(" -r: Set the number of reader threads per uffd.\n");
printf(" -b: specify the size of the memory region which should be\n" " demand paged by each vCPU. e.g. 10M or 3G.\n" " Default: 1G\n");
backing_src_help("-s");
printf(" -v: specify the number of vCPUs to run.\n");
printf(" -o: Overlap guest memory accesses instead of partitioning\n" " them into a separate region of memory for each vCPU.\n");
puts(""); exit(0);
}
int main(int argc, char *argv[])
{ int max_vcpus = kvm_check_cap(KVM_CAP_MAX_VCPUS); constchar *cpulist = NULL; struct test_params p = {
.src_type = DEFAULT_VM_MEM_SRC,
.partition_vcpu_memory_access = true,
.readers_per_uffd = 1,
.single_uffd = false,
}; int opt;
guest_modes_append_default();
while ((opt = getopt(argc, argv, "ahom:u:d:b:s:v:c:r:")) != -1) { switch (opt) { case'm':
guest_modes_cmdline(optarg); break; case'u': if (!strcmp("MISSING", optarg))
p.uffd_mode = UFFDIO_REGISTER_MODE_MISSING; elseif (!strcmp("MINOR", optarg))
p.uffd_mode = UFFDIO_REGISTER_MODE_MINOR;
TEST_ASSERT(p.uffd_mode, "UFFD mode must be 'MISSING' or 'MINOR'."); break; case'a':
p.single_uffd = true; break; case'd':
p.uffd_delay = strtoul(optarg, NULL, 0);
TEST_ASSERT(p.uffd_delay >= 0, "A negative UFFD delay is not supported."); break; case'b':
guest_percpu_mem_size = parse_size(optarg); break; case's':
p.src_type = parse_backing_src_type(optarg); break; case'v':
nr_vcpus = atoi_positive("Number of vCPUs", optarg);
TEST_ASSERT(nr_vcpus <= max_vcpus, "Invalid number of vcpus, must be between 1 and %d", max_vcpus); break; case'c':
cpulist = optarg; break; case'o':
p.partition_vcpu_memory_access = false;
p.single_uffd = true; break; case'r':
p.readers_per_uffd = atoi(optarg);
TEST_ASSERT(p.readers_per_uffd >= 1, "Invalid number of readers per uffd %d: must be >=1",
p.readers_per_uffd); break; case'h': default:
help(argv[0]); break;
}
}
if (p.uffd_mode == UFFDIO_REGISTER_MODE_MINOR &&
!backing_src_is_shared(p.src_type)) {
TEST_FAIL("userfaultfd MINOR mode requires shared memory; pick a different -s");
}
if (cpulist) {
kvm_parse_vcpu_pinning(cpulist, memstress_args.vcpu_to_pcpu,
nr_vcpus);
memstress_args.pin_vcpus = true;
}
for_each_guest_mode(run_test, &p);
return 0;
}
#else/* __NR_userfaultfd */
#warning"missing __NR_userfaultfd definition"
int main(void)
{
print_skip("__NR_userfaultfd must be present for userfaultfd test"); return KSFT_SKIP;
}
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.