KVM: selftests: access_tracking_perf_test: Add option to skip the sanity check
authorMaxim Levitsky <mlevitsk@redhat.com>
Thu, 8 May 2025 18:46:43 +0000 (18:46 +0000)
committerSean Christopherson <seanjc@google.com>
Fri, 16 May 2025 18:45:12 +0000 (11:45 -0700)
Add an option to skip sanity check of number of still idle pages,
and set it by default to skip, in case hypervisor or NUMA balancing
is detected.

Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
Co-developed-by: James Houghton <jthoughton@google.com>
Signed-off-by: James Houghton <jthoughton@google.com>
Reviewed-by: Maxim Levitsky <mlevitsk@redhat.com>
Link: https://lore.kernel.org/r/20250508184649.2576210-3-jthoughton@google.com
Signed-off-by: Sean Christopherson <seanjc@google.com>
tools/testing/selftests/kvm/access_tracking_perf_test.c
tools/testing/selftests/kvm/include/test_util.h
tools/testing/selftests/kvm/lib/test_util.c

index 447e619cf856e60eb9c0126ad40d9e8b90c10f93..f273e4a33b11d18edbe4b778f7f290fd3bb56117 100644 (file)
@@ -65,6 +65,16 @@ static int vcpu_last_completed_iteration[KVM_MAX_VCPUS];
 /* Whether to overlap the regions of memory vCPUs access. */
 static bool overlap_memory_access;
 
+/*
+ * If the test should only warn if there are too many idle pages (i.e., it is
+ * expected).
+ * -1: Not yet set.
+ *  0: We do not expect too many idle pages, so FAIL if too many idle pages.
+ *  1: Having too many idle pages is expected, so merely print a warning if
+ *     too many idle pages are found.
+ */
+static int idle_pages_warn_only = -1;
+
 struct test_params {
        /* The backing source for the region of memory. */
        enum vm_mem_backing_src_type backing_src;
@@ -177,18 +187,12 @@ static void mark_vcpu_memory_idle(struct kvm_vm *vm,
         * arbitrary; high enough that we ensure most memory access went through
         * access tracking but low enough as to not make the test too brittle
         * over time and across architectures.
-        *
-        * When running the guest as a nested VM, "warn" instead of asserting
-        * as the TLB size is effectively unlimited and the KVM doesn't
-        * explicitly flush the TLB when aging SPTEs.  As a result, more pages
-        * are cached and the guest won't see the "idle" bit cleared.
         */
        if (still_idle >= pages / 10) {
-#ifdef __x86_64__
-               TEST_ASSERT(this_cpu_has(X86_FEATURE_HYPERVISOR),
+               TEST_ASSERT(idle_pages_warn_only,
                            "vCPU%d: Too many pages still idle (%lu out of %lu)",
                            vcpu_idx, still_idle, pages);
-#endif
+
                printf("WARNING: vCPU%d: Too many pages still idle (%lu out of %lu), "
                       "this will affect performance results.\n",
                       vcpu_idx, still_idle, pages);
@@ -328,6 +332,32 @@ static void run_test(enum vm_guest_mode mode, void *arg)
        memstress_destroy_vm(vm);
 }
 
+static int access_tracking_unreliable(void)
+{
+#ifdef __x86_64__
+       /*
+        * When running nested, the TLB size may be effectively unlimited (for
+        * example, this is the case when running on KVM L0), and KVM doesn't
+        * explicitly flush the TLB when aging SPTEs.  As a result, more pages
+        * are cached and the guest won't see the "idle" bit cleared.
+        */
+       if (this_cpu_has(X86_FEATURE_HYPERVISOR)) {
+               puts("Skipping idle page count sanity check, because the test is run nested");
+               return 1;
+       }
+#endif
+       /*
+        * When NUMA balancing is enabled, guest memory will be unmapped to get
+        * NUMA faults, dropping the Accessed bits.
+        */
+       if (is_numa_balancing_enabled()) {
+               puts("Skipping idle page count sanity check, because NUMA balancing is enabled");
+               return 1;
+       }
+
+       return 0;
+}
+
 static void help(char *name)
 {
        puts("");
@@ -342,6 +372,12 @@ static void help(char *name)
        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");
+       printf(" -w: Control whether the test warns or fails if more than 10%%\n"
+              "     of pages are still seen as idle/old after accessing guest\n"
+              "     memory.  >0 == warn only, 0 == fail, <0 == auto.  For auto\n"
+              "     mode, the test fails by default, but switches to warn only\n"
+              "     if NUMA balancing is enabled or the test detects it's running\n"
+              "     in a VM.\n");
        backing_src_help("-s");
        puts("");
        exit(0);
@@ -359,7 +395,7 @@ int main(int argc, char *argv[])
 
        guest_modes_append_default();
 
-       while ((opt = getopt(argc, argv, "hm:b:v:os:")) != -1) {
+       while ((opt = getopt(argc, argv, "hm:b:v:os:w:")) != -1) {
                switch (opt) {
                case 'm':
                        guest_modes_cmdline(optarg);
@@ -376,6 +412,11 @@ int main(int argc, char *argv[])
                case 's':
                        params.backing_src = parse_backing_src_type(optarg);
                        break;
+               case 'w':
+                       idle_pages_warn_only =
+                               atoi_non_negative("Idle pages warning",
+                                                 optarg);
+                       break;
                case 'h':
                default:
                        help(argv[0]);
@@ -388,6 +429,9 @@ int main(int argc, char *argv[])
                       "CONFIG_IDLE_PAGE_TRACKING is not enabled");
        close(page_idle_fd);
 
+       if (idle_pages_warn_only == -1)
+               idle_pages_warn_only = access_tracking_unreliable();
+
        for_each_guest_mode(run_test, &params);
 
        return 0;
index 77d13d7920cb835bc64a592ca70af7113f47f88d..c6ef895fbd9abed1859b30a8a0386fe376dba2dc 100644 (file)
@@ -153,6 +153,7 @@ bool is_backing_src_hugetlb(uint32_t i);
 void backing_src_help(const char *flag);
 enum vm_mem_backing_src_type parse_backing_src_type(const char *type_name);
 long get_run_delay(void);
+bool is_numa_balancing_enabled(void);
 
 /*
  * Whether or not the given source type is shared memory (as opposed to
index 3dc8538f5d69648e792b91507bba8b3d311c87e7..03eb99af9b8debe8bb6fe32ba54de75efba52aab 100644 (file)
@@ -176,6 +176,13 @@ size_t get_trans_hugepagesz(void)
        return get_sysfs_val("/sys/kernel/mm/transparent_hugepage/hpage_pmd_size");
 }
 
+bool is_numa_balancing_enabled(void)
+{
+       if (!test_sysfs_path("/proc/sys/kernel/numa_balancing"))
+               return false;
+       return get_sysfs_val("/proc/sys/kernel/numa_balancing") == 1;
+}
+
 size_t get_def_hugetlb_pagesz(void)
 {
        char buf[64];