Skip to content

Commit 7b0035e

Browse files
sean-jcbonzini
authored andcommitted
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: 61e52f1 ("KVM: selftests: Add a test for KVM_RUN+rseq to detect task migration bugs") Reported-by: Dongli Zhang <[email protected]> Signed-off-by: Sean Christopherson <[email protected]> Message-Id: <[email protected]> Signed-off-by: Paolo Bonzini <[email protected]>
1 parent e8a747d commit 7b0035e

File tree

1 file changed

+59
-10
lines changed

1 file changed

+59
-10
lines changed

tools/testing/selftests/kvm/rseq_test.c

Lines changed: 59 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <signal.h>
1111
#include <syscall.h>
1212
#include <sys/ioctl.h>
13+
#include <sys/sysinfo.h>
1314
#include <asm/barrier.h>
1415
#include <linux/atomic.h>
1516
#include <linux/rseq.h>
@@ -39,6 +40,7 @@ static __thread volatile struct rseq __rseq = {
3940

4041
static pthread_t migration_thread;
4142
static cpu_set_t possible_mask;
43+
static int min_cpu, max_cpu;
4244
static bool done;
4345

4446
static atomic_t seq_cnt;
@@ -57,20 +59,37 @@ static void sys_rseq(int flags)
5759
TEST_ASSERT(!r, "rseq failed, errno = %d (%s)", errno, strerror(errno));
5860
}
5961

62+
static int next_cpu(int cpu)
63+
{
64+
/*
65+
* Advance to the next CPU, skipping those that weren't in the original
66+
* affinity set. Sadly, there is no CPU_SET_FOR_EACH, and cpu_set_t's
67+
* data storage is considered as opaque. Note, if this task is pinned
68+
* to a small set of discontigous CPUs, e.g. 2 and 1023, this loop will
69+
* burn a lot cycles and the test will take longer than normal to
70+
* complete.
71+
*/
72+
do {
73+
cpu++;
74+
if (cpu > max_cpu) {
75+
cpu = min_cpu;
76+
TEST_ASSERT(CPU_ISSET(cpu, &possible_mask),
77+
"Min CPU = %d must always be usable", cpu);
78+
break;
79+
}
80+
} while (!CPU_ISSET(cpu, &possible_mask));
81+
82+
return cpu;
83+
}
84+
6085
static void *migration_worker(void *ign)
6186
{
6287
cpu_set_t allowed_mask;
63-
int r, i, nr_cpus, cpu;
88+
int r, i, cpu;
6489

6590
CPU_ZERO(&allowed_mask);
6691

67-
nr_cpus = CPU_COUNT(&possible_mask);
68-
69-
for (i = 0; i < NR_TASK_MIGRATIONS; i++) {
70-
cpu = i % nr_cpus;
71-
if (!CPU_ISSET(cpu, &possible_mask))
72-
continue;
73-
92+
for (i = 0, cpu = min_cpu; i < NR_TASK_MIGRATIONS; i++, cpu = next_cpu(cpu)) {
7493
CPU_SET(cpu, &allowed_mask);
7594

7695
/*
@@ -154,6 +173,36 @@ static void *migration_worker(void *ign)
154173
return NULL;
155174
}
156175

176+
static int calc_min_max_cpu(void)
177+
{
178+
int i, cnt, nproc;
179+
180+
if (CPU_COUNT(&possible_mask) < 2)
181+
return -EINVAL;
182+
183+
/*
184+
* CPU_SET doesn't provide a FOR_EACH helper, get the min/max CPU that
185+
* this task is affined to in order to reduce the time spent querying
186+
* unusable CPUs, e.g. if this task is pinned to a small percentage of
187+
* total CPUs.
188+
*/
189+
nproc = get_nprocs_conf();
190+
min_cpu = -1;
191+
max_cpu = -1;
192+
cnt = 0;
193+
194+
for (i = 0; i < nproc; i++) {
195+
if (!CPU_ISSET(i, &possible_mask))
196+
continue;
197+
if (min_cpu == -1)
198+
min_cpu = i;
199+
max_cpu = i;
200+
cnt++;
201+
}
202+
203+
return (cnt < 2) ? -EINVAL : 0;
204+
}
205+
157206
int main(int argc, char *argv[])
158207
{
159208
int r, i, snapshot;
@@ -167,8 +216,8 @@ int main(int argc, char *argv[])
167216
TEST_ASSERT(!r, "sched_getaffinity failed, errno = %d (%s)", errno,
168217
strerror(errno));
169218

170-
if (CPU_COUNT(&possible_mask) < 2) {
171-
print_skip("Only one CPU, task migration not possible\n");
219+
if (calc_min_max_cpu()) {
220+
print_skip("Only one usable CPU, task migration not possible");
172221
exit(KSFT_SKIP);
173222
}
174223

0 commit comments

Comments
 (0)