/* 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;
         * 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);
        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("");
        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);
 
        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);
                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]);
                       "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, ¶ms);
 
        return 0;