Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

KVM: selftests: Ensure all migrations are performed when test is affined

Rework the CPU selection in the migration worker to ensure the specified
number of migrations are performed when the test iteslf is affined to a
subset of CPUs. The existing logic skips iterations if the target CPU is
not in the original set of possible CPUs, which causes the test to fail
if too many iterations are skipped.

==== Test Assertion Failure ====
rseq_test.c:228: i > (NR_TASK_MIGRATIONS / 2)
pid=10127 tid=10127 errno=4 - Interrupted system call
1 0x00000000004018e5: main at rseq_test.c:227
2 0x00007fcc8fc66bf6: ?? ??:0
3 0x0000000000401959: _start at ??:?
Only performed 4 KVM_RUNs, task stalled too much?

Calculate the min/max possible CPUs as a cheap "best effort" to avoid
high runtimes when the test is affined to a small percentage of CPUs.
Alternatively, a list or xarray of the possible CPUs could be used, but
even in a horrendously inefficient setup, such optimizations are not
needed because the runtime is completely dominated by the cost of
migrating the task, and the absolute runtime is well under a minute in
even truly absurd setups, e.g. running on a subset of vCPUs in a VM that
is heavily overcommited (16 vCPUs per pCPU).

Fixes: 61e52f1630f5 ("KVM: selftests: Add a test for KVM_RUN+rseq to detect task migration bugs")
Reported-by: Dongli Zhang <dongli.zhang@oracle.com>
Signed-off-by: Sean Christopherson <seanjc@google.com>
Message-Id: <20210929234112.1862848-1-seanjc@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>

authored by

Sean Christopherson and committed by
Paolo Bonzini
7b0035ea e8a747d0

+59 -10
+59 -10
tools/testing/selftests/kvm/rseq_test.c
··· 10 10 #include <signal.h> 11 11 #include <syscall.h> 12 12 #include <sys/ioctl.h> 13 + #include <sys/sysinfo.h> 13 14 #include <asm/barrier.h> 14 15 #include <linux/atomic.h> 15 16 #include <linux/rseq.h> ··· 40 39 41 40 static pthread_t migration_thread; 42 41 static cpu_set_t possible_mask; 42 + static int min_cpu, max_cpu; 43 43 static bool done; 44 44 45 45 static atomic_t seq_cnt; ··· 59 57 TEST_ASSERT(!r, "rseq failed, errno = %d (%s)", errno, strerror(errno)); 60 58 } 61 59 60 + static int next_cpu(int cpu) 61 + { 62 + /* 63 + * Advance to the next CPU, skipping those that weren't in the original 64 + * affinity set. Sadly, there is no CPU_SET_FOR_EACH, and cpu_set_t's 65 + * data storage is considered as opaque. Note, if this task is pinned 66 + * to a small set of discontigous CPUs, e.g. 2 and 1023, this loop will 67 + * burn a lot cycles and the test will take longer than normal to 68 + * complete. 69 + */ 70 + do { 71 + cpu++; 72 + if (cpu > max_cpu) { 73 + cpu = min_cpu; 74 + TEST_ASSERT(CPU_ISSET(cpu, &possible_mask), 75 + "Min CPU = %d must always be usable", cpu); 76 + break; 77 + } 78 + } while (!CPU_ISSET(cpu, &possible_mask)); 79 + 80 + return cpu; 81 + } 82 + 62 83 static void *migration_worker(void *ign) 63 84 { 64 85 cpu_set_t allowed_mask; 65 - int r, i, nr_cpus, cpu; 86 + int r, i, cpu; 66 87 67 88 CPU_ZERO(&allowed_mask); 68 89 69 - nr_cpus = CPU_COUNT(&possible_mask); 70 - 71 - for (i = 0; i < NR_TASK_MIGRATIONS; i++) { 72 - cpu = i % nr_cpus; 73 - if (!CPU_ISSET(cpu, &possible_mask)) 74 - continue; 75 - 90 + for (i = 0, cpu = min_cpu; i < NR_TASK_MIGRATIONS; i++, cpu = next_cpu(cpu)) { 76 91 CPU_SET(cpu, &allowed_mask); 77 92 78 93 /* ··· 173 154 return NULL; 174 155 } 175 156 157 + static int calc_min_max_cpu(void) 158 + { 159 + int i, cnt, nproc; 160 + 161 + if (CPU_COUNT(&possible_mask) < 2) 162 + return -EINVAL; 163 + 164 + /* 165 + * CPU_SET doesn't provide a FOR_EACH helper, get the min/max CPU that 166 + * this task is affined to in order to reduce the time spent querying 167 + * unusable CPUs, e.g. if this task is pinned to a small percentage of 168 + * total CPUs. 169 + */ 170 + nproc = get_nprocs_conf(); 171 + min_cpu = -1; 172 + max_cpu = -1; 173 + cnt = 0; 174 + 175 + for (i = 0; i < nproc; i++) { 176 + if (!CPU_ISSET(i, &possible_mask)) 177 + continue; 178 + if (min_cpu == -1) 179 + min_cpu = i; 180 + max_cpu = i; 181 + cnt++; 182 + } 183 + 184 + return (cnt < 2) ? -EINVAL : 0; 185 + } 186 + 176 187 int main(int argc, char *argv[]) 177 188 { 178 189 int r, i, snapshot; ··· 216 167 TEST_ASSERT(!r, "sched_getaffinity failed, errno = %d (%s)", errno, 217 168 strerror(errno)); 218 169 219 - if (CPU_COUNT(&possible_mask) < 2) { 220 - print_skip("Only one CPU, task migration not possible\n"); 170 + if (calc_min_max_cpu()) { 171 + print_skip("Only one usable CPU, task migration not possible"); 221 172 exit(KSFT_SKIP); 222 173 } 223 174